@telus-uds/components-web 1.11.0 → 2.0.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 (50) hide show
  1. package/CHANGELOG.md +40 -2
  2. package/lib/Breadcrumbs/Breadcrumbs.js +7 -10
  3. package/lib/Breadcrumbs/Item/Item.js +10 -37
  4. package/lib/DatePicker/CalendarContainer.js +60 -71
  5. package/lib/DatePicker/DatePicker.js +66 -22
  6. package/lib/Footnote/Footnote.js +26 -9
  7. package/lib/List/List.js +11 -0
  8. package/lib/List/ListItem.js +48 -0
  9. package/lib/List/index.js +16 -0
  10. package/lib/PriceLockup/PriceLockup.js +27 -13
  11. package/lib/SkeletonProvider/SkeletonImage.js +55 -0
  12. package/lib/SkeletonProvider/SkeletonProvider.js +84 -0
  13. package/lib/SkeletonProvider/SkeletonTypography.js +54 -0
  14. package/lib/SkeletonProvider/index.js +13 -0
  15. package/lib/Table/Table.js +14 -5
  16. package/lib/Toast/Toast.js +1 -1
  17. package/lib/index.js +19 -1
  18. package/lib-module/Breadcrumbs/Breadcrumbs.js +7 -10
  19. package/lib-module/Breadcrumbs/Item/Item.js +11 -35
  20. package/lib-module/DatePicker/CalendarContainer.js +61 -72
  21. package/lib-module/DatePicker/DatePicker.js +67 -23
  22. package/lib-module/Footnote/Footnote.js +26 -9
  23. package/lib-module/List/List.js +2 -0
  24. package/lib-module/List/ListItem.js +31 -0
  25. package/lib-module/List/index.js +4 -0
  26. package/lib-module/PriceLockup/PriceLockup.js +27 -13
  27. package/lib-module/SkeletonProvider/SkeletonImage.js +42 -0
  28. package/lib-module/SkeletonProvider/SkeletonProvider.js +65 -0
  29. package/lib-module/SkeletonProvider/SkeletonTypography.js +41 -0
  30. package/lib-module/SkeletonProvider/index.js +2 -0
  31. package/lib-module/Table/Table.js +14 -5
  32. package/lib-module/Toast/Toast.js +1 -1
  33. package/lib-module/index.js +2 -0
  34. package/package.json +3 -3
  35. package/src/Breadcrumbs/Breadcrumbs.jsx +19 -22
  36. package/src/Breadcrumbs/Item/Item.jsx +16 -42
  37. package/src/DatePicker/CalendarContainer.jsx +61 -71
  38. package/src/DatePicker/DatePicker.jsx +47 -19
  39. package/src/Footnote/Footnote.jsx +42 -11
  40. package/src/List/List.jsx +3 -0
  41. package/src/List/ListItem.jsx +21 -0
  42. package/src/List/index.js +6 -0
  43. package/src/PriceLockup/PriceLockup.jsx +13 -7
  44. package/src/SkeletonProvider/SkeletonImage.jsx +33 -0
  45. package/src/SkeletonProvider/SkeletonProvider.jsx +62 -0
  46. package/src/SkeletonProvider/SkeletonTypography.jsx +31 -0
  47. package/src/SkeletonProvider/index.js +3 -0
  48. package/src/Table/Table.jsx +7 -4
  49. package/src/Toast/Toast.jsx +1 -0
  50. package/src/index.js +2 -0
@@ -6,7 +6,7 @@ import 'react-dates/initialize'
6
6
  import SingleDatePicker from 'react-dates/lib/components/SingleDatePicker'
7
7
  import DayPickerSingleDateController from 'react-dates/lib/components/DayPickerSingleDateController'
