@telus-uds/components-base 1.51.1 → 1.52.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.
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "@floating-ui/react-native": "^0.8.1",
12
12
  "@gorhom/portal": "^1.0.14",
13
13
  "@telus-uds/system-constants": "^1.2.1",
14
- "@telus-uds/system-theme-tokens": "^2.34.0",
14
+ "@telus-uds/system-theme-tokens": "^2.35.0",
15
15
  "airbnb-prop-types": "^2.16.0",
16
16
  "lodash.debounce": "^4.0.8",
17
17
  "lodash.merge": "^4.6.2",
@@ -72,5 +72,5 @@
72
72
  "standard-engine": {
73
73
  "skip": true
74
74
  },
75
- "version": "1.51.1"
75
+ "version": "1.52.0"
76
76
  }
@@ -0,0 +1,6 @@
1
+ import React, { useContext } from 'react'
2
+
3
+ const ListboxContext = React.createContext({})
4
+ const useListboxContext = () => useContext(ListboxContext)
5
+
6
+ export { ListboxContext, useListboxContext }
@@ -0,0 +1,143 @@
1
+ /* eslint-disable react/require-default-props */
2
+ import React, { forwardRef } from 'react'
3
+ import PropTypes from 'prop-types'
4
+ import { Pressable, Text } from 'react-native'
5
+ import { selectSystemProps, resolvePressableTokens, htmlAttrs } from '../utils'
6
+ import { useListboxContext } from './ListboxContext'
7
+
8
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([htmlAttrs])
9
+
10
+ const getItemStyles = ({
11
+ groupFontName,
12
+ groupFontWeight,
13
+ itemFontSize,
14
+ itemPaddingTop,
15
+ itemPaddingBottom,
16
+ itemPaddingLeft,
17
+ itemBorderLeftWidth,
18
+ itemPaddingRight,
19
+ itemBackgroundColor,
20
+ itemColor,
21
+ itemDisplay,
22
+ itemOutline,
23
+ itemTextDecoration,
24
+ itemBorderLeftColor,
25
+ itemBorderRightWidth,
26
+ itemBorderRightColor,
27
+ itemBorderTopWidth,
28
+ itemBorderTopColor,
29
+ itemBorderBottomWidth,
30
+ itemBorderBottomColor,
31
+ itemBorderRadius,
32
+ itemHeight
33
+ }) => ({
34
+ fontFamily: `${groupFontName}${groupFontWeight}normal`,
35
+ fontSize: itemFontSize,
36
+ paddingTop: itemPaddingTop - itemBorderTopWidth,
37
+ paddingBottom: itemPaddingBottom - itemBorderBottomWidth,
38
+ paddingLeft: itemPaddingLeft - itemBorderLeftWidth,
39
+ paddingRight: itemPaddingRight - itemBorderRightWidth,
40
+ width: '100%',
41
+ backgroundColor: itemBackgroundColor,
42
+ color: itemColor,
43
+ display: itemDisplay,
44
+ outline: itemOutline,
45
+ textDecoration: itemTextDecoration,
46
+ borderLeft: `${itemBorderLeftWidth}px solid ${itemBorderLeftColor}`,
47
+ borderRight: `${itemBorderRightWidth}px solid ${itemBorderRightColor}`,
48
+ borderTop: `${itemBorderTopWidth}px solid ${itemBorderTopColor}`,
49
+ borderBottom: `${itemBorderBottomWidth}px solid ${itemBorderBottomColor}`,
50
+ borderRadius: itemBorderRadius,
51
+ height: itemHeight,
52
+ justifyContent: 'center'
53
+ })
54
+
55
+ const PressableItem = forwardRef(
56
+ (
57
+ {
58
+ children,
59
+ href,
60
+ isChild = false,
61
+ onBlur,
62
+ onPress,
63
+ tabIndex = 0,
64
+ nextItemRef,
65
+ prevItemRef,
66
+ tokens,
67
+ id,
68
+ ...rest
69
+ },
70
+ ref
71
+ ) => {
72
+ const { selectedId, setSelectedId } = useListboxContext()
73
+
74
+ const selectTextStyles = ({
75
+ groupFontName,
76
+ groupFontWeight,
77
+ itemFontSize,
78
+ itemColor,
79
+ lineHeight
80
+ }) => ({
81
+ fontFamily: `${groupFontName}${groupFontWeight}normal`,
82
+ fontSize: itemFontSize,
83
+ color: itemColor,
84
+ lineHeight: lineHeight * itemFontSize
85
+ })
86
+
87
+ const resolveButtonTokens = (pressableState) => {
88
+ const themeTokens = resolvePressableTokens(tokens, pressableState, {
89
+ isChild,
90
+ current: id === selectedId && id !== undefined
91
+ })
92
+ return themeTokens
93
+ }
94
+
95
+ const handleKeyPress = (event) => {
96
+ if (['Enter', ' '].includes(event?.key)) {
97
+ onPress?.(event)
98
+ } else if (event?.key === 'ArrowDown') {
99
+ nextItemRef.current.focus()
100
+ } else if (event?.key === 'ArrowUp' && prevItemRef?.current) {
101
+ prevItemRef.current.focus()
102
+ }
103
+ }
104
+
105
+ return (
106
+ <Pressable
107
+ isChild={isChild}
108
+ style={(pressableState) => getItemStyles(resolveButtonTokens(pressableState))}
109
+ onBlur={onBlur}
110
+ onClick={onPress}
111
+ onKeyPress={handleKeyPress}
112
+ ref={ref}
113
+ tabIndex={tabIndex}
114
+ {...(href && { href })}
115
+ {...(onPress && { onClick: onPress })}
116
+ {...selectProps(rest)}
117
+ onPress={() => {
118
+ setSelectedId(id)
119
+ onPress()
120
+ }}
121
+ >
122
+ {(pressableState) => {
123
+ return (
124
+ <Text style={selectTextStyles(resolveButtonTokens(pressableState))}>{children} </Text>
125
+ )
126
+ }}
127
+ </Pressable>
128
+ )
129
+ }
130
+ )
131
+ PressableItem.displayName = 'PressableItem'
132
+ PressableItem.propTypes = {
133
+ ...selectedSystemPropTypes,
134
+ href: PropTypes.string,
135
+ isChild: PropTypes.bool,
136
+ children: PropTypes.node.isRequired,
137
+ onBlur: PropTypes.func,
138
+ onPress: PropTypes.func,
139
+ nextItemRef: PropTypes.object,
140
+ prevItemRef: PropTypes.object
141
+ }
142
+
143
+ export default PressableItem
@@ -0,0 +1,2 @@
1
+ export * from './ListboxContext'
2
+ export { default as PressableItem } from './PressableItem'
@@ -16,8 +16,7 @@ import {
16
16
  selectSystemProps,
17
17
  useCopy,
18
18
  variantProp,
19
- viewProps,
20
- selectTokens
19
+ viewProps
21
20
  } from '../utils'
