@telus-uds/components-base 1.82.0 → 1.84.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.
@@ -1,5 +1,5 @@
1
1
  import React, { forwardRef } from 'react'
2
- import { View } from 'react-native'
2
+ import { View, StyleSheet } from 'react-native'
3
3
  import PropTypes from 'prop-types'
4
4
 
5
5
  import { useThemeTokens } from '../ThemeProvider'
@@ -46,10 +46,12 @@ const ExpandCollapse = forwardRef(
46
46
  const themeTokens = useThemeTokens('ExpandCollapse', tokens, variant)
47
47
 
48
48
  return (
49
- <View ref={ref} style={selectBorderStyles(themeTokens)} {...selectProps(rest)}>
50
- {typeof children === 'function'
51
- ? children({ openIds, onToggle, resetValues, setValues, unsetValues })
52
- : children}
49
+ <View style={staticStyles.container} ref={ref} {...selectProps(rest)}>
50
+ <View style={selectBorderStyles(themeTokens)}>
51
+ {typeof children === 'function'
52
+ ? children({ openIds, onToggle, resetValues, setValues, unsetValues })
53
+ : children}
54
+ </View>
53
55
  </View>
54
56
  )
55
57
  }
@@ -86,3 +88,9 @@ ExpandCollapse.propTypes = {
86
88
  }
87
89
 
88
90
  export default ExpandCollapse
91
+
92
+ const staticStyles = StyleSheet.create({
93
+ container: {
94
+ display: 'flex'
95
+ }
96
+ })
@@ -11,9 +11,12 @@ import {
11
11
  selectSystemProps,
12
12
  useVerticalExpandAnimation,
13
13
  variantProp,
14
- viewProps
14
+ viewProps,
15
+ useCopy
15
16
  } from '../utils'
16
17
 
18
+ import dictionary from './dictionary'
19
+
17
20
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
18
21
 
19
22
  // just void functions for now, since there are no style tokens for a panel or control at this point
@@ -92,12 +95,14 @@ const ExpandCollapsePanel = forwardRef(
92
95
  variant,
93
96
  controlRef,
94
97
  content,
98
+ copy = 'en',
95
99
  ...rest
96
100
  },
97
101
  ref
98
102
  ) => {
99
103
  const [containerHeight, setContainerHeight] = useState(null)
100
104
  const isExpanded = openIds.includes(panelId)
105
+ const getCopy = useCopy({ dictionary, copy })
101
106
 
102
107
  const selectedProps = selectProps({
103
108
  ...rest,
@@ -130,16 +135,28 @@ const ExpandCollapsePanel = forwardRef(
130
135
  })
131
136
 
132
137
  const focusabilityProps = isExpanded ? {} : a11yProps.nonFocusableProps
138
+
139
+ const panelAccessibilityLabel = `${panelId} ${getCopy('panel')}`
140
+ const subPanelAccessibilityLabel = `${panelId} ${getCopy('subPanel')}`
141
+
133
142
  return content ? (
134
- <View style={selectContentPanelStyles(themeTokens)}>
143
+ <View
144
+ style={selectContentPanelStyles(themeTokens)}
145
+ accessibilityLabel={panelAccessibilityLabel}
146
+ >
135
147
  {typeof children === 'string' ? (
136
- <Text style={selectTextStyles(themeTokens)}>{children}</Text>
148
+ <Text
149
+ style={selectTextStyles(themeTokens)}
150
+ accessibilityLabel={subPanelAccessibilityLabel}
151
+ >
152
+ {children}
153
+ </Text>
137
154
  ) : (
138
155
  children
139
156
  )}
140
157
  </View>
141
158
  ) : (
142
- <View ref={ref} style={themeTokens}>
159
+ <View ref={ref} style={themeTokens} accessibilityLabel={panelAccessibilityLabel}>
143
160
  <View style={selectControlPanelStyles(themeTokens)}>
144
161
  <ExpandCollapseControl
145
162
  {...selectedProps}
@@ -174,7 +191,12 @@ const ExpandCollapsePanel = forwardRef(
174
191
  })
175
192
  }}
176
193
  >
177
- <View style={selectContainerStyles(themeTokens)}>{children}</View>
194
+ <View
195
+ style={selectContainerStyles(themeTokens)}
196
+ accessibilityLabel={subPanelAccessibilityLabel}
197
+ >
198
+ {children}
199
+ </View>
178
200
  </View>
179
201
  </Animated.View>
180
202
  </View>
