@telus-uds/components-web 2.12.0 → 2.14.0

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.
Files changed (65) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/component-docs.json +105 -43
  3. package/lib/Autocomplete/Loading.js +5 -10
  4. package/lib/Autocomplete/dictionary.js +2 -2
  5. package/lib/Badge/Badge.js +10 -1
  6. package/lib/DatePicker/DatePicker.js +13 -0
  7. package/lib/NavigationBar/NavigationSubMenu.js +4 -8
  8. package/lib/QuantitySelector/QuantitySelector.js +67 -66
  9. package/lib/QuantitySelector/SideButton.js +93 -0
  10. package/lib/QuantitySelector/styles.js +4 -20
  11. package/lib/Spinner/Spinner.js +10 -1
  12. package/lib/Spinner/SpinnerContent.js +8 -0
  13. package/lib/Table/Cell.js +62 -91
  14. package/lib/Table/Header.js +4 -1
  15. package/lib/Table/SubHeading.js +4 -1
  16. package/lib/Table/Table.js +2 -1
  17. package/lib/TermsAndConditions/ExpandCollapse.js +31 -13
  18. package/lib/TermsAndConditions/TermsAndConditions.js +42 -10
  19. package/lib/Testimonial/Testimonial.js +48 -12
  20. package/lib/VideoPicker/VideoPickerPlayer.js +4 -2
  21. package/lib/VideoPicker/VideoPickerThumbnail.js +103 -60
  22. package/lib/VideoPicker/VideoSlider.js +2 -2
  23. package/lib-module/Autocomplete/Loading.js +6 -12
  24. package/lib-module/Autocomplete/dictionary.js +2 -2
  25. package/lib-module/Badge/Badge.js +10 -1
  26. package/lib-module/DatePicker/DatePicker.js +13 -1
  27. package/lib-module/NavigationBar/NavigationSubMenu.js +5 -9
  28. package/lib-module/QuantitySelector/QuantitySelector.js +68 -67
  29. package/lib-module/QuantitySelector/SideButton.js +80 -0
  30. package/lib-module/QuantitySelector/styles.js +3 -15
  31. package/lib-module/Spinner/Spinner.js +10 -1
  32. package/lib-module/Spinner/SpinnerContent.js +8 -0
  33. package/lib-module/Table/Cell.js +65 -90
  34. package/lib-module/Table/Header.js +4 -1
  35. package/lib-module/Table/SubHeading.js +4 -1
  36. package/lib-module/Table/Table.js +2 -1
  37. package/lib-module/TermsAndConditions/ExpandCollapse.js +33 -15
  38. package/lib-module/TermsAndConditions/TermsAndConditions.js +42 -11
  39. package/lib-module/Testimonial/Testimonial.js +49 -13
  40. package/lib-module/VideoPicker/VideoPickerPlayer.js +4 -2
  41. package/lib-module/VideoPicker/VideoPickerThumbnail.js +103 -61
  42. package/lib-module/VideoPicker/VideoSlider.js +2 -2
  43. package/package.json +4 -4
  44. package/src/Autocomplete/Loading.jsx +2 -5
  45. package/src/Autocomplete/dictionary.js +2 -2
  46. package/src/Badge/Badge.jsx +14 -2
  47. package/src/DatePicker/DatePicker.jsx +14 -1
  48. package/src/NavigationBar/NavigationSubMenu.jsx +3 -4
  49. package/src/QuantitySelector/QuantitySelector.jsx +60 -76
  50. package/src/QuantitySelector/SideButton.jsx +74 -0
  51. package/src/QuantitySelector/styles.js +4 -70
  52. package/src/Spinner/Spinner.jsx +9 -1
  53. package/src/Spinner/SpinnerContent.jsx +13 -1
  54. package/src/Table/Cell.jsx +58 -78
  55. package/src/Table/Header.jsx +6 -1
  56. package/src/Table/SubHeading.jsx +4 -1
  57. package/src/Table/Table.jsx +10 -2
  58. package/src/TermsAndConditions/ExpandCollapse.jsx +36 -14
  59. package/src/TermsAndConditions/TermsAndConditions.jsx +48 -10
  60. package/src/Testimonial/Testimonial.jsx +73 -11
  61. package/src/VideoPicker/VideoPickerPlayer.jsx +2 -2
  62. package/src/VideoPicker/VideoPickerThumbnail.jsx +51 -30
  63. package/src/VideoPicker/VideoSlider.jsx +2 -2
  64. package/types/BaseProvider.d.ts +25 -0
  65. package/types/index.d.ts +1 -1
