@telus-uds/components-base 1.5.0 → 1.7.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 (125) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/.turbo/turbo-lint.log +13 -0
  3. package/CHANGELOG.json +154 -1
  4. package/CHANGELOG.md +47 -2
  5. package/__tests__/FlexGrid/Row.test.jsx +100 -25
  6. package/__tests__/utils/containUniqueFields.test.js +25 -0
  7. package/component-docs.json +78 -26
  8. package/generate-component-docs.js +20 -7
  9. package/lib/Button/ButtonBase.js +1 -1
  10. package/lib/Button/ButtonGroup.js +20 -12
  11. package/lib/Card/PressableCardBase.js +1 -1
  12. package/lib/Checkbox/Checkbox.js +27 -16
  13. package/lib/Checkbox/CheckboxGroup.js +19 -5
  14. package/lib/ExpandCollapse/Panel.js +10 -10
  15. package/lib/FlexGrid/Col/Col.js +13 -3
  16. package/lib/FlexGrid/Row/Row.js +8 -2
  17. package/lib/InputLabel/InputLabel.js +27 -25
  18. package/lib/Link/LinkBase.js +19 -6
  19. package/lib/Link/TextButton.js +1 -10
  20. package/lib/List/ListItem.js +22 -12
  21. package/lib/Modal/Modal.js +18 -18
  22. package/lib/Radio/Radio.js +23 -12
  23. package/lib/Radio/RadioGroup.js +12 -3
  24. package/lib/RadioCard/RadioCard.js +1 -1
  25. package/lib/RadioCard/RadioCardGroup.js +11 -2
  26. package/lib/Search/Search.js +27 -19
  27. package/lib/Select/Select.js +2 -3
  28. package/lib/Tags/Tags.js +23 -17
  29. package/lib/TextInput/TextArea.js +2 -2
  30. package/lib/TextInput/TextInput.js +12 -2
  31. package/lib/TextInput/TextInputBase.js +1 -1
  32. package/lib/TextInput/propTypes.js +8 -1
  33. package/lib/ToggleSwitch/ToggleSwitch.js +5 -2
  34. package/lib/ToggleSwitch/ToggleSwitchGroup.js +20 -12
  35. package/lib/Typography/Typography.js +12 -10
  36. package/lib/index.js +22 -1
  37. package/lib/utils/containUniqueFields.js +34 -0
  38. package/lib/utils/index.js +10 -1
  39. package/lib/utils/input.js +5 -6
  40. package/lib/utils/props/handlerProps.js +72 -0
  41. package/lib/utils/props/index.js +32 -0
  42. package/lib/utils/props/inputSupportsProps.js +3 -5
  43. package/lib/utils/props/linkProps.js +3 -7
  44. package/lib/utils/props/textInputProps.js +206 -0
  45. package/lib/utils/props/textProps.js +72 -0
  46. package/lib-module/Button/ButtonBase.js +2 -2
  47. package/lib-module/Button/ButtonGroup.js +15 -6
  48. package/lib-module/Card/PressableCardBase.js +2 -2
  49. package/lib-module/Checkbox/Checkbox.js +28 -17
  50. package/lib-module/Checkbox/CheckboxGroup.js +20 -7
  51. package/lib-module/ExpandCollapse/Panel.js +10 -10
  52. package/lib-module/FlexGrid/Col/Col.js +13 -3
  53. package/lib-module/FlexGrid/Row/Row.js +8 -2
  54. package/lib-module/InputLabel/InputLabel.js +28 -25
  55. package/lib-module/Link/LinkBase.js +19 -6
  56. package/lib-module/Link/TextButton.js +1 -10
  57. package/lib-module/List/ListItem.js +22 -12
  58. package/lib-module/Modal/Modal.js +19 -19
  59. package/lib-module/Radio/Radio.js +24 -13
  60. package/lib-module/Radio/RadioGroup.js +13 -4
  61. package/lib-module/RadioCard/RadioCard.js +2 -2
  62. package/lib-module/RadioCard/RadioCardGroup.js +12 -3
  63. package/lib-module/Search/Search.js +29 -21
  64. package/lib-module/Select/Select.js +2 -3
  65. package/lib-module/Tags/Tags.js +18 -11
  66. package/lib-module/TextInput/TextArea.js +3 -3
  67. package/lib-module/TextInput/TextInput.js +11 -3
  68. package/lib-module/TextInput/TextInputBase.js +2 -2
  69. package/lib-module/TextInput/propTypes.js +7 -1
  70. package/lib-module/ToggleSwitch/ToggleSwitch.js +6 -3
  71. package/lib-module/ToggleSwitch/ToggleSwitchGroup.js +15 -6
  72. package/lib-module/Typography/Typography.js +13 -11
  73. package/lib-module/index.js +1 -1
  74. package/lib-module/utils/containUniqueFields.js +26 -0
  75. package/lib-module/utils/index.js +2 -1
  76. package/lib-module/utils/input.js +6 -6
  77. package/lib-module/utils/props/handlerProps.js +59 -0
  78. package/lib-module/utils/props/index.js +3 -0
  79. package/lib-module/utils/props/inputSupportsProps.js +3 -5
  80. package/lib-module/utils/props/linkProps.js +3 -7
  81. package/lib-module/utils/props/textInputProps.js +193 -0
  82. package/lib-module/utils/props/textProps.js +59 -0
  83. package/package.json +5 -5
  84. package/src/Button/ButtonBase.jsx +8 -2
  85. package/src/Button/ButtonGroup.jsx +51 -34
  86. package/src/Card/PressableCardBase.jsx +6 -1
  87. package/src/Checkbox/Checkbox.jsx +35 -23
  88. package/src/Checkbox/CheckboxGroup.jsx +52 -22
  89. package/src/ExpandCollapse/Panel.jsx +9 -9
  90. package/src/FlexGrid/Col/Col.jsx +11 -2
  91. package/src/FlexGrid/Row/Row.jsx +8 -2
  92. package/src/InputLabel/InputLabel.jsx +36 -27
  93. package/src/Link/LinkBase.jsx +20 -4
  94. package/src/Link/TextButton.jsx +1 -19
  95. package/src/List/ListItem.jsx +17 -9
  96. package/src/Modal/Modal.jsx +30 -26
  97. package/src/Radio/Radio.jsx +26 -14
  98. package/src/Radio/RadioGroup.jsx +39 -21
  99. package/src/RadioCard/RadioCard.jsx +6 -1
  100. package/src/RadioCard/RadioCardGroup.jsx +17 -1
  101. package/src/Search/Search.jsx +33 -21
  102. package/src/Select/Select.jsx +2 -2
  103. package/src/Tags/Tags.jsx +23 -9
  104. package/src/TextInput/TextArea.jsx +7 -1
  105. package/src/TextInput/TextInput.jsx +15 -3
  106. package/src/TextInput/TextInputBase.jsx +8 -1
  107. package/src/TextInput/propTypes.js +7 -1
  108. package/src/ToggleSwitch/ToggleSwitch.jsx +11 -2
  109. package/src/ToggleSwitch/ToggleSwitchGroup.jsx +19 -6
  110. package/src/Typography/Typography.jsx +13 -9
  111. package/src/index.js +4 -1
  112. package/src/utils/containUniqueFields.js +32 -0
  113. package/src/utils/index.js +1 -0
  114. package/src/utils/input.js +5 -7
  115. package/src/utils/props/handlerProps.js +47 -0
  116. package/src/utils/props/index.js +3 -0
  117. package/src/utils/props/inputSupportsProps.js +3 -4
  118. package/src/utils/props/linkProps.js +3 -6
  119. package/src/utils/props/textInputProps.js +177 -0
  120. package/src/utils/props/textProps.js +58 -0
  121. package/stories/InputLabel/InputLabel.stories.jsx +25 -28
  122. package/stories/Modal/Modal.stories.jsx +25 -0
  123. package/stories/Search/Search.stories.jsx +53 -3
  124. package/stories/TextInput/TextInput.stories.jsx +40 -2
  125. package/__tests__/Link/LinkBase.test.jsx +0 -22