8
8
  import {
9
- Icon,
9
+ IconButton,
10
10
  InputSupports,
11
11
  selectSystemProps,
12
12
  useCopy,
@@ -31,8 +31,16 @@ const getResponsiveDaySize = (inline = false, viewport = 'md') => {
31
31
  return responsiveDaySize
32
32
  }
33
33
 
34
- const getIcon = (icon) => <Icon icon={icon} variant={{ size: 'small' }} />
34
+ const getResponsiveCircleSize = (inline = false, viewport = 'md') => {
35
+ let responsiveCircleSize
36
+ if (viewport === 'xs') {
37
+ responsiveCircleSize = 26
38
+ } else {
39
+ responsiveCircleSize = inline ? 44 : 26
40
+ }
35
41
 
42
+ return responsiveCircleSize
43
+ }
36
44
  const MonthCenterContainer = styled.div({
37
45
  display: 'flex',
38
46
  justifyContent: 'center'
@@ -82,6 +90,7 @@ const DatePicker = forwardRef(
82
90
  ) => {
83
91
  const [inputDate, setInputDate] = useState(date)
84
92
  const [isFocused, setIsFocused] = useState(false)
93
+
85
94
  const getCopy = useCopy({ dictionary, copy })
86
95
  const onFocusChange = ({ focused }) => {
87
96
  setIsFocused(focused)
@@ -92,9 +101,9 @@ const DatePicker = forwardRef(
92
101
  }
93
102
  const viewport = useViewport()
94
103
  const daySize = getResponsiveDaySize(inline, viewport)
104
+ const circleSize = getResponsiveCircleSize(inline, viewport)
95
105
 
96
106
  const value = date ?? inputDate
97
-
98
107
  const HiddenInputFieldContainer = styled.div`
99
108
  height: ${(props) => props.height};
100
109
  width: ${(props) => props.width};
@@ -107,9 +116,7 @@ const DatePicker = forwardRef(
107
116
  previousIcon,
108
117
  nextIcon,
109
118
  ...remainingTokens
110
- } = useThemeTokens('DatePicker', tokens, variant, {
111
- viewport
112
- })
119
+ } = useThemeTokens('DatePicker', tokens, { ...variant, inline }, { viewport })
113
120
 
114
121
  const defaultFontTokens = applyTextStyles({
115
122
  fontName: remainingTokens.calendarDayDefaultFontName,
@@ -121,19 +128,40 @@ const DatePicker = forwardRef(
121
128
  fontWeight: remainingTokens.calendarMonthCaptionFontWeight
122
129
  })
123
130
 
124
- const dayPickerNavigationButtonTokens = applyTextStyles({
131
+ const calendarWeekFontTokens = applyTextStyles({
125
132
  fontName: remainingTokens.dayPickerWeekHeaderFontName,
126
133
  fontWeight: remainingTokens.dayPickerWeekHeaderFontWeight
127
134
  })
128
-
135
+ const renderPrevButton = ({ onClick }) => (
136
+ <IconButton
137
+ onPress={() => {
138
+ onClick()
139
+ }}
140
+ icon={previousIcon}
141
+ variant={{ size: 'small' }}
142
+ />
143
+ )
144
+ const renderNextButton = ({ onClick }) => (
145
+ <IconButton
146
+ onPress={() => {
147
+ onClick()
148
+ }}
149
+ icon={nextIcon}
150
+ variant={{ size: 'small' }}
151
+ />
152
+ )
129
153
  return (
130
154
  <CalendarContainer
131
155
  {...selectProps(rest)}
132
156
  daySize={daySize}
133
157
  validation={validation}
134
- remainingTokens={remainingTokens}
158
+ remainingTokens={{
159
+ ...remainingTokens
160
+ }}
161
+ calendarDayDefaultHeight={circleSize}
162
+ calendarDayDefaultWidth={circleSize}
135
163
  calendarMonthFontTokens={calendarMonthFontTokens}
136
- dayPickerNavigationButtonTokens={dayPickerNavigationButtonTokens}
164
+ calendarWeekFontTokens={calendarWeekFontTokens}
137
165
  defaultFontTokens={defaultFontTokens}
138
166
  >
139
167
  <InputSupports
@@ -169,8 +197,8 @@ const DatePicker = forwardRef(
169
197
  hideKeyboardShortcutsPanel={true}
170
198
  keepOpenOnDateSelect={false}
171
199
  daySize={daySize}
172
- navPrev={getIcon(previousIcon)}
173
- navNext={getIcon(nextIcon)}
200
+ renderNavPrevButton={renderPrevButton}
201
+ renderNavNextButton={renderNextButton}
174
202
  isOutsideRange={isDayDisabled}
175
203
  phrases={getCopy()}
176
204
  renderMonthElement={({ month }) => (
@@ -190,22 +218,22 @@ const DatePicker = forwardRef(
190
218
  </>
191
219
  ) : (
192
220
  <SingleDatePicker
193
- ref={ref}
194
- id={id}
195
221
  date={value}
196
222
  onDateChange={onChange}
197
223
  focused={isFocused}
198
224
  onFocusChange={onFocusChange}
199
225
  numberOfMonths={1}
200
226
  hideKeyboardShortcutsPanel={true}
201
- displayFormat="DD / MM / YYYY"
202
- placeholder="DD / MM / YYYY"
203
- keepOpenOnDateSelect={false}
227
+ keepOpenOnDateSelect={true}
204
228
  daySize={daySize}
205
- navPrev={getIcon(previousIcon)}
206
- navNext={getIcon(nextIcon)}
229
+ ref={ref}
230
+ renderNavPrevButton={renderPrevButton}
207
231
  isOutsideRange={isDayDisabled}
208
232
  phrases={getCopy()}
233
+ id={id}
234
+ displayFormat="DD / MM / YYYY"
235
+ placeholder="DD / MM / YYYY"
236
+ renderNavNextButton={renderNextButton}
209
237
  renderMonthElement={({ month }) => (
210
238
  <MonthCenterContainer>
211
239
  <div>
@@ -32,7 +32,6 @@ const StyledFootnote = styled.div(
32
32
  width: '100vw',
33
33
  backgroundColor: footnoteBackground,
34
34
  display: 'block',
35
- boxShadow: '0 0 16px 0 rgba(0, 0, 0, 0.1)',
36
35
  transform: 'translateY(100%)',
37
36
  transition: 'transform 500ms ease-out',
38
37
  '@media() (prefers-reduced-motion: reduce)': {
@@ -63,13 +62,23 @@ const StyledFootnoteHeader = styled.div({
63
62
  width: '100%'
64
63
  })
65
64
 
66
- const StyledHeader = styled.div(({ headerMargin }) => ({
67
- alignItems: 'center',
68
- display: 'flex',
69
- flexDirection: 'row',
70
- justifyContent: 'space-between',
71
- margin: headerMargin
72
- }))
65
+ const StyledHeader = styled.div(
66
+ ({
67
+ footnoteHeaderPaddingLeft,
68
+ footnoteHeaderPaddingRight,
69
+ footnoteHeaderPaddingTop,
70
+ footnoteHeaderPaddingBottom
71
+ }) => ({
72
+ alignItems: 'center',
73
+ display: 'flex',
74
+ flexDirection: 'row',
75
+ justifyContent: 'space-between',
76
+ paddingTop: footnoteHeaderPaddingTop,
77
+ paddingBottom: footnoteHeaderPaddingBottom,
78
+ paddingRight: footnoteHeaderPaddingRight,
79
+ paddingLeft: footnoteHeaderPaddingLeft
80
+ })
81
+ )
73
82
 
74
83
  const StyledFootnoteBody = styled.div(
75
84
  {
@@ -134,6 +143,7 @@ const CloseButton = styled.button(
134
143
  cursor: 'pointer',
135
144
  display: 'flex',
136
145
  justifyContent: 'center',
146
+ backgroundColor: 'white',
137
147
  border: closeButtonBorder,
138
148
  height: closeButtonHeight,
139
149
  margin: closeButtonMargin,
@@ -208,6 +218,14 @@ const Footnote = (props) => {
208
218
  footnoteBodyPaddingRight,
209
219
  footnoteBodyPaddingTop,
210
220
  footnoteBodyPaddingBottom,
221
+ footnoteHeaderPaddingLeft,
222
+ footnoteHeaderPaddingRight,
223
+ footnoteHeaderPaddingTop,
224
+ footnoteHeaderPaddingBottom,
225
+ headerLineHeight,
226
+ headerFontSize,
227
+ headerFontName,
228
+ headerFontWeight,
211
229
  listPaddingLeft,
212
230
  listItemMarkerFontSize,
213
231
  listItemMarkerLineHeight,
@@ -423,10 +441,23 @@ const Footnote = (props) => {
423
441
  >
424
442
  <ContentContainer maxWidth={maxWidth}>
425
443
  <StyledFootnoteHeader ref={headerRef}>
426
- <StyledHeader ref={headingRef} headerMargin={headerMargin}>
427
- <Typography block heading tabIndex={-1} variant={{ size: 'h4' }}>
444
+ <StyledHeader
445
+ ref={headingRef}
446
+ footnoteHeaderPaddingLeft={footnoteHeaderPaddingLeft}
447
+ footnoteHeaderPaddingRight={footnoteHeaderPaddingRight}
448
+ footnoteHeaderPaddingTop={footnoteHeaderPaddingTop}
449
+ footnoteHeaderPaddingBottom={footnoteHeaderPaddingBottom}
450
+ headerMargin={headerMargin}
451
+ >
452
+ <h2
453
+ style={{
454
+ fontSize: `${headerFontSize}px`,
455
+ lineHeight: `${headerLineHeight}px`,
456
+ fontFamily: `${headerFontName}${headerFontWeight}normal`
457
+ }}
458
+ >
428
459
  {getCopy('heading')}
429
- </Typography>
460
+ </h2>
430
461
  <CloseButton
431
462
  closeButtonBorder={`${closeButtonBorderSize}px solid ${closeButtonBorderColor}`}
432
463
  closeButtonWidth={`${closeButtonWidth}px`}
@@ -0,0 +1,3 @@
1
+ import { ListBase as List } from '@telus-uds/components-base'
2
+
3
+ export default List
@@ -0,0 +1,21 @@
1
+ import React, { forwardRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { ListItem as ListItemBase, Typography } from '@telus-uds/components-base'
4
+
5
+ const ListItem = forwardRef(({ children, title, ...rest }, ref) => (
6
+ <ListItemBase ref={ref} {...rest}>
7
+ {Boolean(title) && <Typography variant={{ size: 'h4' }}>{title}</Typography>}
8
+ {children}
9
+ </ListItemBase>
10
+ ))
11
+ ListItem.displayName = 'ListItem'
12
+
13
+ ListItem.propTypes = {
14
+ children: PropTypes.node.isRequired,
15
+ title: PropTypes.string
16
+ }
17
+ ListItem.defaultProps = {
18
+ title: undefined
19
+ }
20
+
21
+ export default ListItem
@@ -0,0 +1,6 @@
1
+ import List from './List'
2
+ import ListItem from './ListItem'
3
+
4
+ List.Item = ListItem
5
+
6
+ export default List
@@ -56,7 +56,8 @@ const StrikeThroughContainer = styled.div`
56
56
  ::before {
57
57
  content: '';
58
58
  width: 100%;
59
- height: ${({ strikeThroughHeight }) => strikeThroughHeight};
59
+ top: ${({ strikeThroughPosition }) => `${strikeThroughPosition}px`};
60
+ height: ${({ strikeThroughHeight }) => `${strikeThroughHeight}px`};
60
61
  background: ${({ strikeThroughColor }) => strikeThroughColor};
61
62
  position: absolute;
62
63
  }
@@ -74,6 +75,16 @@ const selectFootnoteLinkStyles = ({
74
75
  }
75
76
  }
76
77
 
78
+ const selectStrikeThroughTokens = ({
79
+ strikeThroughPosition,
80
+ strikeThroughHeight,
81
+ strikeThroughColor
82
+ }) => ({
83
+ strikeThroughHeight,
84
+ strikeThroughPosition,
85
+ strikeThroughColor
86
+ })
87
+
77
88
  const PriceLockup = ({
78
89
  size = 'medium',
79
90
  signDirection = 'left',
@@ -98,8 +109,6 @@ const PriceLockup = ({
98
109
  priceMarginBottom,
99
110
  bottomLinksMarginLeft,
100
111
  topTextMarginBottom,
101
- strikeThroughHeight,
102
- strikeThroughColor,
103
112
  fontColor,
104
113
  dividerColor,
105
114
  footnoteLinkFontSize,
@@ -146,10 +155,7 @@ const PriceLockup = ({
146
155
  return (
147
156
  <>
148
157
  <A11yText text={a11yText} />
149
- <StrikeThroughContainer
150
- strikeThroughHeight={`${strikeThroughHeight}px`}
151
- strikeThroughColor={strikeThroughColor}
152
- >
158
+ <StrikeThroughContainer {...selectStrikeThroughTokens(themeTokens)}>
153
159
  {amountComponent}
154
160
  </StrikeThroughContainer>
155
161
  </>
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Skeleton } from '@telus-uds/components-base'
4
+
5
+ const SkeletonImage = ({
6
+ rounded,
7
+ imgHeight,
8
+ size,
9
+ sizeIndex,
10
+ // Size automatically from image height, unless any size prop is passed in
11
+ sizePixels = !(size || sizeIndex) ? imgHeight : undefined,
12
+ show,
13
+ children
14
+ }) => {
15
+ if (!show) {
16
+ return children
17
+ }
18
+
19
+ const shape = rounded !== 'circle' ? 'box' : rounded
20
+ return <Skeleton size={size} sizePixels={sizePixels} sizeIndex={sizeIndex} shape={shape} />
21
+ }
22
+
23
+ SkeletonImage.propTypes = {
24
+ imgHeight: PropTypes.number,
25
+ rounded: PropTypes.oneOf(['circle', 'corners']),
26
+ size: Skeleton.propTypes?.size,
27
+ sizeIndex: Skeleton.propTypes?.sizeIndex,
28
+ sizePixels: Skeleton.propTypes?.sizePixels,
29
+ show: PropTypes.bool.isRequired,
30
+ children: PropTypes.node
31
+ }
32
+
33
+ export default SkeletonImage
@@ -0,0 +1,62 @@
1
+ import React, { Children, cloneElement } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Typography } from '@telus-uds/components-base'
4
+ import Image from '../Image'
5
+ import SkeletonTypography from './SkeletonTypography'
6
+ import SkeletonImage from './SkeletonImage'
7
+
8
+ const SkeletonProvider = ({ children, show }) => {
9
+ if (!show) {
10
+ return children
11
+ }
12
+
13
+ const mapChild = (child) => {
14
+ if (!child) {
15
+ return null
16
+ }
17
+
18
+ let childElement = child
19
+
20
+ if (childElement.props && 'children' in childElement.props) {
21
+ const { children: elementChildren } = childElement.props
22
+ const mappedChildren = Array.isArray(elementChildren)
23
+ ? elementChildren.map(mapChild)
24
+ : mapChild(elementChildren)
25
+ childElement = cloneElement(childElement, {
26
+ children: mappedChildren
27
+ })
28
+ }
29
+
30
+ if (childElement.type === Typography) {
31
+ return (
32
+ <SkeletonTypography {...childElement.props.skeleton} show={show}>
33
+ {childElement}
34
+ </SkeletonTypography>
35
+ )
36
+ }
37
+
38
+ if (childElement.type === Image) {
39
+ return (
40
+ <SkeletonImage
41
+ {...childElement.props.skeleton}
42
+ imgHeight={childElement.props.height}
43
+ rounded={childElement.props.rounded}
44
+ show={show}
45
+ >
46
+ {childElement}
47
+ </SkeletonImage>
48
+ )
49
+ }
50
+
51
+ return childElement
52
+ }
53
+
54
+ return <>{Children.map(children, mapChild)}</>
55
+ }
56
+
57
+ SkeletonProvider.propTypes = {
58
+ show: PropTypes.bool.isRequired,
59
+ children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]).isRequired
60
+ }
61
+
62
+ export default SkeletonProvider
@@ -0,0 +1,31 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { Skeleton } from '@telus-uds/components-base'
4
+
5
+ const SkeletonTypography = ({ show, size, sizeIndex, sizePixels, characters, lines, children }) => {
6
+ if (!show) {
7
+ return children
8
+ }
9
+
10
+ return (
11
+ <Skeleton
12
+ size={size}
13
+ sizeIndex={sizeIndex}
14
+ sizePixels={sizePixels}
15
+ characters={characters}
16
+ lines={lines}
17
+ />
18
+ )
19
+ }
20
+
21
+ SkeletonTypography.propTypes = {
22
+ show: PropTypes.bool.isRequired,
23
+ children: PropTypes.node,
24
+ size: Skeleton.propTypes?.size,
25
+ sizeIndex: Skeleton.propTypes?.sizeIndex,
26
+ sizePixels: Skeleton.propTypes?.sizePixels,
27
+ characters: Skeleton.propTypes?.characters,
28
+ lines: Skeleton.propTypes?.lines
29
+ }
30
+
31
+ export default SkeletonTypography
@@ -0,0 +1,3 @@
1
+ import SkeletonProvider from './SkeletonProvider'
2
+
3
+ export default SkeletonProvider
@@ -15,6 +15,7 @@ const StyledContainer = styled.div`
15
15
  const StyledTable = styled.table`
16
16
  margin: 0;
17
17
  padding: 0;
18
+ width: ${({ tableWidth }) => `${tableWidth}px`};
18
19
  `
19
20
 
20
21
  const TableContext = React.createContext({})
@@ -35,7 +36,7 @@ export const useTableContext = () => useContext(TableContext)
35
36
  * - Use `Table.Row` and `Table.Cell` to build up the tabular data
36
37
  * - Use `Cell`'s `type` prop to visually mark it as a row heading (`type="rowHeading"`)
37
38
  */
38
- const Table = ({ children, text = 'medium', tokens, variant, ...rest }) => {
39
+ const Table = ({ children, fullWidth = false, text = 'medium', tokens, variant, ...rest }) => {
39
40
  const { tablePaddingBottom } = useThemeTokens('Table', tokens, variant)
40
41
  const containerRef = useRef()
41
42
  const tableRef = useRef()
@@ -45,8 +46,10 @@ const Table = ({ children, text = 'medium', tokens, variant, ...rest }) => {
45
46
 
46
47
  useSafeLayoutEffect(() => {
47
48
  const updateDimensions = () => {
48
- setContainerWidth(containerRef.current.clientWidth)
49
- setTableWidth(tableRef.current.clientWidth)
49
+ const containerClientWidth = containerRef.current.clientWidth
50
+ const responsiveTableWidth = fullWidth ? containerClientWidth : tableRef.current.clientWidth
51
+ setContainerWidth(containerClientWidth)
52
+ setTableWidth(responsiveTableWidth)
50
53
  }
51
54
 
52
55
  const throttledUpdateDimensions = throttle(updateDimensions, 100, { leading: false })
@@ -73,7 +76,7 @@ const Table = ({ children, text = 'medium', tokens, variant, ...rest }) => {
73
76
  {...selectProps(rest)}
74
77
  >
75
78
  <TableContext.Provider value={{ text, isScrollable, tokens, variant }}>
76
- <StyledTable cellSpacing={0} ref={tableRef}>
79
+ <StyledTable tableWidth={tableWidth} cellSpacing={0} ref={tableRef}>
77
80
  {children}
78
81
  </StyledTable>
79
82
  </TableContext.Provider>
@@ -77,6 +77,7 @@ const ToastContainer = styled.div`
77
77
  display: flex;
78
78
  justify-content: center;
79
79
  align-items: center;
80
+ flex-wrap: wrap;
80
81
  background: ${({ containerBackgroundColor }) => containerBackgroundColor};
81
82
  gap: ${({ containerGap }) => containerGap};
82
83
  height: 0;
package/src/index.js CHANGED
@@ -32,8 +32,10 @@ export { default as Video } from './Video'
32
32
  export { default as StoryCard } from './StoryCard'
33
33
  export { default as Disclaimer } from './Disclaimer'
34
34
  export { default as Card } from './Card'
35
+ export { default as List } from './List'
35
36
  export { default as TermsAndConditions } from './TermsAndConditions'
36
37
  export { default as NavigationBar } from './NavigationBar'
37
38
  export { default as Progress } from './Progress'
39
+ export { default as SkeletonProvider } from './SkeletonProvider'
38
40
 
39
41
  export * from './baseExports'