@@ -7,12 +7,13 @@ import {
7
7
  selectSystemProps,
8
8
  Typography,
9
9
  useCopy,
10
- useThemeTokens
10
+ useThemeTokens,
11
+ useViewport
11
12
  } from '@telus-uds/components-base'
12
13
  import ExpandCollapse from './ExpandCollapse'
13
14
  import OrderedListBase from '../OrderedList/OrderedListBase'
14
15
  import { htmlAttrs, media, renderStructuredContent } from '../utils'
15
- import dictionary from './dictionary'
16
+ import defaultDictionary from './dictionary'
16
17
 
17
18
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
18
19
 
@@ -33,7 +34,8 @@ const Ordered = styled(OrderedListBase)(({ tokens }) => ({
33
34
  const Unordered = styled.ul(({ tokens }) => ({
34
35
  listStyleType: 'none',
35
36
  margin: 0,
36
- padding: tokens.unorderedPadding
37
+ padding: 0,
38
+ paddingTop: tokens.unorderedPadding
37
39
  }))
38
40
 
39
41
  const ListItem = styled(OrderedListBase.Item)(({ tokens }) => ({
@@ -70,16 +72,28 @@ const NonIndexedContentTitle = styled.div(({ tokens }) => ({
70
72
  * - Use `copy` to set language, ‘en’ for English or ‘fr’ for French
71
73
  */
72
74
  const TermsAndConditions = forwardRef(
73
- ({ copy = 'en', indexedContent, nonIndexedContent, tokens, variant = {}, ...rest }, ref) => {
75
+ (
76
+ {
77
+ copy = 'en',
78
+ indexedContent,
79
+ nonIndexedContent,
80
+ tokens,
81
+ variant = {},
82
+ dictionary = defaultDictionary,
83
+ ...rest
84
+ },
85
+ ref
86
+ ) => {
74
87
  const getCopy = useCopy({ dictionary, copy })
75
88
  const hasIndexedContent = indexedContent.length > 0
76
89
  const hasNonIndexedContent = nonIndexedContent.length > 0
77
90
 
78
- const themeTokens = useThemeTokens('TermsAndConditions', tokens, variant)
91
+ const viewport = useViewport()
92
+ const themeTokens = useThemeTokens('TermsAndConditions', tokens, variant, { viewport })
79
93
 
80
94
  return (
81
95
  <div {...selectProps(rest)}>
82
- <Divider />
96
+ <Divider tokens={{ color: themeTokens.dividerColor }} />
83
97
  <ExpandCollapse
84
98
  collapseTitle={getCopy('headingView')}
85
99
  expandTitle={getCopy('headingHide')}
@@ -99,7 +113,16 @@ const TermsAndConditions = forwardRef(
99
113
  {hasNonIndexedContent && (
100
114
  <Box between={3}>
101
115
  <NonIndexedContentTitle tokens={themeTokens}>
102
- <Typography block heading variant={{ size: 'h4' }}>
116
+ <Typography
117
+ block
118
+ heading
119
+ tokens={{
120
+ fontName: themeTokens.expandTitleFontName,
121
+ fontWeight: themeTokens.expandTitleFontWeight,
122
+ fontSize: themeTokens.titleFontSize,
123
+ lineHeight: themeTokens.titleLineHeight
124
+ }}
125
+ >
103
126
  {getCopy('nonIndexedTitle')}
104
127
  </Typography>
105
128
  </NonIndexedContentTitle>
@@ -115,7 +138,7 @@ const TermsAndConditions = forwardRef(
115
138
  )}
116
139
  </ContentContainer>
117
140
  </ExpandCollapse>
118
- <Divider />
141
+ <Divider tokens={{ color: themeTokens.dividerColor }} />
119
142
  </div>
120
143
  )
121
144
  }
@@ -123,6 +146,13 @@ const TermsAndConditions = forwardRef(
123
146
 
124
147
  TermsAndConditions.displayName = 'TermsAndConditions'
125
148
 
149
+ // If a language dictionary entry is provided, it must contain every key
150
+ const dictionaryContentShape = PropTypes.shape({
151
+ headingHide: PropTypes.string.isRequired,
152
+ headingView: PropTypes.string.isRequired,
153
+ nonIndexedTitle: PropTypes.string.isRequired
154
+ })
155
+
126
156
  TermsAndConditions.propTypes = {
127
157
  ...selectedSystemPropTypes,
128
158
  /**
@@ -149,13 +179,21 @@ TermsAndConditions.propTypes = {
149
179
  *
150
180
  * nonIndexedContent do not have a corresponding superscript and instead apply to the page as a whole.
151
181
  */
152
- nonIndexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string]))
182
+ nonIndexedContent: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.node, PropTypes.string])),
183
+ /**
184
+ * Custom dictionary containing the labels to use for `TermsAndConditions`
185
+ */
186
+ dictionary: PropTypes.shape({
187
+ en: dictionaryContentShape,
188
+ fr: dictionaryContentShape
189
+ })
153
190
  }
154
191
 
155
192
  TermsAndConditions.defaultProps = {
156
193
  copy: 'en',
157
194
  indexedContent: [],
158
- nonIndexedContent: []
195
+ nonIndexedContent: [],
196
+ dictionary: defaultDictionary
159
197
  }
160
198
 
161
199
  export default TermsAndConditions
@@ -1,7 +1,13 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import styled from 'styled-components'
4
- import { Icon, selectSystemProps, Typography, useThemeTokens } from '@telus-uds/components-base'
4
+ import {
5
+ Icon,
6
+ selectSystemProps,
7
+ Typography,
8
+ useThemeTokens,
9
+ useViewport
10
+ } from '@telus-uds/components-base'
5
11
  import Image from '../Image'
6
12
  import { htmlAttrs } from '../utils'
7
13
 
@@ -48,11 +54,13 @@ const Testimonial = ({
48
54
  imageSrc,
49
55
  image = imageSrc,
50
56
  additionalInfo,
51
- testimonialStyle = 'heading',
57
+ testimonialStyle = 'large',
52
58
  tokens,
59
+ copy = 'en',
53
60
  variant = {},
54
61
  ...rest
55
62
  }) => {
63
+ const viewport = useViewport()
56
64
  const {
57
65
  testimonialContainerGap,
58
66
  quoteContainerGap,
@@ -61,13 +69,43 @@ const Testimonial = ({
61
69
  figcaptionGap,
62
70
  textColor,
63
71
  icon,
72
+ iconFr,
64
73
  iconColor,
65
- imageSize
66
- } = useThemeTokens('Testimonial', tokens, variant)
74
+ imageSize,
75
+ testimonialFontSizeLarge,
76
+ testimonialLineHeightLarge,
77
+ testimonialFontWeightLarge,
78
+ testimonialFontSizeHeading,
79
+ testimonialLineHeightHeading,
80
+ testimonialFontNameHeading,
81
+ testimonialFontWeightHeading,
82
+ authorFontSize,
83
+ authorLineHeight,
84
+ authorFontName,
85
+ authorFontWeight,
86
+ additionalFontSize,
87
+ additionalLineHeight,
88
+ additionalFontName,
89
+ additionalFontWeight
90
+ } = useThemeTokens('Testimonial', tokens, variant, { viewport })
91
+
92
+ const getQuoteTestimonial = (open) => {
93
+ let quote = ''
94
+
95
+ if (copy === 'en') quote = open ? '\u201C' : '\u201D'
96
+ else quote = open ? '\u00AB ' : ' \u00BB'
97
+
98
+ return quote
99
+ }
100
+
67
101
  return (
68
102
  <TestimonialContainer testimonialContainerGap={testimonialContainerGap} {...selectProps(rest)}>
69
103
  <QuoteContainer quoteContainerGap={quoteContainerGap}>
70
- <Icon tokens={{ color: iconColor }} variant={{ size: 'micro' }} icon={icon} />
104
+ <Icon
105
+ tokens={{ color: iconColor }}
106
+ variant={{ size: 'micro' }}
107
+ icon={copy === 'en' ? icon : iconFr}
108
+ />
71
109
  {showDivider && (
72
110
  <Divider
73
111
  dividerBackgroundColor={dividerBackgroundColor}
@@ -81,10 +119,22 @@ const Testimonial = ({
81
119
  variant={{ size: testimonialStyle === 'large' ? 'large' : 'h3' }}
82
120
  tokens={{
83
121
  color: textColor,
84
- fontWeight: '400'
122
+ fontSize:
123
+ testimonialStyle === 'heading'
124
+ ? testimonialFontSizeHeading
125
+ : testimonialFontSizeLarge,
126
+ lineHeight:
127
+ testimonialStyle === 'heading'
128
+ ? testimonialLineHeightHeading
129
+ : testimonialLineHeightLarge,
130
+ fontName: testimonialFontNameHeading,
131
+ fontWeight:
132
+ testimonialStyle === 'heading'
133
+ ? testimonialFontWeightHeading
134
+ : testimonialFontWeightLarge
85
135
  }}
86
136
  >
87
- {`\u201C${testimonial}\u201D`}
137
+ {`${getQuoteTestimonial(true)}${testimonial}${getQuoteTestimonial()}`}
88
138
  </Typography>
89
139
  </BlockQuote>
90
140
  {(image || title || additionalInfo) && (
@@ -105,16 +155,28 @@ const Testimonial = ({
105
155
  <AuthorInfoContainer>
106
156
  {title && (
107
157
  <Typography
108
- variant={{ size: 'small', colour: 'secondary' }}
109
- tokens={{ fontWeight: '500' }}
158
+ variant={{ size: 'small' }}
159
+ tokens={{
160
+ color: textColor,
161
+ fontSize: authorFontSize,
162
+ lineHeight: authorLineHeight,
163
+ fontName: authorFontName,
164
+ fontWeight: authorFontWeight
165
+ }}
110
166
  >
111
167
  {title}
112
168
  </Typography>
113
169
  )}
114
170
  {additionalInfo && (
115
171
  <Typography
116
- variant={{ size: 'micro', colour: 'secondary' }}
117
- tokens={{ fontWeight: '400' }}
172
+ variant={{ size: 'small' }}
173
+ tokens={{
174
+ color: textColor,
175
+ fontSize: additionalFontSize,
176
+ lineHeight: additionalLineHeight,
177
+ fontName: additionalFontName,
178
+ fontWeight: additionalFontWeight
179
+ }}
118
180
  >
119
181
  {additionalInfo}
120
182
  </Typography>
@@ -10,8 +10,8 @@ const VideoPickerPlayer = ({ video = {}, videoPlayerRef, onStartVideo = () => {}
10
10
  {video.videoId && <WebVideo {...video} onStart={() => onStartVideo(video)} />}
11
11
  </div>
12
12
  <StackView space={2}>
13
- <Typography variant={{ size: 'h2', colour: 'secondary' }}>{video.title}</Typography>
14
- <Typography>{video.description}</Typography>
13
+ <Typography variant={{ size: 'h3' }}>{video.title}</Typography>
14
+ <Typography variant={{ colour: 'default' }}>{video.description}</Typography>
15
15
  </StackView>
16
16
  </StackView>
17
17
  )
@@ -8,7 +8,7 @@ import {
8
8
  Typography,
9
9
  useViewport,
10
10
  horizontalScrollUtils,
11
- useThemeTokens
11
+ useThemeTokensCallback
12
12
  } from '@telus-uds/components-base'
13
13
  import { getTimestamp } from '../shared/VideoSplash/helpers'
14
14
  import { VideoPropType, RefPropType } from './videoPropType'
@@ -42,6 +42,12 @@ const createReactNativeStyles = ({
42
42
  }
43
43
  })
44
44
 
45
+ const ImageContainer = styled.div`
46
+ padding: ${(props) => `${props.outerBorderGap}px`};
47
+ border: ${(props) => `${props.outerBorderWidth}px solid ${props.outerBorderColor}`};
48
+ border-radius: ${({ outerBorderRadius }) => outerBorderRadius}px;
49
+ `
50
+
45
51
  const VideoThumbnail = styled.div`
46
52
  position: relative;
47
53
  width: ${(props) => (props.layout === 'vertical' ? '100%' : '144px')};
@@ -61,7 +67,7 @@ const VideoThumbnail = styled.div`
61
67
  &::after {
62
68
  content: '';
63
69
  border: ${({ borderWidth }) => borderWidth}px solid;
64
- border-color: ${({ isPlaying, borderColor }) => (isPlaying ? borderColor : 'transparent')};
70
+ border-color: ${({ borderColor }) => borderColor};
65
71
  border-radius: ${({ borderRadius }) => borderRadius}px;
66
72
  position: absolute;
67
73
  top: 0;
@@ -94,27 +100,30 @@ const VideoPickerThumbnail = ({
94
100
  width = '100%'
95
101
  }) => {
96
102
  const viewport = useViewport()
97
- const { titleColor, subTitleColor, ...themeTokens } = useThemeTokens('VideoPickerThumbnail')
103
+ const getTokens = useThemeTokensCallback('VideoPickerThumbnail', {}, {})
98
104
 
99
- const rnStyles = createReactNativeStyles(themeTokens)
100
105
  const { timestamp } = getTimestamp(video.videoLength, video.copy)
101
106
  const isPlaying = selectedVideoId === video.videoId
102
107
 
103
- const renderThumbnailImage = () => (
104
- <VideoThumbnail {...themeTokens} isPlaying={isPlaying} layout={layout}>
105
- <VideoSplash
106
- simpleMode
107
- poster={video.posterSrc || `https://img.youtube.com/vi/${video.videoId}/maxresdefault.jpg`}
108
- videoLength={video.videoLength}
109
- copy={video.copy}
110
- />
111
- </VideoThumbnail>
112
- )
108
+ const renderThumbnailImage = (themeTokens) => {
109
+ return (
110
+ <VideoThumbnail {...themeTokens} isPlaying={isPlaying} layout={layout}>
111
+ <VideoSplash
112
+ simpleMode
113
+ poster={
114
+ video.posterSrc || `https://img.youtube.com/vi/${video.videoId}/maxresdefault.jpg`
115
+ }
116
+ videoLength={video.videoLength}
117
+ copy={video.copy}
118
+ />
119
+ </VideoThumbnail>
120
+ )
121
+ }
113
122
 
114
- const renderThumbnailInfo = () => (
123
+ const renderThumbnailInfo = ({ titleColor, subTitleColor }) => (
115
124
  <StackView space={2} tokens={{ flexShrink: 1 }}>
116
125
  <ThumbnailTitleContainer viewport={viewport}>
117
- <Typography variant={{ bold: true }} tokens={{ color: isPlaying ? titleColor : 'none' }}>
126
+ <Typography variant={{ bold: true }} tokens={{ color: titleColor }}>
118
127
  {video.title}
119
128
  </Typography>
120
129
  </ThumbnailTitleContainer>
@@ -148,21 +157,33 @@ const VideoPickerThumbnail = ({
148
157
  onKeyPress={onKeyPress}
149
158
  accessibilityRole="radio"
150
159
  accessibilityState={{ checked: isPlaying }}
151
- style={[
152
- rnStyles.container,
153
- layout === 'horizontal' && rnStyles.horizontal,
154
- isFramed && rnStyles.framed,
155
- isFramed && index > 0 && rnStyles.framedLine,
156
- { width }
157
- ]}
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
+ }}
158
173
  >
159
- <StackView
160
- space={layout === 'vertical' ? 2 : 3}
161
- direction={layout === 'vertical' ? 'column' : 'row'}
162
- >
163
- {renderThumbnailImage()}
164
- {renderThumbnailInfo()}
165
- </StackView>
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
+ }}
166
187
  </Pressable>
167
188
  )
168
189
  }
@@ -26,7 +26,7 @@ const VideoSlider = ({ children }) => {
26
26
  setContainerWidth(width)
27
27
  }
28
28
 
29
- const itemsGap = 24 // '4' on spacing scale
29
+ const itemsGap = 32 // '5' on spacing scale
30
30
  const itemsCount = viewport === 'lg' || viewport === 'xl' ? 4 : 3
31
31
  const itemGapPortioned = ((itemsCount - 1) * itemsGap) / itemsCount
32
32
  const itemWidth =
@@ -40,7 +40,7 @@ const VideoSlider = ({ children }) => {
40
40
  )
41
41
 
42
42
  const content = (
43
- <StackView space={4} direction="row" accessibilityRole="radiogroup" tokens={{ flexGrow: 1 }}>
43
+ <StackView space={5} direction="row" accessibilityRole="radiogroup" tokens={{ flexGrow: 1 }}>
44
44
  {React.Children.map(children, (child, index) =>
45
45
  cloneElement(child, {
46
46
  index,
@@ -0,0 +1,25 @@
1
+ import type { ComponentType } from 'react'
2
+
3
+ declare interface ThemeMetadata {
4
+ themeTokensVersion: string
5
+ name: string
6
+ }
7
+
8
+ declare interface DefaultTheme {
9
+ metadata: ThemeMetadata
10
+ }
11
+
12
+ declare interface ThemeOptions {
13
+ forceAbsoluteFontSizing?: boolean
14
+ forceZIndex?: boolean
15
+ }
16
+
17
+ export interface BaseProviderProps {
18
+ themeOptions?: ThemeOptions
19
+ defaultTheme: DefaultTheme
20
+ children: React.ReactNode
21
+ }
22
+
23
+ declare const BaseProvider: ComponentType<BaseProviderProps>
24
+
25
+ export default BaseProvider
package/types/index.d.ts CHANGED
@@ -112,7 +112,7 @@ declare const Pagination: ComponentType<Props> & {
112
112
 
113
113
  export { default as QuantitySelector } from './QuantitySelector'
114
114
  export type { QuantitySelectorProps } from './QuantitySelector'
115
-
115
+ export { default as BaseProvider } from './BaseProvider'
116
116
  declare const QuickLinks: ComponentType<Props> & {
117
117
  Item: ComponentType<Props>
118
118
  }