@@ -1,10 +1,13 @@
1
1
  import React, { forwardRef } from 'react'
2
-
2
+ import { Platform } from 'react-native'
3
3
  import {
4
4
  a11yProps,
5
+ focusHandlerProps,
5
6
  getTokensPropType,
6
7
  inputSupportsProps,
7
8
  selectSystemProps,
9
+ textInputHandlerProps,
10
+ textInputProps,
8
11
  variantProp,
9
12
  viewProps
10
13
  } from '../utils'
@@ -14,7 +17,10 @@ import textInputPropTypes from './propTypes'
14
17
 
15
18
  const [selectProps, selectedSystemPropTypes] = selectSystemProps([
16
19
  a11yProps,
20
+ focusHandlerProps,
17
21
  inputSupportsProps,
22
+ textInputHandlerProps,
23
+ textInputProps,
18
24
  viewProps
19
25
  ])
20
26
 
@@ -36,8 +42,14 @@ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
36
42
  * supported props and <a href="https://reactnative.dev/docs/textinput" target="_blank">React Native Web documentation</a> for
37
43
  * their implementation on the web.
38
44
  */
39
- const TextInput = forwardRef(({ tokens, variant = {}, ...rest }, ref) => {
40
- const { props: supportsProps, ...selectedProps } = selectProps(rest)
45
+ const TextInput = forwardRef(({ tokens, variant = {}, pattern, ...rest }, ref) => {
46
+ React.useEffect(() => {
47
+ if (Platform.OS === 'web' && pattern && ref.current) {
48
+ // eslint-disable-next-line no-param-reassign
49
+ ref.current.pattern = pattern
50
+ }
51
+ }, [ref, pattern])
52
+ const { supportsProps, ...selectedProps } = selectProps(rest)
41
53
 
42
54
  const inputProps = {
43
55
  ...selectedProps,
@@ -7,12 +7,19 @@ import {
7
7
  a11yProps,
8
8
  getTokensPropType,
9
9
  selectSystemProps,
10
+ textInputHandlerProps,
11
+ textInputProps,
10
12
  useInputValue,
11
13
  variantProp,
12
14
  viewProps
13
15
  } from '../utils'
14
16
 
15
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
17
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
18
+ a11yProps,
19
+ textInputHandlerProps,
20
+ textInputProps,
21
+ viewProps
22
+ ])
16
23
 
17
24
  const selectInputStyles = (
18
25
  {
@@ -1,4 +1,5 @@
1
1
  import PropTypes from 'prop-types'
2
+ import { Platform } from 'react-native'
2
3
 
3
4
  const textInputPropTypes = {
4
5
  /**
@@ -23,7 +24,12 @@ const textInputPropTypes = {
23
24
  * Use to react upon input's value changes. Required when the `value` prop is set.
24
25
  * Will receive the input's value as an argument.
25
26
  */
26
- onChange: PropTypes.func
27
+ onChange: PropTypes.func,
28
+ ...Platform.select({
29
+ web: {
30
+ pattern: PropTypes.string
31
+ }
32
+ })
27
33
  }
28
34
 
29
35
  export default textInputPropTypes
@@ -8,6 +8,7 @@ import StackView from '../StackView'
8
8
  import { useThemeTokensCallback, applyShadowToken } from '../ThemeProvider'
9
9
  import {
10
10
  a11yProps,
11
+ focusHandlerProps,
11
12
  getTokensPropType,
12
13
  selectTokens,
13
14
  pressProps,
@@ -18,7 +19,12 @@ import {
18
19
  } from '../utils'
19
20
  import { useInputValue } from '../utils/input'
20
21
 
21
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, pressProps, viewProps])
22
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([
23
+ a11yProps,
24
+ focusHandlerProps,
25
+ pressProps,
26
+ viewProps
27
+ ])
22
28
 
23
29
  const selectButtonTokens = (tokens) =>
24
30
  selectTokens('Button', {
@@ -111,7 +117,7 @@ const ToggleSwitch = forwardRef(
111
117
  return (
112
118
  <StackView space={2} direction="row">
113
119
  {Boolean(label) && (
114
- <View style={selectLabelStyles(themeTokens)}>
120
+ <View style={[selectLabelStyles(themeTokens), staticStyles.containText]}>
115
121
  <InputLabel
116
122
  forId={inputId}
117
123
  label={label}
@@ -210,6 +216,9 @@ const staticStyles = StyleSheet.create({
210
216
  switch: {
211
217
  alignItems: 'center',
212
218
  justifyContent: 'center'
219
+ },
220
+ containText: {
221
+ flexShrink: 1
213
222
  }
214
223
  })
215
224
 
@@ -10,16 +10,21 @@ import { useViewport } from '../ViewportProvider'
10
10
  import { useThemeTokens } from '../ThemeProvider'
11
11
  import {
12
12
  a11yProps,
13
+ containUniqueFields,
14
+ focusHandlerProps,
13
15
  getTokensPropType,
14
- pressProps,
15
16
  selectSystemProps,
17
+ useMultipleInputValues,
16
18
  variantProp,
17
19
  viewProps
18
- } from '../utils/props'
19
- import { useMultipleInputValues } from '../utils/input'
20
-
21
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, pressProps, viewProps])
20
+ } from '../utils'
22
21
 
22
+ const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
23
+ const [selectItemProps, selectedItemPropTypes] = selectSystemProps([
24
+ a11yProps,
25
+ focusHandlerProps,
26
+ viewProps
27
+ ])
23
28
  const ToggleSwitchGroup = forwardRef(
24
29
  (
25
30
  {
@@ -66,6 +71,11 @@ const ToggleSwitchGroup = forwardRef(
66
71
  })
67
72
  const itemA11yRole = selectedProps.accessibilityRole === 'radiogroup' ? 'radio' : 'switch'
68
73
 
74
+ const uniqueFields = ['id', 'label']
75
+ if (!containUniqueFields(items, uniqueFields)) {
76
+ throw new Error(`ToggleSwitchGroup items must have unique ${uniqueFields.join(', ')}`)
77
+ }
78
+
69
79
  const toggleSwitches = items.map(
70
80
  (
71
81
  {
@@ -74,7 +84,8 @@ const ToggleSwitchGroup = forwardRef(
74
84
  accessibilityLabel = label,
75
85
  onChange: itemOnChange,
76
86
  ref: itemRef,
77
- tooltip: itemTooltip
87
+ tooltip: itemTooltip,
88
+ ...itemRest
78
89
  },
79
90
  index
80
91
  ) => {
@@ -104,6 +115,7 @@ const ToggleSwitchGroup = forwardRef(
104
115
  label={label}
105
116
  tooltip={itemTooltip}
106
117
  {...itemA11y}
118
+ {...selectItemProps(itemRest)}
107
119
  />
108
120
  )
109
121
  }
@@ -143,6 +155,7 @@ ToggleSwitchGroup.propTypes = {
143
155
  */
144
156
  items: PropTypes.arrayOf(
145
157
  PropTypes.shape({
158
+ ...selectedItemPropTypes,
146
159
  /**
147
160
  * The text displayed to the user on the label.
148
161
  */
@@ -13,6 +13,7 @@ import {
13
13
  headingTags,
14
14
  selectSystemProps,
15
15
  textTags,
16
+ textProps,
16
17
  viewProps,
17
18
  getA11yPropsFromHtmlTag
18
19
  } from '../utils'
@@ -20,7 +21,8 @@ import {
20
21
  * @typedef {import('../utils/a11y/semantics').TextTag} TextTag
21
22
  */
22
23
 
23
- const [selectProps, selectedSystemPropTypes] = selectSystemProps([a11yProps, viewProps])
24
+ const [selectContainerProps, selectedContainerPropTypes] = selectSystemProps([a11yProps, viewProps])
25
+ const [selectTextProps, selectedTextPropTypes] = selectSystemProps([textProps])
24
26
 
25
27
  const selectTextStyles = ({
26
28
  fontWeight,
@@ -62,23 +64,24 @@ const Typography = forwardRef(
62
64
  ) => {
63
65
  const viewport = useViewport()
64
66
  const themeTokens = useThemeTokens('Typography', tokens, variant, { viewport })
65
- const textProps = {
67
+ const resolvedTextProps = {
68
+ ...selectTextProps(rest),
66
69
  style: selectTextStyles(align ? { ...themeTokens, textAlign: align } : themeTokens),
67
70
  dataSet,
68
71
  maxFontSizeMultiplier: getMaxFontMultiplier(themeTokens)
69
72
  }
70
73
 
71
- const selectedProps = selectProps({
74
+ const containerProps = {
72
75
  ...getA11yPropsFromHtmlTag(tag, accessibilityRole),
73
- ...rest
74
- })
76
+ ...selectContainerProps(rest)
77
+ }
75
78
 
76
79
  return block ? (
77
- <View ref={ref} {...selectedProps}>
78
- <Text {...textProps}>{children}</Text>
80
+ <View ref={ref} {...containerProps}>
81
+ <Text {...resolvedTextProps}>{children}</Text>
79
82
  </View>
80
83
  ) : (
81
- <Text ref={ref} {...textProps} {...selectedProps}>
84
+ <Text ref={ref} {...containerProps} {...resolvedTextProps}>
82
85
  {children}
83
86
  </Text>
84
87
  )
@@ -87,7 +90,8 @@ const Typography = forwardRef(
87
90
  Typography.displayName = 'Typography'
88
91
 
89
92
  Typography.propTypes = {
90
- ...selectedSystemPropTypes,
93
+ ...selectedContainerPropTypes,
94
+ ...selectedTextPropTypes,
91
95
  tokens: getTokensPropType('Typography'),
92
96
  variant: variantProp.propType,
93
97
  /**
package/src/index.js CHANGED
@@ -50,7 +50,10 @@ export {
50
50
  useTheme,
51
51
  useSetTheme,
52
52
  useThemeTokens,
53
- getThemeTokens
53
+ getThemeTokens,
54
+ applyOuterBorder,
55
+ applyTextStyles,
56
+ applyShadowToken
54
57
  } from './ThemeProvider'
55
58
 
56
59
  export * from './utils'
@@ -0,0 +1,32 @@
1
+ // Returns true if there are no duplicate values of the fields listed
2
+ // in the `fields` array across the objects in the `items` array, false
3
+ // otherwise.
4
+ // Note that if a value of a field in an item is not set, it will be
5
+ // excluded from comparison.
6
+ const containUniqueFields = (items, fields) => {
7
+ const map = []
8
+
9
+ const itemsHaveDuplicateFields = items.some((item) =>
10
+ fields.some((field) => {
11
+ if (!map[field]) {
12
+ map[field] = []
13
+ }
14
+
15
+ if (!item[field]) {
16
+ // We exclude empty values from comparison
17
+ return false
18
+ }
19
+
20
+ // Duplicate found!
21
+ if (map[field][item[field]]) return true
22
+
23
+ map[field][item[field]] = true
24
+
25
+ return false
26
+ })
27
+ )
28
+
29
+ return !itemsHaveDuplicateFields
30
+ }
31
+
32
+ export default containUniqueFields
@@ -14,3 +14,4 @@ export * from './useResponsiveProp'
14
14
  export { default as useUniqueId } from './useUniqueId'
15
15
  export { default as withLinkRouter } from './withLinkRouter'
16
16
  export * from './ssr'
17
+ export { default as containUniqueFields } from './containUniqueFields'
@@ -1,4 +1,7 @@
1
1
  import { useCallback, useRef, useState } from 'react'
2
+ /**
3
+ * @typedef {import('react').SyntheticEvent} Event
4
+ */
2
5
 
3
6
  const pluralHooks = ['useMultipleInputValues']
4
7
 
@@ -51,16 +54,13 @@ Consumers of this hook must be one of:
51
54
  *
52
55
  * @param {string} hookName - optional, used for tailoring error messages
53
56
  *
54
- * @typedef {(oldValue: string|number|null) => string|number|null} UpdaterFunction - `setValue` takes a value or
55
- * a function returning a new value from the old value
56
57
  * @returns {{
57
58
  * currentValue: string|number|null
58
- * setValue: (newValue: string|number|null|UpdaterFunction) => void
59
+ * setValue: (newValue: string|number|null|(oldValue: string|number) => string|number, event: Event) => void
59
60
  * resetValue: () => void
60
61
  * isControlled: bool
61
62
  * }}
62
63
  */
63
-
64
64
  export const useInputValue = (props = {}, hookName = 'useInputValue') => {
65
65
  const isCurrentlyControlled = props.value !== undefined
66
66
  const [isControlled] = useState(isCurrentlyControlled)
@@ -108,12 +108,10 @@ export const useInputValue = (props = {}, hookName = 'useInputValue') => {
108
108
  *
109
109
  * @param {string} componentName - optional, used in error messages
110
110
  *
111
- * @typedef {(oldValues: string[]|number[]) => string[]|number[]} UpdaterFunction - `setValues` takes values or
112
- * a function returning new values from old values
113
111
  * @returns {{
114
112
  * currentValues: any
115
113
  * resetValues: () => void
116
- * setValues: (newValues: string[]|number[]|UpdaterFunction) => void
114
+ * setValues: (newValues: string[]|number[]|(oldValues: string[]|number[]) => string[]|number[], event: Event) => void
117
115
  * toggleOneValue: (value: string|number) => void
118
116
  * unsetValues: () => void
119
117
  * }}
@@ -0,0 +1,47 @@
1
+ import PropTypes from 'prop-types'
2
+
3
+ export const focusHandlerProps = {
4
+ types: {
5
+ /**
6
+ * onBlur handler
7
+ */
8
+ onBlur: PropTypes.func,
9
+ /**
10
+ * onFocus handler
11
+ */
12
+ onFocus: PropTypes.func
13
+ },
14
+ select: ({ onBlur, onFocus }) => ({
15
+ onBlur,
16
+ onFocus
17
+ })
18
+ }
19
+
20
+ export const textInputHandlerProps = {
21
+ types: {
22
+ /**
23
+ * onChange handler
24
+ */
25
+ onChange: PropTypes.func,
26
+ /**
27
+ * onChangeText handler
28
+ */
29
+ onChangeText: PropTypes.func,
30
+ /**
31
+ * onSubmit handler
32
+ */
33
+ onSubmit: PropTypes.func,
34
+ /**
35
+ * onSubmitEditing handler
36
+ */
37
+ onSubmitEditing: PropTypes.func
38
+ },
39
+ select: ({ onChange, onChangeText, onSubmit, onSubmitEditing }) => ({
40
+ onChange,
41
+ onChangeText,
42
+ onSubmit,
43
+ onSubmitEditing
44
+ })
45
+ }
46
+
47
+ export default { focusHandlerProps, textInputHandlerProps }
@@ -1,4 +1,5 @@
1
1
  export * from './tokens'
2
+ export * from './handlerProps'
2
3
  export { default as a11yProps } from './a11yProps'
3
4
  export { default as clickProps } from './clickProps'
4
5
  export { default as copyPropTypes } from './copyPropTypes'
@@ -12,5 +13,7 @@ export { default as rectProp } from './rectProp'
12
13
  export { default as responsiveProps } from './responsiveProps'
13
14
  export { default as spacingProps } from './spacingProps'
14
15
  export { default as selectSystemProps } from './selectSystemProps'
16
+ export { default as textInputProps } from './textInputProps'
17
+ export { default as textProps } from './textProps'
15
18
  export { default as variantProp } from './variantProp'
16
19
  export { default as viewProps } from './viewProps'
@@ -28,15 +28,14 @@ export default {
28
28
  */
29
29
  validation: PropTypes.oneOf(['error', 'success'])
30
30
  },
31
- select: ({ label, hint, hintPosition, feedback, tooltip, validation, ...rest }) => ({
32
- props: {
31
+ select: ({ label, hint, hintPosition, feedback, tooltip, validation }) => ({
32
+ supportsProps: {
33
33
  label,
34
34
  hint,
35
35
  hintPosition,
36
36
  feedback,
37
37
  tooltip,
38
38
  validation
39
- },
40
- ...rest
39
+ }
41
40
  })
42
41
  }
@@ -22,16 +22,13 @@ export default {
22
22
  */
23
23
  select: getPropSelector(linkPropTypes),
24
24
  /**
25
- * Turn hrefs into press handlers on Native and throw if not given `onPress` or `href`.
25
+ * Turn hrefs into press handlers that open links on Native.
26
26
  *
27
27
  * @param {({ onPress?: () => void, href?: string })}
28
- * @returns {(() => void)|undefined} Returns a press handler, or undefined on web if href
29
- * string is provided. Expects calling component to use href string on web (e.g. in `<a>`).
28
+ * @returns {(() => void)|undefined} Returns a press handler, or undefined on web if no press
29
+ * handler is provided (expects calling component to render as `<a href={href}>` on web).
30
30
  */
31
31
  handleHref: ({ onPress, href }) => {
32
- if (!href && !onPress) {
33
- throw new Error('handleHref requires either href or onPress')
34
- }
35
32
  return Platform.select({
36
33
  web: onPress,
37
34
  default: (...args) => {
@@ -0,0 +1,177 @@
1
+ import PropTypes from 'prop-types'
2
+ import { Platform } from 'react-native'
3
+ import getPropSelector from './getPropSelector'
4
+
5
+ // This file contains common props for components that render a React Native TextInput
6
+ // It excludes interaction handler functions which are in `./handlerProps.js`
7
+
8
+ /**
9
+ * TextInput (web and native) supports some common React Native props
10
+ * shared with React Native's Text component.
11
+ */
12
+ const textProps = {
13
+ maxFontSizeMultiplier: PropTypes.number,
14
+ nativeId: PropTypes.string,
15
+ onLayout: PropTypes.func
16
+ }
17
+
18
+ /**
19
+ * UDS text inputs accept props related to UDS's useInputValue hook
20
+ */
21
+ const inputValueProps = {
22
+ value: PropTypes.string,
23
+ initialValue: PropTypes.string,
24
+ readOnly: PropTypes.bool
25
+ }
26
+
27
+ /**
28
+ * This collection adds props that can be passed through to both React Native's
29
+ * and React Native Web's implementations of the React Native TextInput component.
30
+ */
31
+ const crossPlatform = {
32
+ ...textProps,
33
+ ...inputValueProps,
34
+ /**
35
+ * Web and Android; 'off' disables device autocomplete, other strings are platform-specific.
36
+ * Valid values on Native: https://reactnative.dev/docs/textinput#autocomplete-android
37
+ * Valid values on Web: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
38
+ */
39
+ autoComplete: PropTypes.string,
40
+ /**
41
+ * On Native, default is `true`, passing `false` disables autoCorrect.
42
+ * On web, only supported by Safari and expects "on" or "off" strings.
43
+ */
44
+ autoCorrect: PropTypes.oneOf([true, false, 'on', 'off']),
45
+ /**
46
+ * Focuses the element on mount. On Web, only the first form element with autoFocus is focussed.
47
+ */
48
+ autoFocus: PropTypes.bool,
49
+ /**
50
+ * Default is `true` for single line, `false` for multi-line
51
+ */
52
+ blurOnSubmit: PropTypes.bool,
53
+ /**
54
+ * iOS and Web only, no effect on Android
55
+ */
56
+ clearTextOnFocus: PropTypes.bool,
57
+ /**
58
+ * Default is `true`. On web, this works by mapping to input's `readonly` attribute
59
+ */
60
+ editable: PropTypes.bool,
61
+ /**
62
+ * See documentation for allowed values (varies between Web, Android and iOS) and important notes:
63
+ * - Native: https://reactnative.dev/docs/textinput#keyboardtype
64
+ * - Web: equivalent to `inputmode` but see https://necolas.github.io/react-native-web/docs/text-input/
65
+ */
66
+ keyboardType: PropTypes.string,
67
+ /**
68
+ * Uses native tools (no flicker) to cap the maximum number of characters.
69
+ * On Web, works via `maxlength` attr.
70
+ */
71
+ maxLength: PropTypes.number,
72
+ /**
73
+ * If passed as true, the text input can be multiple lines.
74
+ *
75
+ * > It is important to note that this aligns the text to the top on iOS, and centers it on Android.
76
+ * > Use with textAlignVertical set to top for the same behavior in both platforms.
77
+ */
78
+ multiline: PropTypes.bool,
79
+ /**
80
+ * Web and Android only, requires `multiline` to be `true`.
81
+ */
82
+ numberOfLines: PropTypes.number,
83
+ /**
84
+ * Text to display when no value set.
85
+ * Accesibility guidelines recommend using labels to describe the input and using
86
+ * placeholders rarely and sparingly to prompt a particular format.
87
+ */
88
+ placeholder: PropTypes.string,
89
+ /**
90
+ * Sets placeholder colour. On Web, uses `::placeholder { color: ... }` selector.
91
+ */
92
+ placeholderTextColor: PropTypes.string,
93
+ /**
94
+ * One of a subset of platform-specific options that controls labelling and presentation
95
+ * in on-screen keyboards and accessibility tools. Uses `enterkeyhint` attr on Web.
96
+ *
97
+ * 'done', 'go', 'next', 'search', and 'send' are available for Web, Android and iOS.
98
+ */
99
+ returnKeyType: PropTypes.string,
100
+ /**
101
+ * Obscures passwords and similar. Equivalent to type="password" on Web.
102
+ * Does not work if multiline is true.
103
+ */
104
+ secureTextEntry: PropTypes.bool,
105
+ /**
106
+ * If true, all text will automatically be selected on focus.
107
+ */
108
+ selectTextOnFocus: PropTypes.bool,
109
+ /**
110
+ * Web and iOS. On iOS, default inherits from `autoCorrect`.
111
+ * On Web, equivalent to `spellcheck` attr.
112
+ */
113
+ spellCheck: PropTypes.bool
114
+ }
115
+
116
+ /**
117
+ * These web-only props all control HTML `input` attributes of the same name.
118
+ * Refer to general HTML documentation for more details.
119
+ */
120
+ const webOnly = {
121
+ disabled: PropTypes.bool,
122
+ dir: PropTypes.oneOf(['auto', 'ltr', 'rtl']),
123
+ lang: PropTypes.string
124
+ }
125
+
126
+ /**
127
+ * These props are supported in React Native but not React Native Web.
128
+ *
129
+ * React Native text inputs can be quirky, so a full set of props should be allowed to handle edge cases.
130
+ * Refer to React Native documentation for details, allowed values, and Android/iOS support and versions:
131
+ * https://reactnative.dev/docs/textinput
132
+ *
133
+ * Beware that many React Native text input props apply via complicated logic that chooses a built-in
134
+ * native component based on the values of multiple boolean flags, and may sometimes appear to pick an
135
+ * option that is inappropriate for one flag based on the values of one or more other other flags.
136
+ */
137
+ const nativeOnly = {
138
+ caretHidden: PropTypes.bool,
139
+ clearButtonMode: PropTypes.string,
140
+ contextMenuHidden: PropTypes.bool,
141
+ dataDetectorTypes: PropTypes.string,
142
+ disableFullscreenUI: PropTypes.bool,
143
+ enablesReturnKeyAutomatically: PropTypes.bool,
144
+ importantForAutofill: PropTypes.string,
145
+ inlineImageLeft: PropTypes.string,
146
+ keyboardAppearance: PropTypes.string,
147
+ returnKeyLabel: PropTypes.string,
148
+ rejectResponderTermination: PropTypes.bool,
149
+ scrollEnabled: PropTypes.bool,
150
+ selection: PropTypes.object,
151
+ selectionColor: PropTypes.string,
152
+ showSoftInputOnFocus: PropTypes.bool,
153
+ textAlign: PropTypes.string,
154
+ textContentType: PropTypes.string,
155
+ passwordRules: PropTypes.string,
156
+ textBreakStrategy: PropTypes.string,
157
+ underlineColorAndroid: PropTypes.string
158
+ }
159
+
160
+ export default {
161
+ /**
162
+ * Subset of proptypes that can be passed down to a React Native or React Native Web
163
+ * `TextInput` component. Allow regardless of platform, so cross-platform apps don't warn.
164
+ */
165
+ types: { ...crossPlatform, ...webOnly, ...nativeOnly },
166
+ /**
167
+ * Filters a props object. Return only platform-appropriate TextInput props, native inputs
168
+ * may throw errors on receiving unknown props.
169
+ */
170
+ select: getPropSelector({
171
+ ...crossPlatform,
172
+ ...Platform.select({
173
+ web: webOnly,
174
+ default: nativeOnly
175
+ })
176
+ })
177
+ }
@@ -0,0 +1,58 @@
1
+ import PropTypes from 'prop-types'
2
+ import { Platform } from 'react-native'
3
+ import getPropSelector from './getPropSelector'
4
+
5
+ // These are the props accepted specifically on React Native (Web) `Text` elements.
6
+ // They are generally concerned with the behaviour of multiline text.
7
+
8
+ const crossPlatform = {
9
+ /**
10
+ * Truncates text after this many lines with an ellipsis at the end.
11
+ * On native, ellipsis behaviour may be changed with `ellipsizeMode` prop.
12
+ */
13
+ numberOfLines: PropTypes.number,
14
+ /**
15
+ * Default is true on web and false on native
16
+ */
17
+ selectable: PropTypes.bool
18
+ }
19
+
20
+ /**
21
+ * See React Native docs for latest details on these.
22
+ * https://reactnative.dev/docs/text
23
+ */
24
+ const nativeOnly = {
25
+ ellipsizeMode: PropTypes.string,
26
+ maxFontSizeMultiplier: PropTypes.number,
27
+ minimumFontScale: PropTypes.number,
28
+ onTextLayout: PropTypes.func,
29
+ suppressHighlighting: PropTypes.bool,
30
+ textBreakStrategy: PropTypes.string
31
+ }
32
+
33
+ /**
34
+ * These set HTML attributes of the same name.
35
+ */
36
+ const webOnly = {
37
+ dir: PropTypes.oneOf(['auto', 'ltr', 'rtl']),
38
+ lang: PropTypes.string
39
+ }
40
+
41
+ export default {
42
+ /**
43
+ * Set of prop types specific to React Native and React Native Web `Text`,
44
+ * which largely revolve around the behaviour of multiline non-pressable text.
45
+ */
46
+ types: { ...crossPlatform, ...webOnly, ...nativeOnly },
47
+ /**
48
+ * Filters a props object, returning only props specific to `Text` elements
49
+ * on the current platform. Does not include props applicable to `Text` and `View`.
50
+ */
51
+ select: getPropSelector({
52
+ ...crossPlatform,
53
+ ...Platform.select({
54
+ web: webOnly,
55
+ default: nativeOnly
56
+ })
57
+ })
58
+ }