@telus-uds/components-base 1.72.0 → 1.73.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 (53) hide show
  1. package/CHANGELOG.md +20 -2
  2. package/lib/Box/Box.js +17 -6
  3. package/lib/FlexGrid/Col/Col.js +42 -19
  4. package/lib/FlexGrid/FlexGrid.js +40 -17
  5. package/lib/FlexGrid/Row/Row.js +45 -22
  6. package/lib/Listbox/ListboxGroup.js +7 -1
  7. package/lib/MultiSelectFilter/MultiSelectFilter.js +1 -0
  8. package/lib/Notification/Notification.js +13 -5
  9. package/lib/OrderedList/ItemBase.js +7 -1
  10. package/lib/Responsive/Responsive.js +24 -14
  11. package/lib/Responsive/ResponsiveProp.js +46 -0
  12. package/lib/Responsive/ResponsiveWithMediaQueryStyleSheet.js +72 -0
  13. package/lib/ThemeProvider/ThemeProvider.js +5 -2
  14. package/lib/ThemeProvider/index.js +9 -1
  15. package/lib/ThemeProvider/useResponsiveThemeTokens.js +89 -0
  16. package/lib/Typography/Typography.js +48 -22
  17. package/lib/server.js +40 -0
  18. package/lib/utils/ssr-media-query/utils/create-media-query-styles.js +39 -6
  19. package/lib-module/Box/Box.js +17 -6
  20. package/lib-module/FlexGrid/Col/Col.js +42 -19
  21. package/lib-module/FlexGrid/FlexGrid.js +40 -17
  22. package/lib-module/FlexGrid/Row/Row.js +45 -22
  23. package/lib-module/Listbox/ListboxGroup.js +7 -1
  24. package/lib-module/MultiSelectFilter/MultiSelectFilter.js +1 -0
  25. package/lib-module/Notification/Notification.js +13 -5
  26. package/lib-module/OrderedList/ItemBase.js +7 -1
  27. package/lib-module/Responsive/Responsive.js +24 -15
  28. package/lib-module/Responsive/ResponsiveProp.js +39 -0
  29. package/lib-module/Responsive/ResponsiveWithMediaQueryStyleSheet.js +64 -0
  30. package/lib-module/ThemeProvider/ThemeProvider.js +5 -2
  31. package/lib-module/ThemeProvider/index.js +1 -0
  32. package/lib-module/ThemeProvider/useResponsiveThemeTokens.js +81 -0
  33. package/lib-module/Typography/Typography.js +50 -24
  34. package/lib-module/server.js +4 -0
  35. package/lib-module/utils/ssr-media-query/utils/create-media-query-styles.js +36 -6
  36. package/package.json +13 -2
  37. package/src/Box/Box.jsx +35 -17
  38. package/src/FlexGrid/Col/Col.jsx +42 -13
  39. package/src/FlexGrid/FlexGrid.jsx +40 -11
  40. package/src/FlexGrid/Row/Row.jsx +40 -16
  41. package/src/Listbox/ListboxGroup.jsx +9 -2
  42. package/src/MultiSelectFilter/MultiSelectFilter.jsx +2 -0
  43. package/src/Notification/Notification.jsx +15 -3
  44. package/src/OrderedList/ItemBase.jsx +14 -2
  45. package/src/Responsive/Responsive.jsx +24 -11
  46. package/src/Responsive/ResponsiveProp.jsx +33 -0
  47. package/src/Responsive/ResponsiveWithMediaQueryStyleSheet.jsx +58 -0
  48. package/src/ThemeProvider/ThemeProvider.jsx +5 -2
  49. package/src/ThemeProvider/index.js +1 -0
  50. package/src/ThemeProvider/useResponsiveThemeTokens.js +85 -0
  51. package/src/Typography/Typography.jsx +72 -24
  52. package/src/server.js +4 -0
  53. package/src/utils/ssr-media-query/utils/create-media-query-styles.js +21 -6