22
21
  import { useViewport } from '../ViewportProvider'
23
22
  import IconButton from '../IconButton'
@@ -138,7 +137,6 @@ const Modal = forwardRef(
138
137
  icon={CloseIconComponent}
139
138
  accessibilityRole="button"
140
139
  accessibilityLabel={closeLabel}
141
- tokens={selectTokens('IconButton', themeTokens, 'close')}
142
140
  />
143
141
  )}
144
142
  </View>
@@ -1,6 +1,7 @@
1
1
  import React, { useState, useEffect } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
+ import { View, StyleSheet } from 'react-native'
4
5
  import { useThemeTokens, useThemeTokensCallback, applyTextStyles } from '../ThemeProvider'
5
6
  import {
6
7
  containUniqueFields,
@@ -104,7 +105,6 @@ const MultiSelectFilter = ({
104
105
  if (!containUniqueFields(items, uniqueFields)) {
105
106
  throw new Error(`MultiSelectFilter items must have unique ${uniqueFields.join(', ')}`)
106
107
  }
107
-
108
108
  // Pass an object of relevant component state as first argument for any passed-in press handlers
109
109
  const pressHandlers = getPressHandlersWithArgs(rest, [{ id, label, currentValues }])
110
110
 
@@ -239,9 +239,11 @@ const MultiSelectFilter = ({
239
239
  onLayout={onTargetLayout}
240
240
  >
241
241
  <Row>
242
- <Typography tokens={{ ...headerStyles, lineHeight: headerLineHeight }}>
243
- {getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())}
244
- </Typography>
242
+ <View style={styles.textContainerStyle}>
243
+ <Typography tokens={{ ...headerStyles, lineHeight: headerLineHeight }}>
244
+ {getCopy('filterByLabel').replace(/%\{filterCategory\}/g, label.toLowerCase())}
245
+ </Typography>
246
+ </View>
245
247
  </Row>
246
248
  {subtitle && (
247
249
  <>
@@ -301,6 +303,10 @@ const MultiSelectFilter = ({
301
303
 
302
304
  MultiSelectFilter.displayName = 'MultiSelectFilter'
303
305
 
306
+ const styles = StyleSheet.create({
307
+ textContainerStyle: { marginRight: 52 }
308
+ })
309
+
304
310
  MultiSelectFilter.propTypes = {
305
311
  /**
306
312
  * The text displayed to the user in a ButtonDropdown.
@@ -23,10 +23,10 @@ const selectDotStyles = ({ dotWidth, timelineColor, dotBorderWidth, dotColor })
23
23
  borderColor: timelineColor
24
24
  })
25
25
 
26
- const selectConnectorStyles = ({ timelineColor, connectorHeight, connectorWidth }) => ({
26
+ const selectConnectorStyles = ({ timelineConnectorColor, connectorHeight, connectorWidth }) => ({
27
27
  width: connectorWidth,
28
28
  height: connectorHeight,
29
- backgroundColor: timelineColor
29
+ backgroundColor: timelineConnectorColor
30
30
  })
31
31
 
32
32
  const selectTimelineContainerStyle = ({ timelineContainerDirection }) => ({
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ export { default as Box } from './Box'
4
4
  export * from './Button'
5
5
  export { default as Card, PressableCardBase } from './Card'
6
6
  export * from './Carousel'
7
+ export * from './Listbox'
7
8
  export { default as Checkbox } from './Checkbox'
8
9
  export * from './Checkbox'
9
10
  export { default as Divider } from './Divider'
@@ -0,0 +1,29 @@
1
+ import PropTypes from 'prop-types'
2
+
3
+ const htmlAttrTypes = {
4
+ dataSet: PropTypes.object,
5
+ id: PropTypes.string,
6
+ loading: PropTypes.oneOf(['eager', 'lazy']),
7
+ // @todo figure out if we need to enum all the possible roles or maybe use
8
+ // an npm package
9
+ role: PropTypes.string,
10
+ src: PropTypes.string,
11
+ tabIndex: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
12
+ testID: PropTypes.string,
13
+ title: PropTypes.string
14
+ }
15
+
16
+ export default {
17
+ types: htmlAttrTypes,
18
+ select: (props) =>
19
+ Object.entries(props).reduce(
20
+ (items, [key, value]) =>
21
+ Object.keys(htmlAttrTypes).includes(key) || /^(data|aria)-/.test(key)
22
+ ? {
23
+ ...items,
24
+ [key]: value
25
+ }
26
+ : items,
27
+ {}
28
+ )
29
+ }
@@ -19,3 +19,4 @@ export { default as withLinkRouter } from './withLinkRouter'
19
19
  export * from './ssr'
20
20
  export { default as containUniqueFields } from './containUniqueFields'
21
21
  export { default as BaseView } from './BaseView'
22
+ export { default as htmlAttrs } from './htmlAttrs'