@telus-uds/components-web 2.33.2 → 2.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -3
- package/lib/Badge/Badge.js +4 -2
- package/lib/BlockQuote/BlockQuote.js +4 -2
- package/lib/Breadcrumbs/Breadcrumbs.js +7 -5
- package/lib/Breadcrumbs/Item/Item.js +2 -13
- package/lib/Callout/Callout.js +4 -2
- package/lib/Card/Card.js +3 -5
- package/lib/Card/CardContent.js +4 -2
- package/lib/Countdown/Countdown.js +2 -6
- package/lib/Countdown/Segment.js +4 -2
- package/lib/DatePicker/CalendarContainer.js +2 -2
- package/lib/DatePicker/DatePicker.js +21 -35
- package/lib/Disclaimer/Disclaimer.js +4 -2
- package/lib/ExpandCollapseMini/ExpandCollapseMini.js +5 -11
- package/lib/ExpandCollapseMini/ExpandCollapseMiniControl.js +4 -2
- package/lib/Footnote/Footnote.js +32 -37
- package/lib/Footnote/FootnoteLink.js +9 -6
- package/lib/IconButton/IconButton.js +4 -11
- package/lib/Image/Image.js +5 -3
- package/lib/List/ListItem.js +2 -7
- package/lib/NavigationBar/NavigationBar.js +8 -18
- package/lib/NavigationBar/NavigationItem.js +4 -9
- package/lib/NavigationBar/NavigationSubMenu.js +8 -7
- package/lib/NavigationBar/index.js +2 -0
- package/lib/OptimizeImage/OptimizeImage.js +8 -8
- package/lib/OrderedList/Item.js +3 -9
- package/lib/OrderedList/OrderedList.js +5 -13
- package/lib/OrderedList/OrderedListBase.js +3 -8
- package/lib/Paragraph/Paragraph.js +5 -3
- package/lib/PreviewCard/PreviewCard.js +3 -5
- package/lib/PriceLockup/PriceLockup.js +4 -2
- package/lib/Progress/ProgressBar.js +4 -2
- package/lib/QuantitySelector/QuantitySelector.js +21 -24
- package/lib/QuantitySelector/SideButton.js +12 -20
- package/lib/ResponsiveImage/ResponsiveImage.js +4 -2
- package/lib/Ribbon/Ribbon.js +4 -2
- package/lib/SkeletonProvider/SkeletonImage.js +5 -3
- package/lib/SkeletonProvider/SkeletonProvider.js +3 -5
- package/lib/SkeletonProvider/SkeletonTypography.js +5 -3
- package/lib/Span/Span.js +5 -3
- package/lib/Spinner/Spinner.js +4 -2
- package/lib/Spinner/SpinnerContent.js +4 -2
- package/lib/StoryCard/StoryCard.js +3 -5
- package/lib/Table/Body.js +4 -2
- package/lib/Table/Cell.js +5 -3
- package/lib/Table/Header.js +6 -6
- package/lib/Table/Row.js +6 -6
- package/lib/Table/SubHeading.js +6 -6
- package/lib/Table/Table.js +6 -8
- package/lib/TermsAndConditions/ExpandCollapse.js +2 -7
- package/lib/TermsAndConditions/TermsAndConditions.js +5 -14
- package/lib/Testimonial/Testimonial.js +4 -2
- package/lib/Toast/Toast.js +4 -2
- package/lib/Video/Video.js +19 -55
- package/lib/VideoPicker/VideoPicker.js +38 -9
- package/lib/VideoPicker/VideoPickerPlayer.js +4 -2
- package/lib/VideoPicker/VideoPickerThumbnail.js +4 -2
- package/lib/VideoPicker/VideoSlider.js +7 -7
- package/lib/WaffleGrid/WaffleGrid.js +4 -2
- package/lib/WebVideo/WebVideo.js +51 -13
- package/lib/WebVideo/utils/index.js +58 -0
- package/lib/baseExports.js +6 -0
- package/lib/utils/theming/with-client-theme.js +1 -1
- package/lib/utils/theming/with-server-theme.js +1 -1
- package/lib-module/Badge/Badge.js +4 -2
- package/lib-module/BlockQuote/BlockQuote.js +4 -2
- package/lib-module/Breadcrumbs/Breadcrumbs.js +7 -5
- package/lib-module/Breadcrumbs/Item/Item.js +2 -11
- package/lib-module/Callout/Callout.js +4 -2
- package/lib-module/Card/Card.js +2 -3
- package/lib-module/Card/CardContent.js +4 -2
- package/lib-module/Countdown/Countdown.js +2 -3
- package/lib-module/Countdown/Segment.js +4 -2
- package/lib-module/DatePicker/CalendarContainer.js +2 -2
- package/lib-module/DatePicker/DatePicker.js +21 -33
- package/lib-module/Disclaimer/Disclaimer.js +4 -2
- package/lib-module/ExpandCollapseMini/ExpandCollapseMini.js +5 -9
- package/lib-module/ExpandCollapseMini/ExpandCollapseMiniControl.js +4 -2
- package/lib-module/Footnote/Footnote.js +31 -36
- package/lib-module/Footnote/FootnoteLink.js +9 -7
- package/lib-module/IconButton/IconButton.js +4 -9
- package/lib-module/Image/Image.js +5 -3
- package/lib-module/List/ListItem.js +2 -5
- package/lib-module/NavigationBar/NavigationBar.js +9 -17
- package/lib-module/NavigationBar/NavigationItem.js +5 -8
- package/lib-module/NavigationBar/NavigationSubMenu.js +9 -6
- package/lib-module/NavigationBar/index.js +2 -0
- package/lib-module/OptimizeImage/OptimizeImage.js +8 -6
- package/lib-module/OrderedList/Item.js +3 -7
- package/lib-module/OrderedList/OrderedList.js +6 -12
- package/lib-module/OrderedList/OrderedListBase.js +3 -6
- package/lib-module/Paragraph/Paragraph.js +6 -4
- package/lib-module/PreviewCard/PreviewCard.js +2 -3
- package/lib-module/PriceLockup/PriceLockup.js +4 -2
- package/lib-module/Progress/ProgressBar.js +4 -2
- package/lib-module/QuantitySelector/QuantitySelector.js +22 -23
- package/lib-module/QuantitySelector/SideButton.js +13 -21
- package/lib-module/ResponsiveImage/ResponsiveImage.js +4 -2
- package/lib-module/Ribbon/Ribbon.js +4 -2
- package/lib-module/SkeletonProvider/SkeletonImage.js +5 -3
- package/lib-module/SkeletonProvider/SkeletonProvider.js +3 -3
- package/lib-module/SkeletonProvider/SkeletonTypography.js +5 -3
- package/lib-module/Span/Span.js +6 -4
- package/lib-module/Spinner/Spinner.js +4 -2
- package/lib-module/Spinner/SpinnerContent.js +4 -2
- package/lib-module/StoryCard/StoryCard.js +2 -3
- package/lib-module/Table/Body.js +4 -2
- package/lib-module/Table/Cell.js +5 -3
- package/lib-module/Table/Header.js +6 -4
- package/lib-module/Table/Row.js +6 -4
- package/lib-module/Table/SubHeading.js +6 -4
- package/lib-module/Table/Table.js +6 -6
- package/lib-module/TermsAndConditions/ExpandCollapse.js +2 -5
- package/lib-module/TermsAndConditions/TermsAndConditions.js +5 -12
- package/lib-module/Testimonial/Testimonial.js +4 -2
- package/lib-module/Toast/Toast.js +4 -2
- package/lib-module/Video/Video.js +19 -53
- package/lib-module/VideoPicker/VideoPicker.js +37 -8
- package/lib-module/VideoPicker/VideoPickerPlayer.js +4 -2
- package/lib-module/VideoPicker/VideoPickerThumbnail.js +4 -2
- package/lib-module/VideoPicker/VideoSlider.js +7 -5
- package/lib-module/WaffleGrid/WaffleGrid.js +4 -2
- package/lib-module/WebVideo/WebVideo.js +51 -11
- package/lib-module/WebVideo/utils/index.js +50 -0
- package/lib-module/baseExports.js +1 -1
- package/lib-module/utils/theming/with-client-theme.js +2 -2
- package/lib-module/utils/theming/with-server-theme.js +2 -2
- package/package.json +3 -3
- package/src/Badge/Badge.jsx +5 -2
- package/src/BlockQuote/BlockQuote.jsx +120 -112
- package/src/Breadcrumbs/Breadcrumbs.jsx +84 -77
- package/src/Breadcrumbs/Item/Item.jsx +2 -9
- package/src/Callout/Callout.jsx +37 -40
- package/src/Card/Card.jsx +2 -3
- package/src/Card/CardContent.jsx +19 -14
- package/src/Countdown/Countdown.jsx +72 -71
- package/src/Countdown/Segment.jsx +40 -28
- package/src/DatePicker/CalendarContainer.jsx +2 -2
- package/src/DatePicker/DatePicker.jsx +21 -34
- package/src/Disclaimer/Disclaimer.jsx +5 -3
- package/src/ExpandCollapseMini/ExpandCollapseMini.jsx +37 -40
- package/src/ExpandCollapseMini/ExpandCollapseMiniControl.jsx +52 -44
- package/src/Footnote/Footnote.jsx +32 -38
- package/src/Footnote/FootnoteLink.jsx +45 -49
- package/src/IconButton/IconButton.jsx +19 -20
- package/src/Image/Image.jsx +40 -43
- package/src/List/ListItem.jsx +3 -5
- package/src/NavigationBar/NavigationBar.jsx +9 -18
- package/src/NavigationBar/NavigationItem.jsx +6 -5
- package/src/NavigationBar/NavigationSubMenu.jsx +104 -88
- package/src/NavigationBar/index.js +3 -0
- package/src/OptimizeImage/OptimizeImage.jsx +48 -41
- package/src/OrderedList/Item.jsx +34 -35
- package/src/OrderedList/OrderedList.jsx +4 -6
- package/src/OrderedList/OrderedListBase.jsx +2 -3
- package/src/Paragraph/Paragraph.jsx +21 -16
- package/src/PreviewCard/PreviewCard.jsx +2 -3
- package/src/PriceLockup/PriceLockup.jsx +143 -136
- package/src/Progress/ProgressBar.jsx +11 -3
- package/src/QuantitySelector/QuantitySelector.jsx +162 -154
- package/src/QuantitySelector/SideButton.jsx +52 -56
- package/src/ResponsiveImage/ResponsiveImage.jsx +16 -22
- package/src/Ribbon/Ribbon.jsx +85 -83
- package/src/SkeletonProvider/SkeletonImage.jsx +24 -15
- package/src/SkeletonProvider/SkeletonProvider.jsx +3 -3
- package/src/SkeletonProvider/SkeletonTypography.jsx +18 -13
- package/src/Span/Span.jsx +7 -5
- package/src/Spinner/Spinner.jsx +86 -79
- package/src/Spinner/SpinnerContent.jsx +31 -33
- package/src/StoryCard/StoryCard.jsx +2 -3
- package/src/Table/Body.jsx +5 -3
- package/src/Table/Cell.jsx +77 -67
- package/src/Table/Header.jsx +7 -5
- package/src/Table/Row.jsx +7 -5
- package/src/Table/SubHeading.jsx +7 -5
- package/src/Table/Table.jsx +6 -6
- package/src/TermsAndConditions/ExpandCollapse.jsx +2 -6
- package/src/TermsAndConditions/TermsAndConditions.jsx +5 -13
- package/src/Testimonial/Testimonial.jsx +148 -137
- package/src/Toast/Toast.jsx +68 -63
- package/src/Video/Video.jsx +25 -45
- package/src/VideoPicker/VideoPicker.jsx +114 -75
- package/src/VideoPicker/VideoPickerPlayer.jsx +13 -9
- package/src/VideoPicker/VideoPickerThumbnail.jsx +102 -94
- package/src/VideoPicker/VideoSlider.jsx +8 -6
- package/src/WaffleGrid/WaffleGrid.jsx +36 -40
- package/src/WebVideo/WebVideo.jsx +114 -60
- package/src/WebVideo/utils/index.js +56 -0
- package/src/baseExports.js +1 -0
- package/src/utils/theming/with-client-theme.jsx +2 -2
- package/src/utils/theming/with-server-theme.jsx +2 -2
- package/types/WebVideo.d.ts +2 -1
|
@@ -88,105 +88,113 @@ const ThumbnailTitleContainer = styled.div`
|
|
|
88
88
|
overflow: hidden;
|
|
89
89
|
`
|
|
90
90
|
|
|
91
|
-
const VideoPickerThumbnail = (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
91
|
+
const VideoPickerThumbnail = React.forwardRef(
|
|
92
|
+
(
|
|
93
|
+
{
|
|
94
|
+
videoPlayerRef,
|
|
95
|
+
selectedVideoId,
|
|
96
|
+
video,
|
|
97
|
+
onSelectVideo,
|
|
98
|
+
layout = 'vertical',
|
|
99
|
+
isFramed,
|
|
100
|
+
itemPositions,
|
|
101
|
+
index,
|
|
102
|
+
width = '100%'
|
|
103
|
+
},
|
|
104
|
+
ref
|
|
105
|
+
) => {
|
|
106
|
+
const viewport = useViewport()
|
|
107
|
+
const getTokens = useThemeTokensCallback('VideoPickerThumbnail', {}, {})
|
|
108
|
+
|
|
109
|
+
const { timestamp } = getTimestamp(video.videoLength, video.copy)
|
|
110
|
+
const isPlaying = selectedVideoId === video.videoId
|
|
111
|
+
|
|
112
|
+
const renderThumbnailImage = (themeTokens) => {
|
|
113
|
+
return (
|
|
114
|
+
<VideoThumbnail {...themeTokens} isPlaying={isPlaying} layout={layout}>
|
|
115
|
+
<VideoSplash
|
|
116
|
+
simpleMode
|
|
117
|
+
poster={
|
|
118
|
+
video.posterSrc || `https://img.youtube.com/vi/${video.videoId}/maxresdefault.jpg`
|
|
119
|
+
}
|
|
120
|
+
videoLength={video.videoLength}
|
|
121
|
+
copy={video.copy}
|
|
122
|
+
/>
|
|
123
|
+
</VideoThumbnail>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const renderThumbnailInfo = ({ titleColor, subTitleColor }) => (
|
|
128
|
+
<StackView space={2} tokens={{ flexShrink: 1 }}>
|
|
129
|
+
<ThumbnailTitleContainer viewport={viewport}>
|
|
130
|
+
<Typography variant={{ bold: true }} tokens={{ color: titleColor }}>
|
|
131
|
+
{video.title}
|
|
132
|
+
</Typography>
|
|
133
|
+
</ThumbnailTitleContainer>
|
|
134
|
+
{viewport !== viewports.xs && (
|
|
135
|
+
<Typography variant={{ size: 'micro' }} tokens={{ color: subTitleColor }}>
|
|
136
|
+
{timestamp}
|
|
137
|
+
</Typography>
|
|
138
|
+
)}
|
|
139
|
+
</StackView>
|
|
120
140
|
)
|
|
121
|
-
}
|
|
122
141
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
)}
|
|
135
|
-
</StackView>
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
const handleLayout =
|
|
139
|
-
itemPositions !== undefined
|
|
140
|
-
? getItemPositionLayoutHandler(itemPositions.positions, index)
|
|
141
|
-
: undefined
|
|
142
|
-
|
|
143
|
-
const onKeyPress = (event) => {
|
|
144
|
-
if (['Space', 'Enter'].includes(event.key)) {
|
|
145
|
-
onSelectVideo(video)
|
|
146
|
-
const splashButton = videoPlayerRef.current?.querySelector('button')
|
|
147
|
-
if (splashButton) splashButton.focus()
|
|
142
|
+
const handleLayout =
|
|
143
|
+
itemPositions !== undefined
|
|
144
|
+
? getItemPositionLayoutHandler(itemPositions.positions, index)
|
|
145
|
+
: undefined
|
|
146
|
+
|
|
147
|
+
const onKeyPress = (event) => {
|
|
148
|
+
if (['Space', 'Enter'].includes(event.key)) {
|
|
149
|
+
onSelectVideo(video)
|
|
150
|
+
const splashButton = videoPlayerRef.current?.querySelector('button')
|
|
151
|
+
if (splashButton) splashButton.focus()
|
|
152
|
+
}
|
|
148
153
|
}
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<Pressable
|
|
157
|
+
key={video.videoId}
|
|
158
|
+
onLayout={handleLayout}
|
|
159
|
+
onPress={() => onSelectVideo(video)}
|
|
160
|
+
testID={`thumbnail-container-${video.videoId}`}
|
|
161
|
+
onKeyPress={onKeyPress}
|
|
162
|
+
accessibilityRole="radio"
|
|
163
|
+
accessibilityState={{ checked: isPlaying }}
|
|
164
|
+
style={({ hovered: hover, focused: focus, pressed }) => {
|
|
165
|
+
const themeTokens = getTokens({ hover, focus, pressed, selected: isPlaying })
|
|
166
|
+
|
|
167
|
+
const rnStyles = createReactNativeStyles(themeTokens)
|
|
168
|
+
return [
|
|
169
|
+
rnStyles.container,
|
|
170
|
+
layout === 'horizontal' && rnStyles.horizontal,
|
|
171
|
+
isFramed && rnStyles.framed,
|
|
172
|
+
isFramed && index > 0 && rnStyles.framedLine,
|
|
173
|
+
{ width },
|
|
174
|
+
{ outline: 'none' }
|
|
175
|
+
]
|
|
176
|
+
}}
|
|
177
|
+
ref={ref}
|
|
178
|
+
>
|
|
179
|
+
{({ hovered: hover, focused: focus, pressed }) => {
|
|
180
|
+
const themeTokens = getTokens({ hover, focus, pressed, selected: isPlaying })
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<StackView
|
|
184
|
+
space={layout === 'vertical' ? 2 : 3}
|
|
185
|
+
direction={layout === 'vertical' ? 'column' : 'row'}
|
|
186
|
+
>
|
|
187
|
+
<ImageContainer {...themeTokens}>{renderThumbnailImage(themeTokens)}</ImageContainer>
|
|
188
|
+
{renderThumbnailInfo(themeTokens)}
|
|
189
|
+
</StackView>
|
|
190
|
+
)
|
|
191
|
+
}}
|
|
192
|
+
</Pressable>
|
|
193
|
+
)
|
|
149
194
|
}
|
|
195
|
+
)
|
|
150
196
|
|
|
151
|
-
|
|
152
|
-
<Pressable
|
|
153
|
-
key={video.videoId}
|
|
154
|
-
onLayout={handleLayout}
|
|
155
|
-
onPress={() => onSelectVideo(video)}
|
|
156
|
-
testID={`thumbnail-container-${video.videoId}`}
|
|
157
|
-
onKeyPress={onKeyPress}
|
|
158
|
-
accessibilityRole="radio"
|
|
159
|
-
accessibilityState={{ checked: isPlaying }}
|
|
160
|
-
style={({ hovered: hover, focused: focus, pressed }) => {
|
|
161
|
-
const themeTokens = getTokens({ hover, focus, pressed, selected: isPlaying })
|
|
162
|
-
|
|
163
|
-
const rnStyles = createReactNativeStyles(themeTokens)
|
|
164
|
-
return [
|
|
165
|
-
rnStyles.container,
|
|
166
|
-
layout === 'horizontal' && rnStyles.horizontal,
|
|
167
|
-
isFramed && rnStyles.framed,
|
|
168
|
-
isFramed && index > 0 && rnStyles.framedLine,
|
|
169
|
-
{ width },
|
|
170
|
-
{ outline: 'none' }
|
|
171
|
-
]
|
|
172
|
-
}}
|
|
173
|
-
>
|
|
174
|
-
{({ hovered: hover, focused: focus, pressed }) => {
|
|
175
|
-
const themeTokens = getTokens({ hover, focus, pressed, selected: isPlaying })
|
|
176
|
-
|
|
177
|
-
return (
|
|
178
|
-
<StackView
|
|
179
|
-
space={layout === 'vertical' ? 2 : 3}
|
|
180
|
-
direction={layout === 'vertical' ? 'column' : 'row'}
|
|
181
|
-
>
|
|
182
|
-
<ImageContainer {...themeTokens}>{renderThumbnailImage(themeTokens)}</ImageContainer>
|
|
183
|
-
{renderThumbnailInfo(themeTokens)}
|
|
184
|
-
</StackView>
|
|
185
|
-
)
|
|
186
|
-
}}
|
|
187
|
-
</Pressable>
|
|
188
|
-
)
|
|
189
|
-
}
|
|
197
|
+
VideoPickerThumbnail.displayName = 'VideoPickerThumbnail'
|
|
190
198
|
|
|
191
199
|
VideoPickerThumbnail.propTypes = {
|
|
192
200
|
selectedVideoId: PropTypes.string,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
HorizontalScroll,
|
|
4
4
|
HorizontalScrollButton,
|
|
@@ -12,10 +12,10 @@ import PropTypes from 'prop-types'
|
|
|
12
12
|
|
|
13
13
|
const { useItemPositions } = horizontalScrollUtils
|
|
14
14
|
|
|
15
|
-
const VideoSlider = ({ children }) => {
|
|
15
|
+
const VideoSlider = React.forwardRef(({ children }, ref) => {
|
|
16
16
|
const viewport = useViewport()
|
|
17
17
|
const [itemPositions] = useItemPositions()
|
|
18
|
-
const [containerWidth, setContainerWidth] = useState(null)
|
|
18
|
+
const [containerWidth, setContainerWidth] = React.useState(null)
|
|
19
19
|
const { previousIcon: PreviousIcon, nextIcon: NextIcon } = useThemeTokens('VideoPickerSlider')
|
|
20
20
|
|
|
21
21
|
const onLayout = ({
|
|
@@ -42,7 +42,7 @@ const VideoSlider = ({ children }) => {
|
|
|
42
42
|
const content = (
|
|
43
43
|
<StackView space={5} direction="row" accessibilityRole="radiogroup" tokens={{ flexGrow: 1 }}>
|
|
44
44
|
{React.Children.map(children, (child, index) =>
|
|
45
|
-
cloneElement(child, {
|
|
45
|
+
React.cloneElement(child, {
|
|
46
46
|
index,
|
|
47
47
|
itemPositions,
|
|
48
48
|
width: itemWidth
|
|
@@ -61,7 +61,7 @@ const VideoSlider = ({ children }) => {
|
|
|
61
61
|
buttonClearance: 0
|
|
62
62
|
}
|
|
63
63
|
return (
|
|
64
|
-
<View onLayout={onLayout} style={overflow}>
|
|
64
|
+
<View onLayout={onLayout} style={overflow} ref={ref}>
|
|
65
65
|
{containerWidth === null ? (
|
|
66
66
|
// Use a 100% width non-scrollable parent until containerWidth is known, to avoid flicker
|
|
67
67
|
content
|
|
@@ -76,7 +76,9 @@ const VideoSlider = ({ children }) => {
|
|
|
76
76
|
)}
|
|
77
77
|
</View>
|
|
78
78
|
)
|
|
79
|
-
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
VideoSlider.displayName = 'VideoSlider'
|
|
80
82
|
|
|
81
83
|
VideoSlider.propTypes = {
|
|
82
84
|
children: PropTypes.node
|
|
@@ -64,48 +64,44 @@ const Center = styled('div')({
|
|
|
64
64
|
/**
|
|
65
65
|
* The WaffleGrid is used to show items in a waffle like manner with borders surrounding the element
|
|
66
66
|
*/
|
|
67
|
-
const WaffleGrid = (
|
|
68
|
-
items,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
variant,
|
|
73
|
-
linkRouterProps,
|
|
74
|
-
...rest
|
|
75
|
-
}) => {
|
|
76
|
-
const viewport = useViewport()
|
|
77
|
-
const themeTokens = useThemeTokens('WaffleGrid', tokens, variant, { viewport })
|
|
78
|
-
const currentRowSize = useResponsiveProp(rowSize)
|
|
67
|
+
const WaffleGrid = React.forwardRef(
|
|
68
|
+
({ items, rowSize = null, LinkRouter, tokens, variant, linkRouterProps, ...rest }, ref) => {
|
|
69
|
+
const viewport = useViewport()
|
|
70
|
+
const themeTokens = useThemeTokens('WaffleGrid', tokens, variant, { viewport })
|
|
71
|
+
const currentRowSize = useResponsiveProp(rowSize)
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
>
|
|
88
|
-
<Link
|
|
89
|
-
href={child.href}
|
|
90
|
-
LinkRouter={child.LinkRouter || LinkRouter}
|
|
91
|
-
linkRouterProps={{ ...linkRouterProps, ...child.linkRouterProps }}
|
|
73
|
+
return (
|
|
74
|
+
<Container ref={ref} {...selectProps(rest)}>
|
|
75
|
+
{items.map((child) => (
|
|
76
|
+
<Item
|
|
77
|
+
{...themeTokens}
|
|
78
|
+
key={child.href}
|
|
79
|
+
rowSize={rowSize ? currentRowSize : themeTokens.rowSize}
|
|
92
80
|
>
|
|
93
|
-
<
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
child.image
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
81
|
+
<Link
|
|
82
|
+
href={child.href}
|
|
83
|
+
LinkRouter={child.LinkRouter || LinkRouter}
|
|
84
|
+
linkRouterProps={{ ...linkRouterProps, ...child.linkRouterProps }}
|
|
85
|
+
>
|
|
86
|
+
<Center>
|
|
87
|
+
{typeof child.image === 'string' ? (
|
|
88
|
+
// Assuming that string passed is the image URL
|
|
89
|
+
<Image src={child.image} alt={child.imageAltText} width={96} />
|
|
90
|
+
) : (
|
|
91
|
+
// Otherwise it must be an arbitrary content, which we just display by itself
|
|
92
|
+
child.image
|
|
93
|
+
)}
|
|
94
|
+
<Typography variant={{ weight: 'semibold' }}>{child.text}</Typography>
|
|
95
|
+
</Center>
|
|
96
|
+
</Link>
|
|
97
|
+
</Item>
|
|
98
|
+
))}
|
|
99
|
+
</Container>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
WaffleGrid.displayName = 'WaffleGrid'
|
|
109
105
|
|
|
110
106
|
WaffleGrid.propTypes = {
|
|
111
107
|
...selectedSystemPropTypes,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { selectSystemProps } from '@telus-uds/components-base'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import React
|
|
3
|
+
import React from 'react'
|
|
4
4
|
import YouTube from 'react-youtube'
|
|
5
5
|
import styled from 'styled-components'
|
|
6
6
|
import VideoSplash from '../shared/VideoSplash/VideoSplash'
|
|
7
7
|
import { htmlAttrs } from '../utils'
|
|
8
|
+
import { triggerInProgressVideoIntervals, YoutubePlayerState } from './utils'
|
|
8
9
|
|
|
9
10
|
const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
|
|
10
11
|
|
|
@@ -33,68 +34,111 @@ const AspectLimiter = styled.div((props) => ({
|
|
|
33
34
|
position: 'relative'
|
|
34
35
|
}))
|
|
35
36
|
|
|
36
|
-
const WebVideo = (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
37
|
+
const WebVideo = React.forwardRef(
|
|
38
|
+
(
|
|
39
|
+
{
|
|
40
|
+
videoId,
|
|
41
|
+
aspectRatio = '16:9',
|
|
42
|
+
posterSrc,
|
|
43
|
+
defaultVolume = 1,
|
|
44
|
+
beginMuted = false,
|
|
45
|
+
videoLength,
|
|
46
|
+
copy,
|
|
47
|
+
onPlay = () => {},
|
|
48
|
+
onPause = () => {},
|
|
49
|
+
onEnd = () => {},
|
|
50
|
+
onProgress = () => {},
|
|
51
|
+
onStart = () => {},
|
|
52
|
+
...rest
|
|
53
|
+
},
|
|
54
|
+
ref
|
|
55
|
+
) => {
|
|
56
|
+
const [started, setStarted] = React.useState(false)
|
|
57
|
+
const videoStateData = React.useRef({
|
|
58
|
+
requestAnimationIds: [],
|
|
59
|
+
state: YoutubePlayerState.UNSTARTED
|
|
60
|
+
})
|
|
61
|
+
const playerRef = React.useRef(null)
|
|
62
|
+
|
|
63
|
+
const onPlayCallback = (event) => {
|
|
64
|
+
onPlay(event, videoStateData.current.state === YoutubePlayerState.PAUSED)
|
|
65
|
+
videoStateData.current.state = YoutubePlayerState.PLAYING
|
|
66
|
+
if (onProgress) {
|
|
67
|
+
videoStateData.current.requestAnimationIds = triggerInProgressVideoIntervals(
|
|
68
|
+
onProgress,
|
|
69
|
+
playerRef,
|
|
70
|
+
event
|
|
71
|
+
)
|
|
72
|
+
}
|
|
57
73
|
}
|
|
58
74
|
|
|
59
|
-
event
|
|
60
|
-
|
|
75
|
+
const initializeYoutubePlayer = (event) => {
|
|
76
|
+
onStart()
|
|
77
|
+
playerRef.current = event.target
|
|
78
|
+
|
|
79
|
+
if (beginMuted) {
|
|
80
|
+
event.target.mute()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
event.target.setVolume(defaultVolume)
|
|
84
|
+
event.target.playVideo() // This plays the video after passing the splash screen on mobile.
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const onEndVideoCallback = (event) => {
|
|
88
|
+
onEnd(event)
|
|
89
|
+
videoStateData.current.requestAnimationIds.forEach((id) => cancelAnimationFrame(id))
|
|
90
|
+
videoStateData.current.requestAnimationIds = []
|
|
91
|
+
videoStateData.current.state = YoutubePlayerState.ENDED
|
|
92
|
+
if (onProgress) {
|
|
93
|
+
onProgress(event, 100)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const onPauseVideoCallback = (event) => {
|
|
98
|
+
videoStateData.current.requestAnimationIds.forEach((id) => cancelAnimationFrame(id))
|
|
99
|
+
onPause(event)
|
|
100
|
+
videoStateData.current.state = YoutubePlayerState.PAUSED
|
|
101
|
+
videoStateData.current.requestAnimationIds = []
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<StyledPlayerContainer ref={ref} data-testid="web-video-container" {...selectProps(rest)}>
|
|
106
|
+
<AspectLimiter aspectRatio={aspectRatio}>
|
|
107
|
+
{started ? (
|
|
108
|
+
<StyledYoutubePlayer
|
|
109
|
+
videoId={videoId}
|
|
110
|
+
opts={{
|
|
111
|
+
width: '100%',
|
|
112
|
+
height: '100%',
|
|
113
|
+
playerVars: {
|
|
114
|
+
autoplay: 1,
|
|
115
|
+
modestbranding: 1,
|
|
116
|
+
playsinline: 1,
|
|
117
|
+
rel: 0
|
|
118
|
+
}
|
|
119
|
+
}}
|
|
120
|
+
onReady={initializeYoutubePlayer}
|
|
121
|
+
onPlay={onPlayCallback}
|
|
122
|
+
onPause={onPauseVideoCallback}
|
|
123
|
+
onEnd={onEndVideoCallback}
|
|
124
|
+
/>
|
|
125
|
+
) : (
|
|
126
|
+
<VideoSplash
|
|
127
|
+
poster={posterSrc || `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`}
|
|
128
|
+
videoLength={videoLength}
|
|
129
|
+
copy={copy}
|
|
130
|
+
onClick={() => {
|
|
131
|
+
setStarted(true)
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
135
|
+
</AspectLimiter>
|
|
136
|
+
</StyledPlayerContainer>
|
|
137
|
+
)
|
|
61
138
|
}
|
|
139
|
+
)
|
|
62
140
|
|
|
63
|
-
|
|
64
|
-
<StyledPlayerContainer data-testid="web-video-container" {...selectProps(rest)}>
|
|
65
|
-
<AspectLimiter aspectRatio={aspectRatio}>
|
|
66
|
-
{started ? (
|
|
67
|
-
<StyledYoutubePlayer
|
|
68
|
-
videoId={videoId}
|
|
69
|
-
opts={{
|
|
70
|
-
width: '100%',
|
|
71
|
-
height: '100%',
|
|
72
|
-
playerVars: {
|
|
73
|
-
autoplay: 1,
|
|
74
|
-
modestbranding: 1,
|
|
75
|
-
playsinline: 1,
|
|
76
|
-
rel: 0
|
|
77
|
-
}
|
|
78
|
-
}}
|
|
79
|
-
onReady={initializeYoutubePlayer}
|
|
80
|
-
onPlay={onPlay}
|
|
81
|
-
onPause={onPause}
|
|
82
|
-
onEnd={onEnd}
|
|
83
|
-
/>
|
|
84
|
-
) : (
|
|
85
|
-
<VideoSplash
|
|
86
|
-
poster={posterSrc || `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`}
|
|
87
|
-
videoLength={videoLength}
|
|
88
|
-
copy={copy}
|
|
89
|
-
onClick={() => {
|
|
90
|
-
setStarted(true)
|
|
91
|
-
}}
|
|
92
|
-
/>
|
|
93
|
-
)}
|
|
94
|
-
</AspectLimiter>
|
|
95
|
-
</StyledPlayerContainer>
|
|
96
|
-
)
|
|
97
|
-
}
|
|
141
|
+
WebVideo.displayName = 'WebVideo'
|
|
98
142
|
|
|
99
143
|
export const VideoProps = {
|
|
100
144
|
...selectedSystemPropTypes,
|
|
@@ -144,7 +188,17 @@ export const VideoProps = {
|
|
|
144
188
|
/**
|
|
145
189
|
* A function to be run when the video ends.
|
|
146
190
|
*/
|
|
147
|
-
onEnd: PropTypes.func
|
|
191
|
+
onEnd: PropTypes.func,
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* A function to be run when the video resumes.
|
|
195
|
+
*/
|
|
196
|
+
onResume: PropTypes.func,
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* A function to be run when the video progresses. This function will be run at 10%, 25%, 50%, 75% and 100%.
|
|
200
|
+
*/
|
|
201
|
+
onProgress: PropTypes.func
|
|
148
202
|
}
|
|
149
203
|
|
|
150
204
|
WebVideo.propTypes = VideoProps
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fires analytics events for inProgress analytics
|
|
3
|
+
* @param {current progress} progress
|
|
4
|
+
* @param {callback to run on defined milestones} onProgress
|
|
5
|
+
* @param {YouTube Video Event} event
|
|
6
|
+
*/
|
|
7
|
+
const fireVideoProgressEvents = (progress, onProgress, event) => {
|
|
8
|
+
const validProgressValues = [10, 25, 50, 75]
|
|
9
|
+
if (validProgressValues.includes(progress)) {
|
|
10
|
+
onProgress(event, progress)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const YoutubePlayerState = {
|
|
15
|
+
UNSTARTED: -1,
|
|
16
|
+
ENDED: 0,
|
|
17
|
+
PLAYING: 1,
|
|
18
|
+
PAUSED: 2,
|
|
19
|
+
BUFFERING: 3,
|
|
20
|
+
CUED: 5
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const PERCENTAGE_MULTIPLIER = 100
|
|
24
|
+
/**
|
|
25
|
+
* Run an interval to check the progress of the video and fire events at 10%, 25%, 50% and 75%
|
|
26
|
+
* @param {callback to run on defined milestones} onProgress
|
|
27
|
+
* @param {player reference} playerRef
|
|
28
|
+
* @param {YouTube video event} event
|
|
29
|
+
*/
|
|
30
|
+
export const triggerInProgressVideoIntervals = (onProgress, playerRef, event) => {
|
|
31
|
+
const duration = playerRef.current.getDuration()
|
|
32
|
+
let lastProgress = null
|
|
33
|
+
const requestAnimationFrameIds = []
|
|
34
|
+
|
|
35
|
+
const frame = () => {
|
|
36
|
+
const currentTime = playerRef.current.getCurrentTime()
|
|
37
|
+
const progress = Math.round((currentTime / duration) * PERCENTAGE_MULTIPLIER)
|
|
38
|
+
|
|
39
|
+
if (progress !== lastProgress) {
|
|
40
|
+
fireVideoProgressEvents(progress, onProgress, event)
|
|
41
|
+
lastProgress = progress
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (currentTime < duration) {
|
|
45
|
+
requestAnimationFrameIds.push(requestAnimationFrame(frame))
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Cancel any previous animation frames
|
|
50
|
+
requestAnimationFrameIds.forEach((id) => cancelAnimationFrame(id))
|
|
51
|
+
|
|
52
|
+
// Start a new animation frame
|
|
53
|
+
requestAnimationFrameIds.push(requestAnimationFrame(frame))
|
|
54
|
+
|
|
55
|
+
return requestAnimationFrameIds
|
|
56
|
+
}
|
package/src/baseExports.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
|
|
4
|
-
import { useThemeTokens } from '@telus-uds/components-base'
|
|
4
|
+
import { getTokensPropType, useThemeTokens } from '@telus-uds/components-base'
|
|
5
5
|
|
|
6
6
|
const withClientTheme = (Component) => {
|
|
7
7
|
const UdsStyledComponent = ({ tokens: tokenOverrides, variant, ...props }) => {
|
|
@@ -10,7 +10,7 @@ const withClientTheme = (Component) => {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
UdsStyledComponent.propTypes = {
|
|
13
|
-
tokens:
|
|
13
|
+
tokens: getTokensPropType(Component.displayName),
|
|
14
14
|
variant: PropTypes.string
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
|
-
import { getThemeTokens } from '@telus-uds/components-base/server'
|
|
3
|
+
import { getThemeTokens, getTokensPropType } from '@telus-uds/components-base/server'
|
|
4
4
|
|
|
5
5
|
import getTheme from './get-theme-from-server'
|
|
6
6
|
|
|
@@ -11,7 +11,7 @@ const withServerTheme = (Component, componentName) => {
|
|
|
11
11
|
return <Component theme={themeTokens} {...props} />
|
|
12
12
|
}
|
|
13
13
|
UdsStyledComponent.propTypes = {
|
|
14
|
-
tokens:
|
|
14
|
+
tokens: getTokensPropType(componentName),
|
|
15
15
|
variant: PropTypes.string
|
|
16
16
|
}
|
|
17
17
|
|
package/types/WebVideo.d.ts
CHANGED
|
@@ -11,9 +11,10 @@ export interface WebVideoProps extends HTMLAttrs {
|
|
|
11
11
|
videoLength: number
|
|
12
12
|
copy?: 'en' | 'fr'
|
|
13
13
|
onStart?: () => void
|
|
14
|
-
onPlay?: (event: YouTubeEvent<number
|
|
14
|
+
onPlay?: (event: YouTubeEvent<number>, videoResumed: boolean) => void
|
|
15
15
|
onEnd?: (event: YouTubeEvent<number>) => void
|
|
16
16
|
onPause?: (event: YouTubeEvent<number>) => void
|
|
17
|
+
onProgress?: (event: YouTubeEvent<number>, milestone: number) => void
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
declare const WebVideo: ComponentType<WebVideoProps>
|