package/package.json CHANGED
@@ -11,9 +11,10 @@
11
11
  "@floating-ui/react-native": "^0.8.1",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@telus-uds/system-constants": "^1.3.0",
14
- "@telus-uds/system-theme-tokens": "^2.48.0",
14
+ "@telus-uds/system-theme-tokens": "^2.49.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "css-mediaquery": "^0.1.2",
17
+ "expo-linear-gradient": "^12.5.0",
17
18
  "lodash.debounce": "^4.0.8",
18
19
  "lodash.merge": "^4.6.2",
19
20
  "lodash.throttle": "^4.1.1",
@@ -38,6 +39,16 @@
38
39
  "lib": "lib",
39
40
  "test": "__tests__"
40
41
  },
42
+ "exports": {
43
+ ".": {
44
+ "import": "./lib-module/index.js",
45
+ "require": "./lib/index.js"
46
+ },
47
+ "./server": {
48
+ "import": "./lib-module/server.js",
49
+ "require": "./lib/server.js"
50
+ }
51
+ },
41
52
  "homepage": "https://github.com/telus/universal-design-system#readme",
42
53
  "keywords": [
43
54
  "base"
@@ -74,5 +85,5 @@
74
85
  "standard-engine": {
75
86
  "skip": true
76
87
  },
77
- "version": "1.72.0"
88
+ "version": "1.73.0"
78
89
  }
