Skip to content

Commit 8ba8ca0

Browse files
authored
feat: add video microdata (#1070)
1 parent 1acaf8a commit 8ba8ca0

File tree

10 files changed

+113
-31
lines changed

10 files changed

+113
-31
lines changed

src/blocks/Media/Media.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import {useTheme} from '../../context/theme';
66
import {MediaBlockProps} from '../../models';
77
import {block, getThemedValue} from '../../utils';
88
import {getMediaBorder} from '../../utils/borderSelector';
9+
import {mergeVideoMicrodata} from '../../utils/microdata';
910

1011
import './Media.scss';
1112

1213
const b = block('media-block');
1314

1415
export const MediaBlock = (props: MediaBlockProps) => {
15-
const {media, border, disableShadow} = props;
16+
const {media, border, disableShadow, title, description} = props;
1617
const borderSelected = getMediaBorder({
1718
border,
1819
disableShadow,
@@ -21,13 +22,17 @@ export const MediaBlock = (props: MediaBlockProps) => {
2122
const [play, setPlay] = useState<boolean>(false);
2223
const theme = useTheme();
2324
const mediaThemed = getThemedValue(media, theme);
25+
const mediaWithMicrodata = mergeVideoMicrodata(mediaThemed, {
26+
name: title,
27+
description,
28+
});
2429

2530
return (
2631
<MediaBase {...props} onScroll={() => setPlay(true)}>
2732
<MediaBase.Card>
2833
<Media
2934
imageClassName={b('image')}
30-
{...mediaThemed}
35+
{...mediaWithMicrodata}
3136
playVideo={play}
3237
className={b({border: borderSelected})}
3338
/>

src/blocks/PromoFeaturesBlock/PromoFeaturesBlock.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {BREAKPOINTS} from '../../constants';
1010
import {useTheme} from '../../context/theme';
1111
import {PromoFeaturesProps} from '../../models';
1212
import {block, getThemedValue} from '../../utils';
13+
import {mergeVideoMicrodata} from '../../utils/microdata';
1314

1415
import './PromoFeaturesBlock.scss';
1516

@@ -39,6 +40,10 @@ const PromoFeaturesBlock = (props: PromoFeaturesProps) => {
3940
const blockModifier = backgroundTheme === 'default' ? 'default' : 'light';
4041
const themeMod = cardTheme || blockModifier || '';
4142
const themedMedia = getThemedValue(media, globalTheme);
43+
const allProps = mergeVideoMicrodata(themedMedia, {
44+
name: cardTitle,
45+
description: text,
46+
});
4247

4348
return (
4449
<div
@@ -54,7 +59,7 @@ const PromoFeaturesBlock = (props: PromoFeaturesProps) => {
5459
<YFMWrapper content={text} modifiers={{constructor: true}} />
5560
</div>
5661
</div>
57-
{media && <Media className={b('card-media')} {...themedMedia} />}
62+
{media && <Media className={b('card-media')} {...allProps} />}
5863
</div>
5964
);
6065
})}

src/blocks/Tabs/Tabs.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {useTheme} from '../../context/theme';
1414
import {Col, GridColumnOrderClasses, Row} from '../../grid';
1515
import {TabsBlockProps} from '../../models';
1616
import {block, getThemedValue} from '../../utils';
17+
import {mergeVideoMicrodata} from '../../utils/microdata';
1718

1819
import TabsTextContent from './TabsTextContent/TabsTextContent';
1920

@@ -85,7 +86,7 @@ export const TabsBlock = ({
8586
const showText = Boolean(activeTabData?.text);
8687
const border = activeTabData?.border || 'shadow';
8788

88-
const textContent = activeTabData && showText && (
89+
const textContent = showText && (
8990
<TabsTextContent
9091
showMedia={showMedia}
9192
data={activeTabData}
@@ -109,7 +110,12 @@ export const TabsBlock = ({
109110
<div style={{minHeight: mediaVideoHeight || minImageHeight}}>
110111
<div ref={ref}>
111112
<Media
112-
{...getThemedValue(activeTabData.media, theme)}
113+
{...mergeVideoMicrodata(getThemedValue(activeTabData.media, theme), {
114+
name: activeTabData.tabName,
115+
description: activeTabData.caption
116+
? activeTabData.caption
117+
: undefined,
118+
})}
113119
key={activeTab}
114120
className={b('media', {border})}
115121
playVideo={play}

src/blocks/Tabs/TabsTextContent/TabsTextContent.tsx

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const b = block('tabs-block-text-content');
1212
interface TextContentProps extends Pick<TabsBlockProps, 'centered' | 'contentSize'> {
1313
showMedia: boolean;
1414
isReverse: boolean;
15-
data: TabsBlockItem;
15+
data?: TabsBlockItem;
1616
centered?: boolean;
1717
imageProps?: ImageObjectProps | ImageDeviceProps;
1818
}
@@ -21,29 +21,37 @@ export const TabsTextContent = ({
2121
centered,
2222
contentSize = 's',
2323
showMedia,
24-
data: {media, title, text, additionalInfo, link, links, buttons, list},
24+
data,
2525
imageProps,
2626
isReverse,
27-
}: TextContentProps) => (
28-
<Col sizes={{all: 12, md: showMedia ? 4 : 8}} className={b({centered: centered})}>
29-
<div
30-
className={b('wrapper', {
31-
reverse: isReverse,
32-
'no-image': !(media || imageProps),
33-
})}
34-
>
35-
<Content
36-
title={title}
37-
text={text}
38-
additionalInfo={additionalInfo}
39-
size={contentSize}
40-
list={list}
41-
links={[...(link ? [link] : []), ...(links || [])]}
42-
buttons={buttons}
43-
colSizes={{all: 12}}
44-
/>
45-
</div>
46-
</Col>
47-
);
27+
}: TextContentProps) => {
28+
if (!data) {
29+
return null;
30+
}
31+
32+
const {media, title, text, additionalInfo, link, links, buttons, list} = data;
33+
34+
return (
35+
<Col sizes={{all: 12, md: showMedia ? 4 : 8}} className={b({centered: centered})}>
36+
<div
37+
className={b('wrapper', {
38+
reverse: isReverse,
39+
'no-image': !(media || imageProps),
40+
})}
41+
>
42+
<Content
43+
title={title}
44+
text={text}
45+
additionalInfo={additionalInfo}
46+
size={contentSize}
47+
list={list}
48+
links={[...(link ? [link] : []), ...(links || [])]}
49+
buttons={buttons}
50+
colSizes={{all: 12}}
51+
/>
52+
</div>
53+
</Col>
54+
);
55+
};
4856

4957
export default TabsTextContent;

src/components/Media/Media.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import React, {ReactElement, useMemo, useState} from 'react';
1+
import React, {ReactElement, useContext, useMemo, useState} from 'react';
22

3+
import {InnerContext} from '../../context/innerContext';
34
import {MediaProps, QAProps} from '../../models';
45
import {block, getQaAttrubutes} from '../../utils';
6+
import {sanitizeMicrodata} from '../../utils/microdata';
57
import IframeVideoBlock from '../VideoBlock/VideoBlock';
68

79
import DataLens from './DataLens/DataLens';
@@ -49,9 +51,11 @@ export const Media = (props: MediaAllProps) => {
4951
onImageLoad,
5052
iframe,
5153
margins,
54+
videoMicrodata,
5255
} = props;
5356

5457
const [hasVideoFallback, setHasVideoFallback] = useState(false);
58+
const {microdata} = useContext(InnerContext);
5559

5660
const qaAttributes = getQaAttrubutes(qa, 'video');
5761

@@ -155,8 +159,27 @@ export const Media = (props: MediaAllProps) => {
155159
margins,
156160
]);
157161

162+
const videoMicrodataScript = useMemo(() => {
163+
const {name, description} = videoMicrodata || {};
164+
const json = JSON.stringify({
165+
'@context': 'http://schema.org/',
166+
'@type': 'VideoObject',
167+
uploadDate: microdata?.contentUpdatedDate,
168+
contentUrl: video?.src?.[0] || videoIframe || youtube,
169+
thumbnailUrl: previewImg,
170+
...(videoMicrodata || {}),
171+
name: name ? sanitizeMicrodata(name) : name,
172+
description: description ? sanitizeMicrodata(description) : description,
173+
});
174+
175+
return video || youtube || videoIframe ? (
176+
<script type="application/ld+json">{json}</script>
177+
) : null;
178+
}, [microdata?.contentUpdatedDate, previewImg, video, videoIframe, videoMicrodata, youtube]);
179+
158180
return (
159181
<div className={b(null, className)} style={{backgroundColor: color}} data-qa={qa}>
182+
{videoMicrodataScript}
160183
{content}
161184
</div>
162185
);

src/containers/PageConstructor/PageConstructor.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ export interface PageConstructorProps {
5050
custom?: CustomConfig;
5151
renderMenu?: () => React.ReactNode;
5252
navigation?: NavigationData;
53+
microdata?: {
54+
contentUpdatedDate?: string;
55+
};
5356
}
5457

5558
export const Constructor = (props: PageConstructorProps) => {
@@ -59,6 +62,7 @@ export const Constructor = (props: PageConstructorProps) => {
5962
shouldRenderBlock,
6063
navigation,
6164
custom,
65+
microdata,
6266
} = props;
6367

6468
const {context} = useMemo(
@@ -85,9 +89,10 @@ export const Constructor = (props: PageConstructorProps) => {
8589
customization: {
8690
decorators: custom?.decorators,
8791
},
92+
microdata,
8893
},
8994
}),
90-
[custom, shouldRenderBlock],
95+
[custom, shouldRenderBlock, microdata],
9196
);
9297

9398
const theme = useTheme();

src/context/innerContext/InnerContext.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export interface InnerContextType {
1313
loadables?: LoadableConfig;
1414
shouldRenderBlock?: ShouldRenderBlock;
1515
customization?: Pick<CustomConfig, 'decorators'>;
16+
microdata?: {
17+
contentUpdatedDate?: string;
18+
};
1619
}
1720

1821
export const InnerContext = React.createContext<InnerContextType>({
@@ -22,4 +25,5 @@ export const InnerContext = React.createContext<InnerContextType>({
2225
navigationBlockTypes: [],
2326
itemMap: {} as ItemMap,
2427
navItemMap: {} as NavItemMap,
28+
microdata: {},
2529
});

src/models/constructor-items/common.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,14 @@ export interface MediaProps
289289
Partial<MediaComponentIframeProps>,
290290
Partial<MediaComponentVideoProps> {
291291
color?: string;
292+
videoMicrodata?: {
293+
name?: string;
294+
description?: string;
295+
duration?: string;
296+
uploadDate?: string;
297+
contentUrl?: string;
298+
thumbnailUrl?: string;
299+
};
292300
}
293301

294302
export interface BackgroundMediaProps extends MediaProps, Animatable, QAProps {

src/sub-blocks/LayoutItem/LayoutItem.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {FullscreenMedia, IconWrapper, Media, MetaInfo} from '../../components';
66
import {useTheme} from '../../context/theme';
77
import {ContentBlockProps, LayoutItemProps} from '../../models';
88
import {block, getThemedValue} from '../../utils';
9+
import {mergeVideoMicrodata} from '../../utils/microdata';
910
import Content from '../Content/Content';
1011

1112
import {getLayoutItemLinks, hasFullscreen, showFullscreenIcon} from './utils';
@@ -43,6 +44,11 @@ const LayoutItem = ({
4344
return null;
4445
}
4546
const themedMedia = getThemedValue(media, theme);
47+
const {title} = content;
48+
const mediaWithMicrodata = mergeVideoMicrodata(themedMedia, {
49+
name: typeof title === 'string' ? title : title?.text,
50+
description: content.text,
51+
});
4652

4753
return fullscreen && hasFullscreen(themedMedia) ? (
4854
<FullscreenMedia showFullscreenIcon={showFullscreenIcon(themedMedia)}>
@@ -52,7 +58,7 @@ const LayoutItem = ({
5258
...fullscreenMediaProps
5359
} = {}) => (
5460
<Media
55-
{...themedMedia}
61+
{...mediaWithMicrodata}
5662
{...fullscreenMediaProps}
5763
className={b('media', {border}, mediaClassName)}
5864
analyticsEvents={analyticsEvents}

src/utils/microdata.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import sanitize from 'sanitize-html';
2+
3+
import {MediaProps} from '../models';
4+
5+
export const mergeVideoMicrodata = (
6+
values: MediaProps = {},
7+
newValues: MediaProps['videoMicrodata'] = {},
8+
): MediaProps => ({...values, videoMicrodata: {...newValues, ...(values.videoMicrodata || {})}});
9+
10+
export function sanitizeMicrodata(html: string) {
11+
return html && sanitize(html, {allowedTags: [], allowedAttributes: {}});
12+
}

0 commit comments

Comments
 (0)