Skip to content

Commit 677e9e6

Browse files
🎨 Add embedded videos feature #3863 (#3875)
* 🎨 embedded video list component * 🎨 update video list component * 🎨 columns * 🎨 update to embrace vimeo too * 🎨 hide tabs video panel for now, not done
1 parent cd8eda0 commit 677e9e6

20 files changed

Lines changed: 538 additions & 7 deletions

File tree

‎packages/energyvision/src/brand/tailwind/theme.css‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@
100100

101101
--scale-99: 0.99;
102102

103+
--radius-base: 10px;
103104
--radius-card: 13px;
104105

105106
--shadow-card: rgba(0, 0, 0, 0.08) 0px 1px 3px, 1px -1px 2px 0px rgba(0, 0, 0, 0.07), rgba(0, 0, 0, 0.2) 0px 1px 2px;

‎studio/schemas/components/RolesBasedArrayInput.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type Role, useCurrentUser } from 'sanity'
33
const restrictedComponents = [
44
'videoPlayer',
55
'videoPlayerCarousel',
6+
'embeddedVideoList',
67
'fullWidthVideo',
78
'promoteExternalLinkV2',
89
'grid',

‎studio/schemas/documents/homePage.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export default {
6565
{ type: 'iframeCarousel' },
6666
{ type: 'videoPlayer' },
6767
{ type: 'videoPlayerCarousel' },
68+
{ type: 'embeddedVideoList' },
6869
{ type: 'tableV2' },
6970
{ type: 'imageForText' },
7071
Flags.HAS_CAMPAIGN_BLOCKS && { type: 'grid' },

‎studio/schemas/documents/magazine.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ export default {
176176
{ type: 'imageCarousel' },
177177
{ type: 'iframeCarousel' },
178178
{ type: 'videoPlayer' },
179+
{ type: 'embeddedVideoList' },
179180
{ type: 'anchorLink' },
180181
],
181182
},

‎studio/schemas/documents/topic/sharedTopicPageFields.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export const content = {
4848
{ type: 'iframeCarousel' },
4949
{ type: 'videoPlayer' },
5050
{ type: 'videoPlayerCarousel' },
51+
{ type: 'embeddedVideoList' },
5152
{ type: 'tableV2' },
5253
{ type: 'imageForText' },
5354
Flags.HAS_CAMPAIGN_BLOCKS && { type: 'grid' },

‎studio/schemas/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import colorList from './objects/colorList'
2626
import contactList from './objects/contactList'
2727
import cookieDeclaration from './objects/cookieDeclaration'
2828
import downloadableImage from './objects/downloadableImage'
29+
import embeddedVideoList from './objects/embeddedVideoList'
2930
import eventDate from './objects/eventDate'
3031
import excludeFromSearch from './objects/excludeFromSearch'
3132
import factbox from './objects/factbox'
@@ -85,6 +86,7 @@ import stockValuesApi from './objects/stockValuesApi'
8586
import tableV2 from './objects/tableV2'
8687
import tabs from './objects/tabs/tabs'
8788
import tabsBackground from './objects/tabs/tabsBackground'
89+
import tabsEmbeddedVideosPanel from './objects/tabs/tabsEmbeddedVideosPanel'
8890
import tabsInfoPanel from './objects/tabs/tabsInfoPanel'
8991
import tabsItem from './objects/tabs/tabsItem'
9092
import tabsKeyNumberItem from './objects/tabs/tabsKeyNumberItem'
@@ -165,6 +167,7 @@ const RemainingSchemas = [
165167
titleAndMeta,
166168
downloadableFile,
167169
downloadableImage,
170+
embeddedVideoList,
168171
teaser,
169172
textTeaser,
170173
textBlock,
@@ -229,6 +232,7 @@ const RemainingSchemas = [
229232
tabs,
230233
tabsItem,
231234
tabsBackground,
235+
tabsEmbeddedVideosPanel,
232236
tabsInfoPanel,
233237
tabsKeyNumbers,
234238
tabsKeyNumberItem,

‎studio/schemas/objects/commonFields/commonFields.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export const gridColumns = ({
140140
initialValue?: string | undefined
141141
description?: string | undefined
142142
validation?: any
143-
}) => {
143+
} = {}) => {
144144
return {
145145
title: 'Number of grid columns',
146146
name: 'gridColumns',
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import { play_circle } from '@equinor/eds-icons'
2+
import type { PortableTextBlock, Rule } from 'sanity'
3+
import blocksToText from '../../helpers/blocksToText'
4+
import { EdsIcon } from '../../icons'
5+
import { CompactBlockEditor } from '../components/CompactBlockEditor'
6+
import { configureBlockContent } from '../editors'
7+
import { validateCharCounterEditor } from '../validations/validateCharCounterEditor'
8+
import { gridColumns } from './commonFields/commonFields'
9+
import { cookiePolicy } from './iframe/sharedIframeFields'
10+
11+
export default {
12+
name: 'embeddedVideoList',
13+
title: 'Embedded video list',
14+
type: 'object',
15+
fields: [
16+
{
17+
name: 'title',
18+
type: 'array',
19+
components: {
20+
input: CompactBlockEditor,
21+
},
22+
of: [configureBlockContent({ variant: 'title' })],
23+
title: 'Title',
24+
validation: (Rule: Rule) => Rule.required(),
25+
},
26+
{
27+
type: 'boolean',
28+
name: 'hideTitle',
29+
title: 'Hide title',
30+
description:
31+
'Hides the title, but screen readers will read title of carousel',
32+
},
33+
{
34+
name: 'ingress',
35+
title: 'Ingress',
36+
description: 'Optional short description. Max 400 characters',
37+
type: 'array',
38+
of: [configureBlockContent({ variant: 'ingress' })],
39+
validation: (Rule: Rule) =>
40+
Rule.custom((value: any) =>
41+
validateCharCounterEditor(value, 400, true),
42+
),
43+
},
44+
cookiePolicy(),
45+
gridColumns(),
46+
{
47+
type: 'array',
48+
name: 'items',
49+
title: 'Video items',
50+
description: 'Add one or more embedded videos.',
51+
of: [
52+
{
53+
title: 'Video item',
54+
type: 'object',
55+
fields: [
56+
{
57+
name: 'videoId',
58+
type: 'string',
59+
title: 'Embed URL',
60+
description:
61+
'Paste the full embed URL (YouTube or Vimeo iframe src).',
62+
},
63+
{
64+
name: 'highlighted',
65+
type: 'boolean',
66+
title: 'Highlight video',
67+
description:
68+
'Places this video first and larger at the top of the list on web.',
69+
initialValue: false,
70+
},
71+
{
72+
name: 'title',
73+
type: 'array',
74+
title: 'Title',
75+
description: 'Optional title shown beneath the video.',
76+
components: { input: CompactBlockEditor },
77+
of: [configureBlockContent({ variant: 'title' })],
78+
},
79+
],
80+
preview: {
81+
select: {
82+
title: 'title',
83+
subtitle: 'videoId',
84+
highlighted: 'highlighted',
85+
},
86+
prepare({
87+
title = [],
88+
subtitle,
89+
highlighted,
90+
}: {
91+
title: PortableTextBlock[]
92+
subtitle: string
93+
highlighted?: boolean
94+
}) {
95+
return {
96+
title: blocksToText(title) || 'Embedded video',
97+
subtitle: highlighted ? `Highlighted | ${subtitle}` : subtitle,
98+
}
99+
},
100+
},
101+
},
102+
],
103+
validation: (Rule: Rule) =>
104+
Rule.required()
105+
.min(1)
106+
.custom((items?: Array<{ highlighted?: boolean }>) => {
107+
const highlightedCount =
108+
items?.filter(item => item?.highlighted).length ?? 0
109+
110+
return highlightedCount > 1
111+
? 'Only one video can be highlighted.'
112+
: true
113+
}),
114+
},
115+
],
116+
preview: {
117+
select: {
118+
title: 'title',
119+
items: 'items',
120+
},
121+
prepare(selection: any) {
122+
const { title, items } = selection
123+
const length = items ? items.length : 0
124+
125+
return {
126+
title: title ? blocksToText(title) : 'Untitled embedded video list',
127+
subtitle: `Embedded video list with ${length} items`,
128+
media: EdsIcon(play_circle),
129+
}
130+
},
131+
},
132+
}

‎studio/schemas/objects/iframe/sharedIframeFields.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const title = {
88
name: 'title',
99
type: 'array',
1010
title: 'Title',
11-
description: 'The (optional) title/heading',
11+
description: 'Optional heading',
1212
components: { input: CompactBlockEditor },
1313
of: [configureBlockContent({ variant: 'title' })],
1414
}
@@ -49,7 +49,6 @@ export const cookiePolicy = (fieldset?: string) => {
4949
name: 'cookiePolicy',
5050
type: 'array',
5151
title: 'Cookie policy',
52-
description: 'Select which cookie policy applies to this iframe.',
5352
...(fieldset && { fieldset }),
5453
of: [{ type: 'string' }],
5554
options: {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { play_circle } from '@equinor/eds-icons'
2+
import type { PortableTextBlock, Rule } from 'sanity'
3+
import blocksToText from '../../../helpers/blocksToText'
4+
import { EdsIcon } from '../../../icons'
5+
import { CompactBlockEditor } from '../../components/CompactBlockEditor'
6+
import { configureBlockContent } from '../../editors'
7+
import { cookiePolicy } from '../iframe/sharedIframeFields'
8+
9+
export default {
10+
name: 'tabsEmbeddedVideosPanel',
11+
title: 'Embedded videos panel',
12+
type: 'object',
13+
fields: [
14+
cookiePolicy(),
15+
{
16+
type: 'array',
17+
name: 'items',
18+
title: 'Video items',
19+
description: 'Add one or more videos for this tab panel.',
20+
of: [
21+
{
22+
title: 'Video item',
23+
type: 'object',
24+
fields: [
25+
{
26+
name: 'videoId',
27+
type: 'string',
28+
title: 'YouTube video ID',
29+
description: 'Paste only the video ID, not the full URL.',
30+
validation: (Rule: Rule) =>
31+
Rule.required().custom((value?: string) => {
32+
if (!value) return true
33+
34+
const isValidYoutubeId = /^[A-Za-z0-9_-]{11}$/.test(value)
35+
36+
if (!isValidYoutubeId) {
37+
return 'Enter a valid YouTube video ID (11 characters).'
38+
}
39+
40+
return true
41+
}),
42+
},
43+
{
44+
name: 'title',
45+
type: 'array',
46+
title: 'Title',
47+
description: 'Optional title shown beneath the video.',
48+
components: { input: CompactBlockEditor },
49+
of: [configureBlockContent({ variant: 'title' })],
50+
},
51+
],
52+
preview: {
53+
select: {
54+
title: 'title',
55+
subtitle: 'videoId',
56+
},
57+
prepare({
58+
title = [],
59+
subtitle,
60+
}: {
61+
title: PortableTextBlock[]
62+
subtitle: string
63+
}) {
64+
return {
65+
title: blocksToText(title),
66+
subtitle,
67+
}
68+
},
69+
},
70+
},
71+
],
72+
validation: (Rule: Rule) => Rule.required().min(1),
73+
},
74+
],
75+
preview: {
76+
select: {
77+
items: 'items',
78+
},
79+
prepare({ items = [] }: { items?: unknown[] }) {
80+
return {
81+
title: 'Embedded videos',
82+
subtitle: `${items.length} video${items.length === 1 ? '' : 's'}`,
83+
media: EdsIcon(play_circle),
84+
}
85+
},
86+
},
87+
}

0 commit comments

Comments
 (0)