package/src/Box/Box.jsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import React, { forwardRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { View, ScrollView } from 'react-native'
3
+ import { View, ScrollView, Platform } from 'react-native'
4
4
  import { useThemeTokens } from '../ThemeProvider'
5
5
  import {
6
6
  a11yProps,
@@ -23,17 +23,20 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
23
23
  * @typedef {import('../utils/props/spacingProps.js').SpacingOptions} SpacingOptions
24
24
  */
25
25
 
26
- const selectBoxStyles = ({
27
- backgroundColor,
28
- gradient,
29
- borderWidth,
30
- borderColor,
31
- borderTopLeftRadius,
32
- borderTopRightRadius,
33
- borderBottomLeftRadius,
34
- borderBottomRightRadius,
35
- ...rest
36
- }) => {
26
+ const selectBoxStyles = (
27
+ {
28
+ backgroundColor,
29
+ gradient,
30
+ borderWidth,
31
+ borderColor,
32
+ borderTopLeftRadius,
33
+ borderTopRightRadius,
34
+ borderBottomLeftRadius,
35
+ borderBottomRightRadius,
36
+ ...rest
37
+ },
38
+ customGradient
39
+ ) => {
37
40
  const styles = {
38
41
  backgroundColor,
39
42
  borderWidth,
@@ -48,7 +51,12 @@ const selectBoxStyles = ({
48
51
  angle,
49
52
  stops: [stopOne, stopTwo]
50
53
  } = gradient
51
- styles.backgroundImage = `linear-gradient(${angle}deg, ${stopOne.color}, 75% , ${stopTwo.color})`
54
+
55
+ if (Platform.OS === 'web') {
56
+ styles.backgroundImage = `linear-gradient(${angle}deg, ${stopOne.color}, 75%, ${stopTwo.color})`
57
+ } else if (customGradient && Platform.OS !== 'web') {
58
+ styles.colors = [stopOne.color, stopTwo.color]
59
+ }
52
60
  }
53
61
 
54
62
  const paddings = ['paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom']
@@ -145,6 +153,7 @@ const Box = forwardRef(
145
153
  accessibilityRole,
146
154
  testID,
147
155
  dataSet,
156
+ customGradient,
148
157
  ...rest
149
158
  },
150
159
  ref
@@ -162,21 +171,26 @@ const Box = forwardRef(
162
171
  paddingRight: useSpacingScale(right),
163
172
  paddingTop: useSpacingScale(top),
164
173
  paddingBottom: useSpacingScale(bottom),
165
- ...selectBoxStyles(themeTokens)
174
+ ...selectBoxStyles(themeTokens, customGradient)
166
175
  }
167
176
 
177
+ const childrenToRender =
178
+ typeof customGradient === 'function'
179
+ ? customGradient(styles.colors, styles)(children)
180
+ : children
181
+
168
182
  if (scroll) {
169
183
  const scrollProps = typeof scroll === 'object' ? scroll : {}
170
184
  scrollProps.contentContainerStyle = [styles, scrollProps.contentContainerStyle]
171
185
  return (
172
186
  <ScrollView {...scrollProps} {...props} testID={testID} dataSet={dataSet} ref={ref}>
173
- {children}
187
+ {childrenToRender}
174
188
  </ScrollView>
175
189
  )
176
190
  }
177
191
  return (
178
192
  <View {...props} style={styles} testID={testID} dataSet={dataSet} ref={ref}>
179
- {children}
193
+ {childrenToRender}
180
194
  </View>
181
195
  )
182
196
  }
@@ -266,7 +280,11 @@ Box.propTypes = {
266
280
  /**
267
281
  * Box accepts any content as children.
268
282
  */
269
- children: PropTypes.node.isRequired
283
+ children: PropTypes.node.isRequired,
284
+ /**
285
+ Use this prop if need to add a custom gradient for mobile
286
+ */
287
+ customGradient: PropTypes.func
270
288
  }
271
289
 
272
290
  export default Box
@@ -5,6 +5,8 @@ import { Platform } from 'react-native'
5
5
  import GutterContext from '../providers/GutterContext'
6
6
  import applyInheritance from '../helpers'
7
7
  import { responsiveProps, BaseView, StyleSheet, createMediaQueryStyles } from '../../utils'
8
+ import { useViewport } from '../../ViewportProvider'
9
+ import { useTheme } from '../../ThemeProvider'
8
10
 
9
11
  const Col = forwardRef(
10
12
  (
@@ -27,6 +29,10 @@ const Col = forwardRef(
27
29
  ref
28
30
  ) => {
29
31
  const gutter = useContext(GutterContext)
32
+ const viewport = useViewport()
33
+ const {
34
+ themeOptions: { enableMediaQueryStyleSheet }
35
+ } = useTheme()
30
36
  const hiddenLevels = applyInheritance([xs, sm, md, lg, xl])
31
37
 
32
38
  const getHorizontalAlignLevel = () => {
@@ -104,7 +110,16 @@ const Col = forwardRef(
104
110
  xl: offsetsWithIheritance[4]
105
111
  }
106
112
 
107
- const mediaQueryStyles = createMediaQueryStyles({
113
+ const staticStyles = {
114
+ flexGrow: 1,
115
+ flexShrink: 0,
116
+ flexBasis: 'auto',
117
+ maxWidth: '100%',
118
+ paddingLeft: gutter ? 16 : 0,
119
+ paddingRight: gutter ? 16 : 0
120
+ }
121
+
122
+ const stylesByViewport = {
108
123
  xs: {
109
124
  display: hiddenLevels[0] === 0 ? 'none' : shown,
110
125
  textAlign: horizontalAlignLevel[0],
@@ -135,21 +150,35 @@ const Col = forwardRef(
135
150
  ...calculateWidth(sizes.xl),
136
151
  ...calculateOffset(offsets.xl)
137
152
  }
138
- })
153
+ }
139
154
 
140
- const { ids, styles } = StyleSheet.create({
141
- col: {
142
- flexGrow: 1,
143
- flexShrink: 0,
144
- flexBasis: 'auto',
145
- maxWidth: '100%',
146
- paddingLeft: gutter ? 16 : 0,
147
- paddingRight: gutter ? 16 : 0,
148
- ...mediaQueryStyles
155
+ let colStyles
156
+ let mediaIds
157
+
158
+ if (enableMediaQueryStyleSheet) {
159
+ const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
160
+ const { ids, styles } = StyleSheet.create({
161
+ col: {
162
+ ...staticStyles,
163
+ ...mediaQueryStyles
164
+ }
165
+ })
166
+ colStyles = styles.col
167
+ mediaIds = ids.col
168
+ } else {
169
+ colStyles = {
170
+ ...staticStyles,
171
+ ...stylesByViewport[viewport]
149
172
  }
150
- })
173
+ }
174
+
151
175
  return (
152
- <BaseView ref={ref} {...viewProps} style={[styles.col]} dataSet={{ media: ids.col }}>
176
+ <BaseView
177
+ ref={ref}
178
+ {...viewProps}
179
+ style={colStyles}
180
+ dataSet={mediaIds && { media: mediaIds }}
181
+ >
153
182
  {children}
154
183
  </BaseView>
155
184
  )
@@ -16,6 +16,8 @@ import Row from './Row'
16
16
  import Col from './Col'
17
17
  import GutterContext from './providers/GutterContext'
18
18
  import applyInheritance from './helpers'
19
+ import { useTheme } from '../ThemeProvider'
20
+ import { useViewport } from '../ViewportProvider'
19
21
 
20
22
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
21
23
 
@@ -42,7 +44,15 @@ const FlexGrid = forwardRef(
42
44
  ref
43
45
  ) => {
44
46
  const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse])
45
- const mediaQueryStyles = createMediaQueryStyles({
47
+ const viewport = useViewport()
48
+ const {
49
+ themeOptions: { enableMediaQueryStyleSheet }
50
+ } = useTheme()
51
+
52
+ let flexgridStyles
53
+ let mediaIds
54
+
55
+ const stylesByViewport = {
46
56
  xs: {
47
57
  flexDirection: reverseLevel[0] ? 'column-reverse' : 'column'
48
58
  },
@@ -62,17 +72,31 @@ const FlexGrid = forwardRef(
62
72
  maxWidth: limitWidth && viewports.map.get('xl'),
63
73
  flexDirection: reverseLevel[4] ? 'column-reverse' : 'column'
64
74
  }
65
- })
75
+ }
66
76
 
67
- const { ids, styles } = StyleSheet.create({
68
- flexgrid: {
69
- flexWrap: 'wrap',
70
- width: outsideGutter ? '100%' : 'auto',
71
- marginVertical: 0,
72
- marginHorizontal: outsideGutter ? 'auto' : -16,
73
- ...mediaQueryStyles
77
+ const staticStyles = {
78
+ flexWrap: 'wrap',
79
+ width: outsideGutter ? '100%' : 'auto',
80
+ marginVertical: 0,
81
+ marginHorizontal: outsideGutter ? 'auto' : -16
82
+ }
83
+
84
+ if (enableMediaQueryStyleSheet) {
85
+ const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
86
+ const { ids, styles } = StyleSheet.create({
87
+ flexgrid: {
88
+ ...staticStyles,
89
+ ...mediaQueryStyles
90
+ }
91
+ })
92
+ flexgridStyles = styles.flexgrid
93
+ mediaIds = ids.flexgrid
94
+ } else {
95
+ flexgridStyles = {
96
+ ...staticStyles,
97
+ ...stylesByViewport[viewport]
74
98
  }
75
- })
99
+ }
76
100
 
77
101
  const props = {
78
102
  accessibilityRole,
@@ -82,7 +106,12 @@ const FlexGrid = forwardRef(
82
106
 
83
107
  return (
84
108
  <GutterContext.Provider value={gutter}>
85
- <BaseView ref={ref} {...props} style={[styles.flexgrid]} dataSet={{ media: ids.flexgrid }}>
109
+ <BaseView
110
+ ref={ref}
111
+ {...props}
112
+ style={flexgridStyles}
113
+ dataSet={mediaIds && { media: mediaIds }}
114
+ >
86
115
  {children}
87
116
  </BaseView>
88
117
  </GutterContext.Provider>
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types'
3
3
 
4
4
  import applyInheritance from '../helpers'
5
5
  import { BaseView, StyleSheet, createMediaQueryStyles } from '../../utils'
6
+ import { useTheme } from '../../ThemeProvider'
7
+ import { useViewport } from '../../ViewportProvider'
6
8
 
7
9
  const horizontalAlignStyles = (horizontalAlign) => {
8
10
  switch (horizontalAlign) {
@@ -66,8 +68,23 @@ const Row = forwardRef(
66
68
  },
67
69
  ref
68
70
  ) => {
71
+ const {
72
+ themeOptions: { enableMediaQueryStyleSheet }
73
+ } = useTheme()
74
+ const viewport = useViewport()
75
+ const staticStyles = {
76
+ width: '100%',
77
+ marginVertical: 0,
78
+ marginHorizontal: 'auto',
79
+ flexGrow: 0,
80
+ flexShrink: 1,
81
+ flexBasis: 'auto',
82
+ ...horizontalAlignStyles(horizontalAlign),
83
+ ...verticalAlignStyles(verticalAlign),
84
+ ...distributeStyles(distribute)
85
+ }
69
86
  const reverseLevel = applyInheritance([xsReverse, smReverse, mdReverse, lgReverse, xlReverse])
70
- const mediaQueryStyles = createMediaQueryStyles({
87
+ const stylesByViewport = {
71
88
  xs: {
72
89
  flexDirection: reverseLevel[0] ? 'row-reverse' : 'row',
73
90
  flexWrap: reverseLevel[0] ? 'wrap-reverse' : 'wrap'
@@ -88,23 +105,30 @@ const Row = forwardRef(
88
105
  flexDirection: reverseLevel[4] ? 'row-reverse' : 'row',
89
106
  flexWrap: reverseLevel[4] ? 'wrap-reverse' : 'wrap'
90
107
  }
91
- })
92
- const { ids, styles } = StyleSheet.create({
93
- row: {
94
- width: '100%',
95
- marginVertical: 0,
96
- marginHorizontal: 'auto',
97
- flexGrow: 0,
98
- flexShrink: 1,
99
- flexBasis: 'auto',
100
- ...horizontalAlignStyles(horizontalAlign),
101
- ...verticalAlignStyles(verticalAlign),
102
- ...distributeStyles(distribute),
103
- ...mediaQueryStyles
108
+ }
109
+
110
+ let rowStyles
111
+ let mediaIds
112
+
113
+ if (enableMediaQueryStyleSheet) {
114
+ const mediaQueryStyles = createMediaQueryStyles(stylesByViewport)
115
+ const { ids, styles } = StyleSheet.create({
116
+ row: {
117
+ ...staticStyles,
118
+ ...mediaQueryStyles
119
+ }
120
+ })
121
+ rowStyles = styles.row
122
+ mediaIds = ids.row
123
+ } else {
124
+ rowStyles = {
125
+ ...staticStyles,
126
+ ...stylesByViewport[viewport]
104
127
  }
105
- })
128
+ }
129
+
106
130
  return (
107
- <BaseView ref={ref} {...rest} style={[styles.row]} dataSet={{ media: ids.row }}>
131
+ <BaseView ref={ref} {...rest} style={rowStyles} dataSet={mediaIds && { media: mediaIds }}>
108
132
  {children}
109
133
  </BaseView>
110
134
  )
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable react-native-a11y/has-valid-accessibility-role */
2
2
  import React, { forwardRef } from 'react'
3
3
  import PropTypes from 'prop-types'
4
- import { View, StyleSheet } from 'react-native'
4
+ import { View, StyleSheet, Platform } from 'react-native'
5
5
  import { withLinkRouter } from '../utils'
6
6
  import ExpandCollapse from '../ExpandCollapse'
7
7
  import ListboxItem from './ListboxItem'
@@ -20,6 +20,13 @@ const styles = StyleSheet.create({
20
20
  }
21
21
  })
22
22
 
23
+ const getAccessibilityRole = () =>
24
+ Platform.select({
25
+ ios: 'listitem',
26
+ android: 'none',
27
+ web: 'listitem'
28
+ })
29
+
23
30
  const ListboxGroup = forwardRef(
24
31
  (
25
32
  {
@@ -39,7 +46,7 @@ const ListboxGroup = forwardRef(
39
46
 
40
47
  // TODO: implement keyboard navigation via refs for grouped items separately here
41
48
  return (
42
- <View id="test" style={styles.groupWrapper} accessibilityRole="listitem">
49
+ <View id="test" style={styles.groupWrapper} accessibilityRole={getAccessibilityRole()}>
43
50
  <ExpandCollapse.Panel
44
51
  panelId={id ?? label}
45
52
  controlTokens={{
@@ -89,6 +89,8 @@ const MultiSelectFilter = ({
89
89
  return colSize === 2 && setMaxWidth(true)
90
90
  }, [colSize])
91
91
 
92
+ useEffect(() => setCheckedIds(currentValues ?? []), [currentValues])
93
+
92
94
  const {
93
95
  headerFontColor,
94
96
  headerFontSize,
@@ -22,8 +22,20 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
22
22
 
23
23
  const selectContainerStyles = (tokens) => ({ ...tokens })
24
24
 
25
- const selectTextStyles = (tokens, themeOptions) =>
26
- applyTextStyles({ ...selectTokens('Typography', tokens), themeOptions })
25
+ const selectTextStyles = (tokens, themeOptions, isDismissible) => {
26
+ const textTokens = selectTokens('Typography', tokens)
27
+ const styles = {
28
+ ...textTokens,
29
+ themeOptions,
30
+ overflow: 'hidden'
31
+ }
32
+
33
+ if (!isDismissible) {
34
+ styles.flexShrink = 1
35
+ }
36
+
37
+ return applyTextStyles(styles)
38
+ }
27
39
 
28
40
  const selectIconProps = ({ iconSize, iconColor }) => ({
29
41
  size: iconSize,
@@ -113,7 +125,7 @@ const Notification = forwardRef(
113
125
  return null
114
126
  }
115
127
 
116
- const textStyles = selectTextStyles(themeTokens, themeOptions)
128
+ const textStyles = selectTextStyles(themeTokens, themeOptions, dismissible)
117
129
 
118
130
  const content = wrapStringsInText(
119
131
  typeof children === 'function' ? children({ textStyles, variant }) : children,
@@ -1,10 +1,22 @@
1
1
  /* eslint-disable react-native-a11y/has-valid-accessibility-role */
2
2
  import React, { forwardRef } from 'react'
3
3
  import PropTypes from 'prop-types'
4
- import { View, StyleSheet } from 'react-native'
4
+ import { View, StyleSheet, Platform } from 'react-native'
5
+
6
+ const getAccessibilityRole = () =>
7
+ Platform.select({
8
+ ios: 'listitem',
9
+ android: 'none',
10
+ web: 'listitem'
11
+ })
5
12
 
6
13
  const Item = forwardRef(({ children, style, ...rest }, ref) => (
7
- <View accessibilityRole="listitem" ref={ref} style={[staticStyles.container, style]} {...rest}>
14
+ <View
15
+ accessibilityRole={getAccessibilityRole()}
16
+ ref={ref}
17
+ style={[staticStyles.container, style]}
18
+ {...rest}
19
+ >
8
20
  {children}
9
21
  </View>
10
22
  ))
@@ -1,29 +1,42 @@
1
1
  import React from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { viewports } from '@telus-uds/system-constants'
4
- import { useResponsiveProp } from '../utils'
3
+ import { useTheme } from '../ThemeProvider'
4
+ import ResponsiveProp from './ResponsiveProp'
5
+ import ResponsiveWithMediaQueryStyleSheet from './ResponsiveWithMediaQueryStyleSheet'
5
6
 
6
7
  /**
7
8
  * Responsive conditionally renders children based on whether the viewport matches the provided
8
9
  * min and max viewports.
9
10
  *
10
- * In SSR, like other viewport utilities, it treats the viewport as `xs` both in SSR itself and
11
+ * If enableMediaQueryStyleSheet themeOption is set to false in ThemeProvider, then in SSR,
12
+ * like other viewport utilities, it treats the viewport as `xs` both in SSR itself and
11
13
  * during first hydration on the client side; then if the viewport is not `xs`, it re-renders
12
14
  * after hydration. This may cause a layout shift on devices other than the narrowest.
15
+ *
16
+ * If enableMediaQueryStyleSheet themeOption is set to true in ThemeProvider, then media query stylesheet
17
+ * is used to hide and show children of `Responsive` within a View.
13
18
  */
14
19
 
15
20
  const Responsive = ({ min = 'xs', max, children }) => {
16
- // Start returning children at the 'min' viewport or greater
17
- const byViewports = { [min]: children }
18
- if (max && max !== 'xl') {
19
- // Stop returning children at the viewport one above 'max' or greater
20
- const maxIndex = viewports.keys.indexOf(max)
21
- const maxPlusOne = maxIndex >= 0 ? viewports.keys[maxIndex + 1] : null
22
- if (maxPlusOne) byViewports[maxPlusOne] = null
21
+ const {
22
+ themeOptions: { enableMediaQueryStyleSheet }
23
+ } = useTheme()
24
+ if (enableMediaQueryStyleSheet) {
25
+ return (
26
+ <ResponsiveWithMediaQueryStyleSheet min={min} max={max}>
27
+ {children}
28
+ </ResponsiveWithMediaQueryStyleSheet>
29
+ )
23
30
  }
24
- return <>{useResponsiveProp(byViewports, null)}</>
31
+ return (
32
+ <ResponsiveProp min={min} max={max}>
33
+ {children}
34
+ </ResponsiveProp>
35
+ )
25
36
  }
26
37
 
38
+ Responsive.displayName = 'Responsive'
39
+
27
40
  Responsive.propTypes = {
28
41
  /**
29
42
  * To hide children of `Responsive` if the current viewport is smaller than `min`
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+
4
+ import { viewports } from '@telus-uds/system-constants'
5
+ import { useResponsiveProp } from '../utils'
6
+
7
+ const ResponsiveProp = ({ min = 'xs', max, children }) => {
8
+ const byViewports = { [min]: children }
9
+ if (max && max !== 'xl') {
10
+ // Stop returning children at the viewport one above 'max' or greater
11
+ const maxIndex = viewports.keys.indexOf(max)
12
+ const maxPlusOne = maxIndex >= 0 ? viewports.keys[maxIndex + 1] : null
13
+ if (maxPlusOne) byViewports[maxPlusOne] = null
14
+ }
15
+ const responsiveProp = useResponsiveProp(byViewports, null)
16
+ return <>{responsiveProp}</>
17
+ }
18
+
19
+ ResponsiveProp.displayName = 'Responsive'
20
+
21
+ ResponsiveProp.propTypes = {
22
+ /**
23
+ * To hide children of `Responsive` if the current viewport is smaller than `min`
24
+ */
25
+ min: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
26
+ /**
27
+ * To hide children of `Responsive` if the current viewport is larger than `max`
28
+ */
29
+ max: PropTypes.oneOf(['sm', 'md', 'lg', 'xl']),
30
+ children: PropTypes.node.isRequired
31
+ }
32
+
33
+ export default ResponsiveProp
@@ -0,0 +1,58 @@
1
+ import React from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import { viewports } from '@telus-uds/system-constants'
4
+ import { BaseView, StyleSheet, createMediaQueryStyles } from '../utils'
5
+
6
+ function generateResponsiveStyles(min, max) {
7
+ const viewportsArray = viewports.keys
8
+ const minIndex = viewportsArray.indexOf(min)
9
+ const maxIndex = viewportsArray.indexOf(max)
10
+ let hiddenViewports = []
11
+
12
+ if (minIndex !== -1) {
13
+ hiddenViewports = viewportsArray.slice(0, minIndex)
14
+ }
15
+ if (maxIndex !== -1) {
16
+ hiddenViewports = hiddenViewports.concat(viewportsArray.slice(maxIndex + 1))
17
+ }
18
+
19
+ const styles = {}
20
+
21
+ viewportsArray.forEach((viewport) => {
22
+ if (hiddenViewports.includes(viewport)) {
23
+ styles[viewport] = { display: 'none' }
24
+ }
25
+ })
26
+ return createMediaQueryStyles(styles, false)
27
+ }
28
+ const ResponsiveWithMediaQueryStyleSheet = ({ min, max, children }) => {
29
+ const { ids, styles } = StyleSheet.create({
30
+ responsive: {
31
+ flexDirection: 'inherit',
32
+ alignItems: 'inherit',
33
+ justifyContent: 'inherit',
34
+ ...generateResponsiveStyles(min, max)
35
+ }
36
+ })
37
+ return (
38
+ <BaseView style={styles.responsive} dataSet={ids.responsive && { media: ids.responsive }}>
39
+ {children}
40
+ </BaseView>
41
+ )
42
+ }
43
+
44
+ ResponsiveWithMediaQueryStyleSheet.displayName = 'Responsive'
45
+
46
+ ResponsiveWithMediaQueryStyleSheet.propTypes = {
47
+ /**
48
+ * To hide children of `Responsive` if the current viewport is smaller than `min`
49
+ */
50
+ min: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl']),
51
+ /**
52
+ * To hide children of `Responsive` if the current viewport is larger than `max`
53
+ */
54
+ max: PropTypes.oneOf(['sm', 'md', 'lg', 'xl']),
55
+ children: PropTypes.node.isRequired
56
+ }
57
+
58
+ export default ResponsiveWithMediaQueryStyleSheet
@@ -16,7 +16,8 @@ const defaultThemeOptions = {
16
16
  // TODO: switch `forceZIndex` to be false by default in the next major version
17
17
  forceZIndex: true,
18
18
  // TODO: switch `enableHelmetSSR` to be false by default in the next major version
19
- enableHelmetSSR: true
19
+ enableHelmetSSR: true,
20
+ enableMediaQueryStyleSheet: false
20
21
  }
21
22
 
22
23
  const ThemeProvider = ({ children, defaultTheme, themeOptions = {} }) => {
@@ -65,12 +66,14 @@ ThemeProvider.propTypes = {
65
66
  * - `enableHelmetSSR`: on Web SSR, allows React Helmet to run during server-side rendering. This should be
66
67
  * disabled unless a web app has been specifically configured to stop React Helmet accumulating
67
68
  * instances (which may cause a memory leak). See React Helmet's docs: https://github.com/nfl/react-helmet
69
+ * - `enableMediaQueryStyleSheet`: enables the use of Media Query StyleSheet.
68
70
  */
69
71
  themeOptions: PropTypes.shape({
70
72
  forceAbsoluteFontSizing: PropTypes.bool,
71
73
  forceZIndex: PropTypes.bool,
72
74
  enableHelmetSSR: PropTypes.bool,
73
- contentMaxWidth: responsiveProps.getTypeOptionallyByViewport(PropTypes.number)
75
+ contentMaxWidth: responsiveProps.getTypeOptionallyByViewport(PropTypes.number),
76
+ enableMediaQueryStyleSheet: PropTypes.bool
74
77
  })
75
78
  }
76
79
 
@@ -2,6 +2,7 @@ import ThemeProvider from './ThemeProvider'
2
2
 
3
3
  export { default as useTheme } from './useTheme'
4
4
  export { default as useSetTheme } from './useSetTheme'
5
+ export { default as useResponsiveThemeTokens } from './useResponsiveThemeTokens'
5
6
  export * from './useThemeTokens'
6
7
 
7
8
  export * from './utils'