@utilitywarehouse/hearth-react-native 0.32.0 → 0.32.1
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +12 -15
- package/CHANGELOG.md +8 -1
- package/build/components/Card/Card.props.d.ts +1 -1
- package/build/components/Card/CardRoot.js +19 -0
- package/build/components/Input/Input.js +13 -31
- package/build/components/StepperInput/StepperInput.js +12 -29
- package/build/components/Textarea/Textarea.js +12 -30
- package/build/components/VerificationInput/VerificationInput.js +12 -22
- package/build/hooks/index.d.ts +1 -0
- package/build/hooks/index.js +1 -0
- package/build/hooks/useFormFieldAccessibility.d.ts +17 -0
- package/build/hooks/useFormFieldAccessibility.js +32 -0
- package/build/hooks/useFormFieldAccessibility.test.d.ts +1 -0
- package/build/hooks/useFormFieldAccessibility.test.js +56 -0
- package/package.json +4 -2
- package/src/components/Banner/Banner.stories.tsx +14 -0
- package/src/components/Card/Card.docs.mdx +16 -17
- package/src/components/Card/Card.props.ts +1 -0
- package/src/components/Card/Card.stories.tsx +35 -21
- package/src/components/Card/CardRoot.tsx +19 -0
- package/src/components/Icon/Icon.docs.mdx +1 -1
- package/src/components/Input/Input.tsx +14 -35
- package/src/components/List/List.docs.mdx +4 -2
- package/src/components/StepperInput/StepperInput.tsx +13 -40
- package/src/components/Textarea/Textarea.tsx +13 -34
- package/src/components/VerificationInput/VerificationInput.tsx +13 -25
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useFormFieldAccessibility.test.tsx +74 -0
- package/src/hooks/useFormFieldAccessibility.ts +67 -0
|
@@ -61,23 +61,22 @@ const MyComponent = () => (
|
|
|
61
61
|
|
|
62
62
|
## Props
|
|
63
63
|
|
|
64
|
-
| Property | Type
|
|
65
|
-
| -------------- |
|
|
66
|
-
| variant | `'subtle' \| 'emphasis' `
|
|
67
|
-
| colorScheme | `'neutralStrong' \| 'neutralSubtle' \| '
|
|
68
|
-
| shadowColor | `'functional' \| 'brand' \| 'energy' \| 'broadband' \| 'mobile' `<br /> `'insurance' \| 'cashback' \| 'pig'`
|
|
69
|
-
| noPadding | `boolean`
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| columnGap | `number` | The column gap between the content. | `0` |
|
|
64
|
+
| Property | Type | Description | Default |
|
|
65
|
+
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | ----------------- |
|
|
66
|
+
| variant | `'subtle' \| 'emphasis' ` | The variant of the card. | `'subtle'` |
|
|
67
|
+
| colorScheme | `'neutralStrong' \| 'neutralSubtle' \| 'brand' \| 'energy'` <br /> `'broadband' \| 'mobile' \| 'insurance' \| 'cashback' \|` <br /> `'highlight' \| 'pig'` | The color scheme of the card. | `'neutralStrong'` |
|
|
68
|
+
| shadowColor | `'functional' \| 'brand' \| 'energy' \| 'broadband' \| 'mobile' `<br /> `'insurance' \| 'cashback' \| 'pig'` | The shadow color of the card. | `-` |
|
|
69
|
+
| noPadding | `boolean` | Whether or not the card has padding. | `false` |
|
|
70
|
+
| onPress | `() => void` | Callback function to be called. | `-` |
|
|
71
|
+
| disabled | `boolean` | Whether the card is disabled. | `false` |
|
|
72
|
+
| spacing | `'none' \| '2xs' \| 'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl'` | The space between the content. | `none` |
|
|
73
|
+
| alignItems | `'flex-start' \| 'flex-end' \| `<br />`'center' \| 'stretch' \| 'baseline'` | The align items of the flex container. | `flex-start` |
|
|
74
|
+
| justifyContent | `'flex-start' \| 'flex-end' \| 'center' \| 'space-between' \| `<br />` 'space-around' \| 'space-evenly'` | The justify content of the flex container. | `flex-start` |
|
|
75
|
+
| flexWrap | `'wrap' \| 'nowrap' \| 'wrap-reverse'` | The wrap of the flex container. | `nowrap` |
|
|
76
|
+
| flexDirection | `'row' \| 'column' \| 'row-reverse' \| 'column-reverse'` | The direction of the flex container. | `column` |
|
|
77
|
+
| gap | `number` | The gap between the content. | `0` |
|
|
78
|
+
| rowGap | `number` | The row gap between the content. | `0` |
|
|
79
|
+
| columnGap | `number` | The column gap between the content. | `0` |
|
|
81
80
|
|
|
82
81
|
### `CardPressHandler` Props
|
|
83
82
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Meta, StoryObj } from '@storybook/react-
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react-native';
|
|
2
2
|
import { BellMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
3
|
+
import { ComponentProps } from 'react';
|
|
3
4
|
import { Card, CardAction, CardActions, CardPressHandler } from '.';
|
|
4
5
|
import { VariantTitle } from '../../../docs/components';
|
|
5
6
|
import { BodyText } from '../BodyText';
|
|
@@ -7,6 +8,8 @@ import { Button } from '../Button';
|
|
|
7
8
|
import { Flex } from '../Flex';
|
|
8
9
|
import { Heading } from '../Heading';
|
|
9
10
|
|
|
11
|
+
type CardStoryProps = ComponentProps<typeof Card>;
|
|
12
|
+
|
|
10
13
|
const meta = {
|
|
11
14
|
title: 'Stories / Card',
|
|
12
15
|
component: Card,
|
|
@@ -31,6 +34,7 @@ const meta = {
|
|
|
31
34
|
'brand',
|
|
32
35
|
'energy',
|
|
33
36
|
'broadband',
|
|
37
|
+
'highlight',
|
|
34
38
|
'mobile',
|
|
35
39
|
'insurance',
|
|
36
40
|
'cashback',
|
|
@@ -65,7 +69,7 @@ export default meta;
|
|
|
65
69
|
type Story = StoryObj<typeof meta>;
|
|
66
70
|
|
|
67
71
|
export const Playground: Story = {
|
|
68
|
-
render: ({ children, ...props }) => {
|
|
72
|
+
render: ({ children, ...props }: CardStoryProps) => {
|
|
69
73
|
return (
|
|
70
74
|
<Card {...props}>
|
|
71
75
|
<BodyText>{children as string}</BodyText>
|
|
@@ -75,7 +79,7 @@ export const Playground: Story = {
|
|
|
75
79
|
};
|
|
76
80
|
|
|
77
81
|
export const WithActions: Story = {
|
|
78
|
-
render: ({ children, ...props }) => {
|
|
82
|
+
render: ({ children, ...props }: CardStoryProps) => {
|
|
79
83
|
return (
|
|
80
84
|
<Flex gap="400">
|
|
81
85
|
<Card
|
|
@@ -139,7 +143,7 @@ export const WithOnlyAction: Story = {
|
|
|
139
143
|
args: {
|
|
140
144
|
variant: 'emphasis',
|
|
141
145
|
},
|
|
142
|
-
render: ({ ...props }) => {
|
|
146
|
+
render: ({ ...props }: CardStoryProps) => {
|
|
143
147
|
return (
|
|
144
148
|
<Card {...props} flexDirection="column" alignItems="stretch" spacing="md">
|
|
145
149
|
<CardActions>
|
|
@@ -159,7 +163,7 @@ export const Variants: Story = {
|
|
|
159
163
|
parameters: {
|
|
160
164
|
controls: { exclude: ['variant', 'colorScheme'] },
|
|
161
165
|
},
|
|
162
|
-
render: ({ children, ...props }) => {
|
|
166
|
+
render: ({ children, ...props }: CardStoryProps) => {
|
|
163
167
|
return (
|
|
164
168
|
<Flex spacing="lg">
|
|
165
169
|
<VariantTitle title="Subtle - White">
|
|
@@ -182,76 +186,86 @@ export const Variants: Story = {
|
|
|
182
186
|
<BodyText>{children as string}</BodyText>
|
|
183
187
|
</Card>
|
|
184
188
|
</VariantTitle>
|
|
185
|
-
<VariantTitle title="
|
|
189
|
+
<VariantTitle title="Brand - Subtle">
|
|
186
190
|
<Card {...props} colorScheme="brand" variant="subtle">
|
|
187
191
|
<BodyText>{children as string}</BodyText>
|
|
188
192
|
</Card>
|
|
189
193
|
</VariantTitle>
|
|
190
|
-
<VariantTitle title="
|
|
194
|
+
<VariantTitle title="Brand - Emphasis">
|
|
191
195
|
<Card {...props} colorScheme="brand" variant="emphasis">
|
|
192
196
|
<BodyText inverted>{children as string}</BodyText>
|
|
193
197
|
</Card>
|
|
194
198
|
</VariantTitle>
|
|
195
|
-
<VariantTitle title="Energy
|
|
199
|
+
<VariantTitle title="Energy - Subtle">
|
|
196
200
|
<Card {...props} colorScheme="energy" variant="subtle">
|
|
197
201
|
<BodyText>{children as string}</BodyText>
|
|
198
202
|
</Card>
|
|
199
203
|
</VariantTitle>
|
|
200
|
-
<VariantTitle title="Energy
|
|
204
|
+
<VariantTitle title="Energy - Emphasis">
|
|
201
205
|
<Card {...props} colorScheme="energy" variant="emphasis">
|
|
202
206
|
<BodyText>{children as string}</BodyText>
|
|
203
207
|
</Card>
|
|
204
208
|
</VariantTitle>
|
|
205
|
-
<VariantTitle title="Broadband
|
|
209
|
+
<VariantTitle title="Broadband - Subtle">
|
|
206
210
|
<Card {...props} colorScheme="broadband" variant="subtle">
|
|
207
211
|
<BodyText>{children as string}</BodyText>
|
|
208
212
|
</Card>
|
|
209
213
|
</VariantTitle>
|
|
210
|
-
<VariantTitle title="Broadband
|
|
214
|
+
<VariantTitle title="Broadband - Emphasis">
|
|
211
215
|
<Card {...props} colorScheme="broadband" variant="emphasis">
|
|
212
216
|
<BodyText>{children as string}</BodyText>
|
|
213
217
|
</Card>
|
|
214
218
|
</VariantTitle>
|
|
215
|
-
<VariantTitle title="Mobile
|
|
219
|
+
<VariantTitle title="Mobile - Subtle">
|
|
216
220
|
<Card {...props} colorScheme="mobile" variant="subtle">
|
|
217
221
|
<BodyText>{children as string}</BodyText>
|
|
218
222
|
</Card>
|
|
219
223
|
</VariantTitle>
|
|
220
|
-
<VariantTitle title="Mobile
|
|
224
|
+
<VariantTitle title="Mobile - Emphasis">
|
|
221
225
|
<Card {...props} colorScheme="mobile" variant="emphasis">
|
|
222
226
|
<BodyText>{children as string}</BodyText>
|
|
223
227
|
</Card>
|
|
224
228
|
</VariantTitle>
|
|
225
|
-
<VariantTitle title="Insurance
|
|
229
|
+
<VariantTitle title="Insurance - Subtle">
|
|
226
230
|
<Card {...props} colorScheme="insurance" variant="subtle">
|
|
227
231
|
<BodyText>{children as string}</BodyText>
|
|
228
232
|
</Card>
|
|
229
233
|
</VariantTitle>
|
|
230
|
-
<VariantTitle title="Insurance
|
|
234
|
+
<VariantTitle title="Insurance - Emphasis">
|
|
231
235
|
<Card {...props} colorScheme="insurance" variant="emphasis">
|
|
232
236
|
<BodyText>{children as string}</BodyText>
|
|
233
237
|
</Card>
|
|
234
238
|
</VariantTitle>
|
|
235
|
-
<VariantTitle title="Cashback
|
|
239
|
+
<VariantTitle title="Cashback - Subtle">
|
|
236
240
|
<Card {...props} colorScheme="cashback" variant="subtle">
|
|
237
241
|
<BodyText>{children as string}</BodyText>
|
|
238
242
|
</Card>
|
|
239
243
|
</VariantTitle>
|
|
240
|
-
<VariantTitle title="Cashback
|
|
244
|
+
<VariantTitle title="Cashback - Emphasis">
|
|
241
245
|
<Card {...props} colorScheme="cashback" variant="emphasis">
|
|
242
246
|
<BodyText>{children as string}</BodyText>
|
|
243
247
|
</Card>
|
|
244
248
|
</VariantTitle>
|
|
245
|
-
<VariantTitle title="Piggy
|
|
249
|
+
<VariantTitle title="Piggy - Subtle">
|
|
246
250
|
<Card {...props} colorScheme="pig" variant="subtle">
|
|
247
251
|
<BodyText>{children as string}</BodyText>
|
|
248
252
|
</Card>
|
|
249
253
|
</VariantTitle>
|
|
250
|
-
<VariantTitle title="Piggy
|
|
254
|
+
<VariantTitle title="Piggy - Emphasis">
|
|
251
255
|
<Card {...props} colorScheme="pig" variant="emphasis">
|
|
252
256
|
<BodyText>{children as string}</BodyText>
|
|
253
257
|
</Card>
|
|
254
258
|
</VariantTitle>
|
|
259
|
+
<VariantTitle title="Highlight - Subtle">
|
|
260
|
+
<Card {...props} colorScheme="highlight" variant="subtle">
|
|
261
|
+
<BodyText>{children as string}</BodyText>
|
|
262
|
+
</Card>
|
|
263
|
+
</VariantTitle>
|
|
264
|
+
<VariantTitle title="Highlight - Emphasis">
|
|
265
|
+
<Card {...props} colorScheme="highlight" variant="emphasis">
|
|
266
|
+
<BodyText>{children as string}</BodyText>
|
|
267
|
+
</Card>
|
|
268
|
+
</VariantTitle>
|
|
255
269
|
</Flex>
|
|
256
270
|
);
|
|
257
271
|
},
|
|
@@ -264,7 +278,7 @@ export const WithShadow: Story = {
|
|
|
264
278
|
parameters: {
|
|
265
279
|
controls: { exclude: ['variant'] },
|
|
266
280
|
},
|
|
267
|
-
render: ({ children, ...props }) => {
|
|
281
|
+
render: ({ children, ...props }: CardStoryProps) => {
|
|
268
282
|
return (
|
|
269
283
|
<Flex spacing="lg">
|
|
270
284
|
<VariantTitle title="Subtle - White - Shadow">
|
|
@@ -286,7 +300,7 @@ export const Interactive: Story = {
|
|
|
286
300
|
parameters: {
|
|
287
301
|
controls: { exclude: ['variant', 'colorScheme'] },
|
|
288
302
|
},
|
|
289
|
-
render: ({ children, ...props }) => {
|
|
303
|
+
render: ({ children, ...props }: CardStoryProps) => {
|
|
290
304
|
return (
|
|
291
305
|
<Flex spacing="lg">
|
|
292
306
|
<VariantTitle title="Pressable - Subtle - White">
|
|
@@ -164,6 +164,9 @@ const styles = StyleSheet.create(theme => ({
|
|
|
164
164
|
pig: {
|
|
165
165
|
borderWidth: theme.components.card.brand.borderWidth,
|
|
166
166
|
},
|
|
167
|
+
highlight: {
|
|
168
|
+
borderWidth: theme.components.card.brand.borderWidth,
|
|
169
|
+
},
|
|
167
170
|
},
|
|
168
171
|
shadowColor: {
|
|
169
172
|
functional: {
|
|
@@ -331,6 +334,22 @@ const styles = StyleSheet.create(theme => ({
|
|
|
331
334
|
borderColor: theme.color.border.strong,
|
|
332
335
|
},
|
|
333
336
|
},
|
|
337
|
+
{
|
|
338
|
+
variant: 'subtle',
|
|
339
|
+
colorScheme: 'highlight',
|
|
340
|
+
styles: {
|
|
341
|
+
backgroundColor: theme.color.surface.highlight.subtle,
|
|
342
|
+
borderColor: theme.color.border.strong,
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
variant: 'emphasis',
|
|
347
|
+
colorScheme: 'highlight',
|
|
348
|
+
styles: {
|
|
349
|
+
backgroundColor: theme.color.surface.highlight.default,
|
|
350
|
+
borderColor: theme.color.border.strong,
|
|
351
|
+
},
|
|
352
|
+
},
|
|
334
353
|
],
|
|
335
354
|
},
|
|
336
355
|
}));
|
|
@@ -34,7 +34,7 @@ Icons are often used to enhance the usability and accessibility of digital produ
|
|
|
34
34
|
You can either use the React Native components directy from our `@utilitywarehouse/hearth-react-native-icons` package or use the `Icon`
|
|
35
35
|
component from our `@utilitywarehouse/hearth-react-native` package to render the icons with utility props such as `color`.
|
|
36
36
|
|
|
37
|
-
We
|
|
37
|
+
We recommend that you use the `Icon` component to ensure that the icons are styled correctly and consistently across your application.
|
|
38
38
|
The `Icon` component also provides additional functionality such as handling different color modes and sizes as well as automatically applying
|
|
39
39
|
light and dark mode colors from the theme.
|
|
40
40
|
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
EyeSmallIcon,
|
|
10
10
|
SearchMediumIcon,
|
|
11
11
|
} from '@utilitywarehouse/hearth-react-native-icons';
|
|
12
|
-
import { useTheme } from '../../hooks';
|
|
12
|
+
import { useFormFieldAccessibility, useTheme } from '../../hooks';
|
|
13
13
|
import { BodyText } from '../BodyText';
|
|
14
14
|
import { FormField, useFormFieldContext } from '../FormField';
|
|
15
15
|
import { Spinner } from '../Spinner';
|
|
@@ -75,7 +75,7 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
75
75
|
if (formFieldContext?.setShouldHandleAccessibility) {
|
|
76
76
|
formFieldContext.setShouldHandleAccessibility(true);
|
|
77
77
|
}
|
|
78
|
-
}, []);
|
|
78
|
+
}, [formFieldContext]);
|
|
79
79
|
|
|
80
80
|
const [fieldType, setFieldType] = useState<'password' | 'text'>(
|
|
81
81
|
type === 'password' ? 'password' : 'text'
|
|
@@ -88,6 +88,16 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
88
88
|
|
|
89
89
|
const shouldShowPasswordToggle = type === 'password' && showPasswordToggle;
|
|
90
90
|
const shouldShowClear = clearable && !!(props as InputWithoutChildrenProps)?.value;
|
|
91
|
+
const { accessibilityHint, accessibilityLabel } = useFormFieldAccessibility({
|
|
92
|
+
label: inputLabel,
|
|
93
|
+
helperText: inputHelperText,
|
|
94
|
+
validText: inputValidText,
|
|
95
|
+
invalidText: inputInvalidText,
|
|
96
|
+
required: inputRequired,
|
|
97
|
+
validationStatus: inputValidationStatus,
|
|
98
|
+
fallbackLabel: props.accessibilityLabel,
|
|
99
|
+
fallbackHint: props.accessibilityHint,
|
|
100
|
+
});
|
|
91
101
|
|
|
92
102
|
const toggleFieldType = () => {
|
|
93
103
|
setFieldType(fieldType === 'password' ? 'text' : 'password');
|
|
@@ -107,37 +117,6 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
107
117
|
return undefined;
|
|
108
118
|
})();
|
|
109
119
|
|
|
110
|
-
const getAccessibilityLabel = () => {
|
|
111
|
-
let accessibilityLabel = '';
|
|
112
|
-
if (inputLabel) {
|
|
113
|
-
accessibilityLabel = accessibilityLabel + inputLabel;
|
|
114
|
-
}
|
|
115
|
-
if (inputRequired) {
|
|
116
|
-
accessibilityLabel = accessibilityLabel + ', required';
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return accessibilityLabel || props.accessibilityLabel;
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
const getAccessibilityHint = () => {
|
|
123
|
-
let accessibilityHint = '';
|
|
124
|
-
if (inputHelperText) {
|
|
125
|
-
accessibilityHint = accessibilityHint + inputHelperText;
|
|
126
|
-
}
|
|
127
|
-
if (inputValidationStatus !== 'initial') {
|
|
128
|
-
if (accessibilityHint.length > 0) {
|
|
129
|
-
accessibilityHint = accessibilityHint + ', ';
|
|
130
|
-
}
|
|
131
|
-
if (inputValidationStatus === 'invalid' && inputInvalidText) {
|
|
132
|
-
accessibilityHint = accessibilityHint + inputInvalidText;
|
|
133
|
-
}
|
|
134
|
-
if (inputValidationStatus === 'valid' && inputValidText) {
|
|
135
|
-
accessibilityHint = accessibilityHint + inputValidText;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
return accessibilityHint || props.accessibilityHint;
|
|
139
|
-
};
|
|
140
|
-
|
|
141
120
|
return (
|
|
142
121
|
<FormField
|
|
143
122
|
label={label}
|
|
@@ -188,8 +167,8 @@ const Input = forwardRef<TextInput, InputProps>(
|
|
|
188
167
|
inputMode={getInputMode}
|
|
189
168
|
inBottomSheet={inBottomSheet}
|
|
190
169
|
{...props}
|
|
191
|
-
aria-label={
|
|
192
|
-
accessibilityHint={
|
|
170
|
+
aria-label={accessibilityLabel}
|
|
171
|
+
accessibilityHint={accessibilityHint}
|
|
193
172
|
/>
|
|
194
173
|
{shouldShowClear && (
|
|
195
174
|
<InputSlot>
|
|
@@ -32,6 +32,8 @@ import {
|
|
|
32
32
|
import { BackToTopButton, BadgeList, UsageWrap, ViewFigmaButton } from '../../../docs/components';
|
|
33
33
|
import * as Stories from './List.stories';
|
|
34
34
|
|
|
35
|
+
import StorybookLink from '../../../../../shared/storybook/StorybookLink';
|
|
36
|
+
|
|
35
37
|
<Meta title="Components / List" />
|
|
36
38
|
|
|
37
39
|
<ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components-%26-Tokens?node-id=1-163" />
|
|
@@ -146,11 +148,11 @@ are ignored, so conditional list items will not affect which item loses the top
|
|
|
146
148
|
|
|
147
149
|
#### - `ListItemHeading`
|
|
148
150
|
|
|
149
|
-
Has all props of the
|
|
151
|
+
Has all props of the <StorybookLink to="typography-body-text">`BodyText` component</StorybookLink>.
|
|
150
152
|
|
|
151
153
|
#### - `ListItemHelperText`
|
|
152
154
|
|
|
153
|
-
Has all props of the
|
|
155
|
+
Has all props of the <StorybookLink to="typography-body-text">`BodyText` component</StorybookLink>.
|
|
154
156
|
|
|
155
157
|
#### - `ListItemTrailingContent`
|
|
156
158
|
|
|
@@ -3,6 +3,7 @@ import { useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
|
3
3
|
import type { TextInput, TextInputFocusEvent } from 'react-native';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
5
|
import { StyleSheet } from 'react-native-unistyles';
|
|
6
|
+
import { useFormFieldAccessibility } from '../../hooks';
|
|
6
7
|
import { FormField } from '../FormField';
|
|
7
8
|
import { InputComponent, InputField } from '../Input/Input';
|
|
8
9
|
import StepperButton from './StepperButton';
|
|
@@ -143,6 +144,16 @@ const StepperInput = ({
|
|
|
143
144
|
const allowDecimal = decimalPrecision > 0;
|
|
144
145
|
const keyboardType = allowNegative || allowDecimal ? 'numeric' : 'number-pad';
|
|
145
146
|
const inputMode = allowDecimal ? 'decimal' : 'numeric';
|
|
147
|
+
const { accessibilityHint, accessibilityLabel } = useFormFieldAccessibility({
|
|
148
|
+
label,
|
|
149
|
+
helperText,
|
|
150
|
+
validText,
|
|
151
|
+
invalidText,
|
|
152
|
+
required,
|
|
153
|
+
validationStatus,
|
|
154
|
+
fallbackLabel: props.accessibilityLabel,
|
|
155
|
+
fallbackHint: props.accessibilityHint,
|
|
156
|
+
});
|
|
146
157
|
|
|
147
158
|
useImperativeHandle(ref, () => inputRef.current as TextInput, []);
|
|
148
159
|
|
|
@@ -216,44 +227,6 @@ const StepperInput = ({
|
|
|
216
227
|
onBlur?.(event);
|
|
217
228
|
};
|
|
218
229
|
|
|
219
|
-
const getAccessibilityLabel = () => {
|
|
220
|
-
let accessibilityLabel = '';
|
|
221
|
-
|
|
222
|
-
if (label) {
|
|
223
|
-
accessibilityLabel = accessibilityLabel + label;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
if (required) {
|
|
227
|
-
accessibilityLabel = accessibilityLabel + ', required';
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return accessibilityLabel || props.accessibilityLabel;
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const getAccessibilityHint = () => {
|
|
234
|
-
let accessibilityHint = '';
|
|
235
|
-
|
|
236
|
-
if (helperText) {
|
|
237
|
-
accessibilityHint = accessibilityHint + helperText;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (validationStatus !== 'initial') {
|
|
241
|
-
if (accessibilityHint.length > 0) {
|
|
242
|
-
accessibilityHint = accessibilityHint + ', ';
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (validationStatus === 'invalid' && invalidText) {
|
|
246
|
-
accessibilityHint = accessibilityHint + invalidText;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
if (validationStatus === 'valid' && validText) {
|
|
250
|
-
accessibilityHint = accessibilityHint + validText;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
return accessibilityHint || props.accessibilityHint;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
230
|
return (
|
|
258
231
|
<FormField
|
|
259
232
|
label={label}
|
|
@@ -297,8 +270,8 @@ const StepperInput = ({
|
|
|
297
270
|
onFocus={handleFocus}
|
|
298
271
|
onBlur={handleBlur}
|
|
299
272
|
onChangeText={handleChangeText}
|
|
300
|
-
accessibilityLabel={
|
|
301
|
-
accessibilityHint={
|
|
273
|
+
accessibilityLabel={accessibilityLabel}
|
|
274
|
+
accessibilityHint={accessibilityHint}
|
|
302
275
|
accessibilityState={{
|
|
303
276
|
...(props.accessibilityState ?? {}),
|
|
304
277
|
disabled: disabled || readonly,
|
|
@@ -13,7 +13,7 @@ import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
|
13
13
|
import { useAnimatedStyle, useSharedValue } from 'react-native-reanimated';
|
|
14
14
|
import { Path, Svg } from 'react-native-svg';
|
|
15
15
|
import { StyleSheet } from 'react-native-unistyles';
|
|
16
|
-
import { useTheme } from '../../hooks';
|
|
16
|
+
import { useFormFieldAccessibility, useTheme } from '../../hooks';
|
|
17
17
|
import { FormField, useFormFieldContext } from '../FormField';
|
|
18
18
|
import TextareaFieldComponent from './TextareaField';
|
|
19
19
|
import TextareaRoot from './TextareaRoot';
|
|
@@ -61,6 +61,16 @@ const Textarea = ({
|
|
|
61
61
|
const textareaHeight = useSharedValue(textareaDefaultHeight);
|
|
62
62
|
const resizeStartHeight = useSharedValue(textareaDefaultHeight);
|
|
63
63
|
const theme = useTheme();
|
|
64
|
+
const { accessibilityHint, accessibilityLabel } = useFormFieldAccessibility({
|
|
65
|
+
label: textareaLabel,
|
|
66
|
+
helperText: textareaHelperText,
|
|
67
|
+
validText: textareaValidText,
|
|
68
|
+
invalidText: textareaInvalidText,
|
|
69
|
+
required: textareaRequired,
|
|
70
|
+
validationStatus: textareaValidationStatus,
|
|
71
|
+
fallbackLabel: props.accessibilityLabel,
|
|
72
|
+
fallbackHint: props.accessibilityHint,
|
|
73
|
+
});
|
|
64
74
|
|
|
65
75
|
useEffect(() => {
|
|
66
76
|
if (formFieldContext?.setShouldHandleAccessibility) {
|
|
@@ -75,37 +85,6 @@ const Textarea = ({
|
|
|
75
85
|
}
|
|
76
86
|
}, [resizeStartHeight, textareaDefaultHeight, textareaHeight]);
|
|
77
87
|
|
|
78
|
-
const getAccessibilityLabel = () => {
|
|
79
|
-
let accessibilityLabel = '';
|
|
80
|
-
if (textareaLabel) {
|
|
81
|
-
accessibilityLabel = accessibilityLabel + textareaLabel;
|
|
82
|
-
}
|
|
83
|
-
if (textareaRequired) {
|
|
84
|
-
accessibilityLabel = accessibilityLabel + ', required';
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return accessibilityLabel || props.accessibilityLabel;
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
const getAccessibilityHint = () => {
|
|
91
|
-
let accessibilityHint = '';
|
|
92
|
-
if (textareaHelperText) {
|
|
93
|
-
accessibilityHint = accessibilityHint + textareaHelperText;
|
|
94
|
-
}
|
|
95
|
-
if (textareaValidationStatus !== 'initial') {
|
|
96
|
-
if (accessibilityHint.length > 0) {
|
|
97
|
-
accessibilityHint = accessibilityHint + ', ';
|
|
98
|
-
}
|
|
99
|
-
if (textareaValidationStatus === 'invalid' && textareaInvalidText) {
|
|
100
|
-
accessibilityHint = accessibilityHint + textareaInvalidText;
|
|
101
|
-
}
|
|
102
|
-
if (textareaValidationStatus === 'valid' && textareaValidText) {
|
|
103
|
-
accessibilityHint = accessibilityHint + textareaValidText;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return accessibilityHint || props.accessibilityHint;
|
|
107
|
-
};
|
|
108
|
-
|
|
109
88
|
const handleTextareaLayout = (event: LayoutChangeEvent) => {
|
|
110
89
|
if (!hasMeasuredHeight.current) {
|
|
111
90
|
textareaHeight.value = event.nativeEvent.layout.height;
|
|
@@ -171,8 +150,8 @@ const Textarea = ({
|
|
|
171
150
|
isDisabled={textareaDisabled}
|
|
172
151
|
isFocused={focused}
|
|
173
152
|
required={textareaRequired}
|
|
174
|
-
aria-label={
|
|
175
|
-
accessibilityHint={
|
|
153
|
+
aria-label={accessibilityLabel}
|
|
154
|
+
accessibilityHint={accessibilityHint}
|
|
176
155
|
>
|
|
177
156
|
{children ? (
|
|
178
157
|
<>{children}</>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react';
|
|
2
2
|
import { TextInput, View } from 'react-native';
|
|
3
3
|
import { StyleSheet } from 'react-native-unistyles';
|
|
4
|
+
import { useFormFieldAccessibility } from '../../hooks';
|
|
4
5
|
import { FormField } from '../FormField';
|
|
5
6
|
import type { VerificationInputHandle, VerificationInputProps } from './VerificationInput.props';
|
|
6
7
|
import { getNextIndexFromValueChange } from './VerificationInput.utils';
|
|
@@ -163,29 +164,16 @@ const VerificationInput = forwardRef<VerificationInputHandle, VerificationInputP
|
|
|
163
164
|
);
|
|
164
165
|
|
|
165
166
|
const slots = Array.from({ length }, (_, index) => index);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (validationStatus !== 'initial') {
|
|
177
|
-
if (accessibilityHint.length > 0) {
|
|
178
|
-
accessibilityHint = accessibilityHint + ', ';
|
|
179
|
-
}
|
|
180
|
-
if (validationStatus === 'invalid' && invalidText) {
|
|
181
|
-
accessibilityHint = accessibilityHint + invalidText;
|
|
182
|
-
}
|
|
183
|
-
if (validationStatus === 'valid' && validText) {
|
|
184
|
-
accessibilityHint = accessibilityHint + validText;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return accessibilityHint || props.accessibilityHint;
|
|
188
|
-
};
|
|
167
|
+
const { accessibilityHint, accessibilityLabel } = useFormFieldAccessibility({
|
|
168
|
+
label,
|
|
169
|
+
helperText,
|
|
170
|
+
validText,
|
|
171
|
+
invalidText,
|
|
172
|
+
validationStatus,
|
|
173
|
+
fallbackLabel: props.accessibilityLabel,
|
|
174
|
+
fallbackHint: props.accessibilityHint,
|
|
175
|
+
includeRequiredInLabel: false,
|
|
176
|
+
});
|
|
189
177
|
|
|
190
178
|
return (
|
|
191
179
|
<FormField
|
|
@@ -208,8 +196,8 @@ const VerificationInput = forwardRef<VerificationInputHandle, VerificationInputP
|
|
|
208
196
|
value={displayValue}
|
|
209
197
|
autoFocus={autoFocus}
|
|
210
198
|
editable={!disabled && !readonly}
|
|
211
|
-
accessibilityLabel={
|
|
212
|
-
accessibilityHint={
|
|
199
|
+
accessibilityLabel={accessibilityLabel}
|
|
200
|
+
accessibilityHint={accessibilityHint}
|
|
213
201
|
accessibilityState={{ disabled: disabled || readonly }}
|
|
214
202
|
importantForAccessibility="yes"
|
|
215
203
|
onChangeText={handleChangeText}
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as useBreakpointValue } from './useBreakpointValue';
|
|
2
2
|
export { default as useColorMode } from './useColorMode';
|
|
3
|
+
export { default as useFormFieldAccessibility } from './useFormFieldAccessibility';
|
|
3
4
|
export { default as useMedia } from './useMedia';
|
|
4
5
|
export { usePrevious } from './usePrevious';
|
|
5
6
|
export { useStyleProps } from './useStyleProps';
|