@@ -0,0 +1,10 @@
1
+ export default {
2
+ en: {
3
+ panel: 'Panel',
4
+ subPanel: 'SubPanel'
5
+ },
6
+ fr: {
7
+ panel: 'Panneau',
8
+ subPanel: 'Sous-Panneau'
9
+ }
10
+ }
@@ -31,8 +31,10 @@ const IconText = forwardRef(
31
31
  */
32
32
  const resultY = valueTranslateY ? Math.floor(-1 * (valueTranslateY - 4)) : 0
33
33
 
34
- const iconAdjustedAndriod = (
35
- <View style={{ transform: [{ translateY: size * 0.2 }] }}>{iconContent}</View>
34
+ const iconAdjustedAndroid = (
35
+ <View style={{ transform: [{ translateY: valueTranslateY ? size * 0.2 : size * 0.01 }] }}>
36
+ {iconContent}
37
+ </View>
36
38
  )
37
39
 
38
40
  const iconAdjustedIOS = (
@@ -41,7 +43,7 @@ const IconText = forwardRef(
41
43
  </View>
42
44
  )
43
45
 
44
- const mobile = Platform.OS === 'android' ? iconAdjustedAndriod : iconAdjustedIOS
46
+ const mobile = Platform.OS === 'android' ? iconAdjustedAndroid : iconAdjustedIOS
45
47
  const adjustedContainer = Platform.OS === 'web' ? iconContent : mobile
46
48
 
47
49
  return getStackedContent(
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useRef, useState } from 'react'
1
+ import React, { forwardRef, useCallback, useEffect, useRef, useState } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import { View, StyleSheet, Platform } from 'react-native'
4
4
  import { useThemeTokens } from '../ThemeProvider'
@@ -25,119 +25,126 @@ const getInitialOpen = (items, selectedId) =>
25
25
  )
26
26
  .map((item) => item.id ?? item.label)
27
27
 
28
- const Listbox = ({
29
- items = [],
30
- firstItemRef = null, // focus will be moved to this one once within the menu
31
- parentRef = null, // to return focus to after leaving the last menu item
32
- selectedId: defaultSelectedId,
33
- LinkRouter,
34
- itemRouterProps,
35
- onClose,
36
- variant,
37
- tokens
38
- }) => {
39
- const initialOpen = getInitialOpen(items, defaultSelectedId)
28
+ const Listbox = forwardRef(
29
+ (
30
+ {
31
+ items = [],
32
+ firstItemRef = null, // focus will be moved to this one once within the menu
33
+ parentRef = null, // to return focus to after leaving the last menu item
34
+ selectedId: defaultSelectedId,
35
+ LinkRouter,
36
+ itemRouterProps,
37
+ onClose,
38
+ variant,
39
+ tokens
40
+ },
41
+ ref
42
+ ) => {
43
+ const initialOpen = getInitialOpen(items, defaultSelectedId)
40
44
 
41
- const [selectedId, setSelectedId] = useState(defaultSelectedId)
45
+ const [selectedId, setSelectedId] = useState(defaultSelectedId)
42
46
 
43
- const { minHeight, minWidth } = useThemeTokens('Listbox', variant, tokens)
47
+ const { minHeight, minWidth } = useThemeTokens('Listbox', variant, tokens)
44
48
 
45
- // We need to keep track of each item's ref in order to be able to
46
- // focus on a specific item via keyboard navigation
47
- const itemRefs = useRef([])
48
- if (firstItemRef?.current) itemRefs.current[0] = firstItemRef.current
49
- const [focusedIndex, setFocusedIndex] = useState(0)
50
- const handleKeydown = useCallback(
51
- (event) => {
52
- const nextItemRef = itemRefs.current[focusedIndex + 1]
53
- const prevItemRef = itemRefs.current[focusedIndex - 1]
54
- if (event.key === 'ArrowUp' || (event.shiftKey && event.key === 'Tab')) {
55
- // Move the focus to the previous item or to the parent one if on the first
56
- if (prevItemRef) {
49
+ // We need to keep track of each item's ref in order to be able to
50
+ // focus on a specific item via keyboard navigation
51
+ const itemRefs = useRef([])
52
+ if (firstItemRef?.current) itemRefs.current[0] = firstItemRef.current
53
+ const [focusedIndex, setFocusedIndex] = useState(0)
54
+ const handleKeydown = useCallback(
55
+ (event) => {
56
+ const nextItemRef = itemRefs.current[focusedIndex + 1]
57
+ const prevItemRef = itemRefs.current[focusedIndex - 1]
58
+ if (event.key === 'ArrowUp' || (event.shiftKey && event.key === 'Tab')) {
59
+ // Move the focus to the previous item or to the parent one if on the first
60
+ if (prevItemRef) {
61
+ event.preventDefault()
62
+ prevItemRef.focus()
63
+ } else if (parentRef) parentRef.current?.focus()
64
+ setFocusedIndex(focusedIndex - 1)
65
+ } else if ((event.key === 'ArrowDown' || event.key === 'Tab') && nextItemRef) {
57
66
  event.preventDefault()
58
- prevItemRef.focus()
59
- } else if (parentRef) parentRef.current?.focus()
60
- setFocusedIndex(focusedIndex - 1)
61
- } else if ((event.key === 'ArrowDown' || event.key === 'Tab') && nextItemRef) {
62
- event.preventDefault()
63
- setFocusedIndex(focusedIndex + 1)
64
- nextItemRef.focus()
65
- } else if (event.key === 'Escape') {
66
- // Close the dropdown
67
- parentRef?.current?.click()
68
- // Return focus to the dropdown control after leaving the last item
69
- parentRef?.current?.focus()
70
- if (onClose) onClose(event)
71
- } else if (!nextItemRef && firstItemRef) {
72
- // If the last item is focused, move the focus to the first one
73
- event.preventDefault()
74
- setFocusedIndex(0)
75
- firstItemRef.current?.focus()
76
- }
77
- },
78
- [focusedIndex, onClose, parentRef, firstItemRef]
79
- )
67
+ setFocusedIndex(focusedIndex + 1)
68
+ nextItemRef.focus()
69
+ } else if (event.key === 'Escape') {
70
+ // Close the dropdown
71
+ parentRef?.current?.click()
72
+ // Return focus to the dropdown control after leaving the last item
73
+ parentRef?.current?.focus()
74
+ if (onClose) onClose(event)
75
+ } else if (!nextItemRef && firstItemRef) {
76
+ // If the last item is focused, move the focus to the first one
77
+ event.preventDefault()
78
+ setFocusedIndex(0)
79
+ firstItemRef.current?.focus()
80
+ }
81
+ },
82
+ [focusedIndex, onClose, parentRef, firstItemRef]
83
+ )
80
84
 
81
- // Add listeners for mouse clicks outside and for key presses
82
- useEffect(() => {
83
- if (Platform.OS === 'web') {
84
- window.addEventListener('click', onClose)
85
- window.addEventListener('keydown', handleKeydown)
86
- window.addEventListener('touchstart', onClose)
87
- return () => {
88
- window.removeEventListener('click', onClose)
89
- window.removeEventListener('keydown', handleKeydown)
90
- window.removeEventListener('touchstart', onClose)
85
+ // Add listeners for mouse clicks outside and for key presses
86
+ useEffect(() => {
87
+ if (Platform.OS === 'web') {
88
+ window.addEventListener('click', onClose)
89
+ window.addEventListener('keydown', handleKeydown)
90
+ window.addEventListener('touchstart', onClose)
91
+ return () => {
92
+ window.removeEventListener('click', onClose)
93
+ window.removeEventListener('keydown', handleKeydown)
94
+ window.removeEventListener('touchstart', onClose)
95
+ }
91
96
  }
92
- }
93
- return () => {}
94
- }, [onClose, handleKeydown])
97
+ return () => {}
98
+ }, [onClose, handleKeydown])
95
99
 
96
- return (
97
- <ListboxContext.Provider value={{ selectedId, setSelectedId }}>
98
- <ExpandCollapse initialOpen={initialOpen} maxOpen={1}>
99
- {(expandProps) => (
100
- <View style={[styles.list, { minHeight, minWidth }]} role="listbox">
101
- {items.map((item, index) => {
102
- const { id, label, items: nestedItems } = item
103
- const itemId = id ?? label
100
+ return (
101
+ <ListboxContext.Provider value={{ selectedId, setSelectedId }}>
102
+ <ExpandCollapse initialOpen={initialOpen} maxOpen={1} ref={ref}>
103
+ {(expandProps) => (
104
+ <View style={[styles.list, { minHeight, minWidth }]} role="listbox">
105
+ {items.map((item, index) => {
106
+ const { id, label, items: nestedItems } = item
107
+ const itemId = id ?? label
104
108
 
105
- // Give the list of refs.
106
- const itemRef = (ref) => {
107
- itemRefs.current[index] = ref
108
- return ref
109
- }
109
+ // Give the list of refs.
110
+ const itemRef = (currentItemRef) => {
111
+ itemRefs.current[index] = currentItemRef
112
+ return currentItemRef
113
+ }
110
114
 
111
- return nestedItems ? (
112
- <ListboxGroup
113
- {...item}
114
- expandProps={expandProps}
115
- LinkRouter={LinkRouter}
116
- itemRouterProps={itemRouterProps}
117
- prevItemRef={itemRefs.current[index - 1] ?? null}
118
- nextItemRef={itemRefs.current[index + 1] ?? null}
119
- ref={index === 0 ? firstItemRef : itemRef}
120
- key={itemId}
121
- />
122
- ) : (
123
- <ListboxItem
124
- {...item}
125
- key={itemId}
126
- id={itemId}
127
- LinkRouter={LinkRouter}
128
- itemRouterProps={itemRouterProps}
129
- prevItemRef={itemRefs.current[index - 1] ?? null}
130
- nextItemRef={itemRefs.current[index + 1] ?? null}
131
- ref={index === 0 ? firstItemRef : itemRef}
132
- />
133
- )
134
- })}
135
- </View>
136
- )}
137
- </ExpandCollapse>
138
- </ListboxContext.Provider>
139
- )
140
- }
115
+ return nestedItems ? (
116
+ <ListboxGroup
117
+ {...item}
118
+ expandProps={expandProps}
119
+ LinkRouter={LinkRouter}
120
+ itemRouterProps={itemRouterProps}
121
+ prevItemRef={itemRefs.current[index - 1] ?? null}
122
+ nextItemRef={itemRefs.current[index + 1] ?? null}
123
+ ref={index === 0 ? firstItemRef : itemRef}
124
+ key={itemId}
125
+ />
126
+ ) : (
127
+ <ListboxItem
128
+ {...item}
129
+ key={itemId}
130
+ id={itemId}
131
+ LinkRouter={LinkRouter}
132
+ itemRouterProps={itemRouterProps}
133
+ prevItemRef={itemRefs.current[index - 1] ?? null}
134
+ nextItemRef={itemRefs.current[index + 1] ?? null}
135
+ ref={index === 0 ? firstItemRef : itemRef}
136
+ />
137
+ )
138
+ })}
139
+ </View>
140
+ )}
141
+ </ExpandCollapse>
142
+ </ListboxContext.Provider>
143
+ )
144
+ }
145
+ )
146
+
147
+ Listbox.displayName = 'Listbox'
141
148
 
142
149
  Listbox.propTypes = {
143
150
  ...withLinkRouter.propTypes,
@@ -16,7 +16,10 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, vie
16
16
  const WebModal = ({ children, ...rest }) => {
17
17
  return (
18
18
  <View style={staticStyles.container} {...selectProps(rest)}>
19
- <View style={staticStyles.content}>{children}</View>
19
+ {/* eslint-disable-next-line react-native-a11y/has-valid-accessibility-role */}
20
+ <View style={staticStyles.content} accessibilityRole="dialog">
21
+ {children}
22
+ </View>
20
23
  </View>
21
24
  )
22
25
  }
@@ -46,14 +49,15 @@ const staticStyles = StyleSheet.create({
46
49
  minHeight: 0,
47
50
  minWidth: 0,
48
51
  padding: 0,
49
- textDecoration: 'none',
50
- zIndex: 1
52
+ textDecorationLine: 'none',
53
+ zIndex: 1000
51
54
  },
52
55
  content: {
53
56
  flex: 1,
54
57
  flexGrow: 1,
55
58
  flexShrink: 1,
56
- flexBasis: 0
59
+ flexBasis: 0,
60
+ zIndex: 1000
57
61
  }
58
62
  })
59
63
 
@@ -65,7 +65,7 @@ const selectDismissButtonContainerStyles = ({ dismissButtonGap }) => ({
65
65
  })
66
66
 
67
67
  const selectContentContainerStyle = (maxWidth) => ({
68
- width: maxWidth || '100%'
68
+ maxWidth: maxWidth || '100%'
69
69
  })
70
70
 
71
71
  const getMediaQueryStyles = (themeTokens, themeOptions, viewport, mediaIdsRef, dismissible) => {
@@ -247,7 +247,7 @@ Radio.propTypes = {
247
247
  /**
248
248
  * The label.
249
249
  */
250
- label: PropTypes.string,
250
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
251
251
  /**
252
252
  * Associate this radio button with a group (set as the name attribute).
253
253
  */