@utilitywarehouse/hearth-react-native 0.30.4 → 0.31.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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-lint.log +12 -15
- package/CHANGELOG.md +149 -0
- package/build/components/Badge/Badge.js +2 -2
- package/build/components/Badge/Badge.props.d.ts +1 -0
- package/build/components/Badge/BadgeText.d.ts +1 -1
- package/build/components/Badge/BadgeText.js +2 -2
- package/build/components/Container/Container.props.d.ts +2 -2
- package/build/components/ExpandableCard/ExpandableCard.d.ts +1 -1
- package/build/components/ExpandableCard/ExpandableCard.js +13 -2
- package/build/components/ExpandableCard/ExpandableCard.props.d.ts +43 -23
- package/build/components/ExpandableCard/ExpandableCardText.js +1 -1
- package/build/components/ExpandableCard/ExpandableCardTrigger.d.ts +3 -3
- package/build/components/ExpandableCard/ExpandableCardTrigger.props.d.ts +31 -6
- package/build/components/ExpandableCard/ExpandableCardTriggerRoot.d.ts +1 -1
- package/build/components/ExpandableCard/ExpandableCardTriggerRoot.js +13 -2
- package/build/components/Flex/Flex.props.d.ts +2 -2
- package/build/components/FormField/FormField.d.ts +5 -5
- package/build/components/FormField/FormField.js +3 -2
- package/build/components/Modal/Modal.d.ts +1 -1
- package/build/components/Modal/Modal.js +33 -39
- package/build/components/Modal/Modal.props.d.ts +8 -3
- package/build/components/Modal/Modal.shared.types.d.ts +19 -4
- package/build/components/Modal/Modal.web.d.ts +1 -1
- package/build/components/Modal/Modal.web.js +6 -3
- package/build/components/NavModal/NavModal.d.ts +1 -1
- package/build/components/NavModal/NavModal.js +10 -7
- package/build/components/NavModal/NavModal.props.d.ts +4 -3
- package/build/components/Textarea/Textarea.d.ts +1 -1
- package/build/components/Textarea/Textarea.js +64 -5
- package/build/components/Textarea/Textarea.props.d.ts +10 -0
- package/build/components/Textarea/TextareaRoot.js +4 -1
- package/docs/changelog.mdx +21 -0
- package/package.json +1 -1
- package/src/components/Badge/Badge.props.ts +1 -0
- package/src/components/Badge/Badge.tsx +6 -1
- package/src/components/Badge/BadgeText.tsx +8 -2
- package/src/components/Container/Container.props.ts +10 -1
- package/src/components/ExpandableCard/ExpandableCard.docs.mdx +89 -37
- package/src/components/ExpandableCard/ExpandableCard.props.ts +51 -27
- package/src/components/ExpandableCard/ExpandableCard.stories.tsx +67 -17
- package/src/components/ExpandableCard/ExpandableCard.tsx +15 -7
- package/src/components/ExpandableCard/ExpandableCardText.tsx +1 -1
- package/src/components/ExpandableCard/ExpandableCardTrigger.props.ts +37 -7
- package/src/components/ExpandableCard/ExpandableCardTriggerRoot.tsx +36 -2
- package/src/components/Flex/Flex.props.ts +16 -2
- package/src/components/FormField/FormField.tsx +2 -1
- package/src/components/List/List.stories.tsx +35 -0
- package/src/components/Modal/Modal.docs.mdx +52 -1
- package/src/components/Modal/Modal.props.ts +21 -6
- package/src/components/Modal/Modal.shared.types.ts +23 -4
- package/src/components/Modal/Modal.stories.tsx +165 -1
- package/src/components/Modal/Modal.tsx +101 -81
- package/src/components/Modal/Modal.web.tsx +29 -23
- package/src/components/NavModal/NavModal.docs.mdx +29 -0
- package/src/components/NavModal/NavModal.props.ts +11 -3
- package/src/components/NavModal/NavModal.stories.tsx +29 -0
- package/src/components/NavModal/NavModal.tsx +39 -33
- package/src/components/Textarea/Textarea.docs.mdx +33 -1
- package/src/components/Textarea/Textarea.props.ts +11 -2
- package/src/components/Textarea/Textarea.stories.tsx +21 -1
- package/src/components/Textarea/Textarea.tsx +107 -3
- package/src/components/Textarea/TextareaRoot.tsx +6 -2
|
@@ -22,6 +22,7 @@ The `ExpandableCard` component is an interactive card that expands to reveal add
|
|
|
22
22
|
- [With Leading Icon](#with-leading-icon)
|
|
23
23
|
- [With `Badge`](#with-badge)
|
|
24
24
|
- [With Numeric Value](#with-numeric-value)
|
|
25
|
+
- [With Custom Trigger Content](#with-custom-trigger-content)
|
|
25
26
|
- [`CardGroup`](#cardgroup)
|
|
26
27
|
- [Advanced Usage](#advanced-usage)
|
|
27
28
|
- [Accessibility](#accessibility)
|
|
@@ -58,24 +59,27 @@ const MyComponent = () => (
|
|
|
58
59
|
|
|
59
60
|
### `ExpandableCard`
|
|
60
61
|
|
|
61
|
-
| Prop | Type | Default | Description
|
|
62
|
-
| ------------------ | ----------------------------- | ------------------- |
|
|
63
|
-
| `heading` | `string` | - | The heading text displayed in the trigger
|
|
64
|
-
| `helperText` | `string` | - | Optional helper text displayed below the heading
|
|
65
|
-
| `leadingIcon` | `ComponentType` | - | Leading icon component (automatically wrapped)
|
|
66
|
-
| `leadingContent` | `ReactNode` | - | Leading content (icon or custom element)
|
|
67
|
-
| `badge` | `ReactNode` | - | Badge to display
|
|
68
|
-
| `badgePosition` | `'top' \| 'bottom'` | `'bottom'` | Badge position
|
|
69
|
-
| `numericValue` | `string \| number` | - | Numeric value to display on the right
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
74
|
-
| `
|
|
75
|
-
| `
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
78
|
-
| `
|
|
62
|
+
| Prop | Type | Default | Description |
|
|
63
|
+
| ------------------ | ----------------------------- | ------------------- | -------------------------------------------------------------- |
|
|
64
|
+
| `heading` | `string` | - | The heading text displayed in the trigger |
|
|
65
|
+
| `helperText` | `string` | - | Optional helper text displayed below the heading |
|
|
66
|
+
| `leadingIcon` | `ComponentType` | - | Leading icon component (automatically wrapped) |
|
|
67
|
+
| `leadingContent` | `ReactNode` | - | Leading content (icon or custom element) |
|
|
68
|
+
| `badge` | `ReactNode` | - | Badge to display |
|
|
69
|
+
| `badgePosition` | `'top' \| 'bottom'` | `'bottom'` | Badge position |
|
|
70
|
+
| `numericValue` | `string \| number` | - | Numeric value to display on the right |
|
|
71
|
+
| `triggerContent` | `ReactNode` | - | Replaces the default trigger content while keeping the chevron |
|
|
72
|
+
| `expandedContent` | `ReactNode` | - | The content to show when expanded |
|
|
73
|
+
| `expanded` | `boolean` | - | Whether the card is expanded (controlled) |
|
|
74
|
+
| `onExpandedChange` | `(expanded: boolean) => void` | - | Callback when expanded state changes |
|
|
75
|
+
| `duration` | `number` | `200` | Duration of expansion animation in milliseconds |
|
|
76
|
+
| `animateOpacity` | `boolean` | `true` | Whether to animate opacity during expansion |
|
|
77
|
+
| `disabled` | `boolean` | `false` | Whether the card is disabled |
|
|
78
|
+
| `colorScheme` | `CardColorScheme` | - | Color scheme (inherits from Card component) |
|
|
79
|
+
| `variant` | `CardVariant` | - | Variant (inherits from Card component) |
|
|
80
|
+
| `testID` | `string` | `'expandable-card'` | Test ID for testing |
|
|
81
|
+
|
|
82
|
+
When `triggerContent` is provided, the default trigger props such as `heading`, `helperText`, `leadingIcon`, `leadingContent`, `badge`, `badgePosition`, and `numericValue` are not available.
|
|
79
83
|
|
|
80
84
|
### `ExpandableCardGroup`
|
|
81
85
|
|
|
@@ -175,6 +179,34 @@ const MyComponent = () => (
|
|
|
175
179
|
/>
|
|
176
180
|
```
|
|
177
181
|
|
|
182
|
+
### With Custom Trigger Content
|
|
183
|
+
|
|
184
|
+
Use `triggerContent` when you need to replace the standard trigger layout entirely while keeping the expandable chevron.
|
|
185
|
+
|
|
186
|
+
<Canvas of={Stories.WithCustomTriggerContent} />
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { ExpandableCard, BodyText } from '@utilitywarehouse/hearth-react-native';
|
|
190
|
+
import { View } from 'react-native';
|
|
191
|
+
|
|
192
|
+
const MyComponent = () => (
|
|
193
|
+
<ExpandableCard
|
|
194
|
+
triggerContent={
|
|
195
|
+
<View style={{ flex: 1, gap: 4 }}>
|
|
196
|
+
<BodyText weight="semibold">March bill ready</BodyText>
|
|
197
|
+
<BodyText color="secondary">Due on 18 April 2026</BodyText>
|
|
198
|
+
</View>
|
|
199
|
+
}
|
|
200
|
+
expandedContent={
|
|
201
|
+
<>
|
|
202
|
+
<BodyText>Balance: £89.50</BodyText>
|
|
203
|
+
<BodyText>Payment method: Direct Debit</BodyText>
|
|
204
|
+
</>
|
|
205
|
+
}
|
|
206
|
+
/>
|
|
207
|
+
);
|
|
208
|
+
```
|
|
209
|
+
|
|
178
210
|
### `CardGroup`
|
|
179
211
|
|
|
180
212
|
Use `ExpandableCardGroup` to group multiple expandable cards with an optional header.
|
|
@@ -239,34 +271,52 @@ import {
|
|
|
239
271
|
ExpandableCardTrigger,
|
|
240
272
|
ExpandableCardExpandedContent,
|
|
241
273
|
ExpandableCardContent,
|
|
242
|
-
|
|
274
|
+
ExpandableCardHelperText,
|
|
275
|
+
ExpandableCardText,
|
|
243
276
|
BodyText,
|
|
244
277
|
} from '@utilitywarehouse/hearth-react-native';
|
|
245
|
-
import { BillMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
|
|
246
278
|
import { useState } from 'react';
|
|
279
|
+
import { View } from 'react-native';
|
|
247
280
|
|
|
248
281
|
const MyComponent = () => {
|
|
249
|
-
const [
|
|
282
|
+
const [triggerContentExpanded, setTriggerContentExpanded] = useState(false);
|
|
283
|
+
const [customChildrenExpanded, setCustomChildrenExpanded] = useState(false);
|
|
250
284
|
|
|
251
285
|
return (
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
<
|
|
286
|
+
<>
|
|
287
|
+
<ExpandableCard
|
|
288
|
+
expanded={triggerContentExpanded}
|
|
289
|
+
onExpandedChange={setTriggerContentExpanded}
|
|
290
|
+
expandedContent={<BodyText>Use `triggerContent` for a full replacement.</BodyText>}
|
|
291
|
+
triggerContent={
|
|
292
|
+
<View style={{ flex: 1, gap: 4 }}>
|
|
293
|
+
<BodyText weight="semibold">Use `triggerContent`</BodyText>
|
|
294
|
+
<BodyText color="secondary">Replace the built-in trigger layout</BodyText>
|
|
295
|
+
</View>
|
|
259
296
|
}
|
|
260
|
-
numericValue="£123.45"
|
|
261
|
-
isExpanded={expanded}
|
|
262
297
|
/>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
298
|
+
|
|
299
|
+
<ExpandableCard
|
|
300
|
+
expanded={customChildrenExpanded}
|
|
301
|
+
onExpandedChange={setCustomChildrenExpanded}
|
|
302
|
+
>
|
|
303
|
+
<ExpandableCardTrigger
|
|
304
|
+
onPress={() => setCustomChildrenExpanded(!customChildrenExpanded)}
|
|
305
|
+
isExpanded={customChildrenExpanded}
|
|
306
|
+
showChevron
|
|
307
|
+
>
|
|
308
|
+
<ExpandableCardContent>
|
|
309
|
+
<ExpandableCardText>Use `children` + `showChevron`</ExpandableCardText>
|
|
310
|
+
<ExpandableCardHelperText>
|
|
311
|
+
Build the trigger yourself and keep the chevron
|
|
312
|
+
</ExpandableCardHelperText>
|
|
313
|
+
</ExpandableCardContent>
|
|
314
|
+
</ExpandableCardTrigger>
|
|
315
|
+
<ExpandableCardExpandedContent isExpanded={customChildrenExpanded}>
|
|
316
|
+
<BodyText>Use `children` when you want full trigger composition control.</BodyText>
|
|
317
|
+
</ExpandableCardExpandedContent>
|
|
318
|
+
</ExpandableCard>
|
|
319
|
+
</>
|
|
270
320
|
);
|
|
271
321
|
};
|
|
272
322
|
```
|
|
@@ -285,6 +335,8 @@ When using advanced composition, you have access to these components:
|
|
|
285
335
|
- **`ExpandableCardTrailingContent`**: Container for trailing content
|
|
286
336
|
- **`ExpandableCardTrailingIcon`**: Styled icon for trailing position
|
|
287
337
|
|
|
338
|
+
Use `triggerContent` when you want a simpler one-prop replacement on `ExpandableCard`. Use `ExpandableCardTrigger` children when you want to compose the trigger structure yourself. The built-in chevron is shown by default, and you can set `showChevron={false}` if you want to hide it.
|
|
339
|
+
|
|
288
340
|
## Accessibility
|
|
289
341
|
|
|
290
342
|
The ExpandableCard component includes built-in accessibility support:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import type CardProps from '../Card/Card.props';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
interface ExpandableCardSharedProps extends Omit<CardProps, 'children'> {
|
|
5
5
|
/**
|
|
6
6
|
* Whether the card is expanded
|
|
7
7
|
*/
|
|
@@ -12,6 +12,40 @@ export interface ExpandableCardProps extends Omit<CardProps, 'children'> {
|
|
|
12
12
|
*/
|
|
13
13
|
onExpandedChange?: (expanded: boolean) => void;
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The heading text displayed in the trigger
|
|
17
|
+
*/
|
|
18
|
+
expandedContent?: ReactNode;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Duration of the expansion animation in milliseconds
|
|
22
|
+
* @default 200
|
|
23
|
+
*/
|
|
24
|
+
duration?: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether to animate opacity during expansion
|
|
28
|
+
* @default true
|
|
29
|
+
*/
|
|
30
|
+
animateOpacity?: boolean;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Whether the card is disabled
|
|
34
|
+
*/
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Test ID for testing
|
|
39
|
+
*/
|
|
40
|
+
testID?: string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Custom children for advanced composition
|
|
44
|
+
*/
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface ExpandableCardDefaultTriggerProps {
|
|
15
49
|
/**
|
|
16
50
|
* The heading text displayed in the trigger
|
|
17
51
|
*/
|
|
@@ -49,36 +83,26 @@ export interface ExpandableCardProps extends Omit<CardProps, 'children'> {
|
|
|
49
83
|
numericValue?: string | number;
|
|
50
84
|
|
|
51
85
|
/**
|
|
52
|
-
*
|
|
53
|
-
*/
|
|
54
|
-
expandedContent?: ReactNode;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Duration of the expansion animation in milliseconds
|
|
58
|
-
* @default 200
|
|
59
|
-
*/
|
|
60
|
-
duration?: number;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Whether to animate opacity during expansion
|
|
64
|
-
* @default true
|
|
65
|
-
*/
|
|
66
|
-
animateOpacity?: boolean;
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Whether the card is disabled
|
|
86
|
+
* Custom trigger content that replaces the default trigger layout.
|
|
70
87
|
*/
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Test ID for testing
|
|
75
|
-
*/
|
|
76
|
-
testID?: string;
|
|
88
|
+
triggerContent?: never;
|
|
89
|
+
}
|
|
77
90
|
|
|
91
|
+
interface ExpandableCardCustomTriggerProps {
|
|
78
92
|
/**
|
|
79
|
-
* Custom
|
|
93
|
+
* Custom trigger content that replaces the default trigger layout.
|
|
80
94
|
*/
|
|
81
|
-
|
|
95
|
+
triggerContent: ReactNode;
|
|
96
|
+
heading?: never;
|
|
97
|
+
helperText?: never;
|
|
98
|
+
leadingIcon?: never;
|
|
99
|
+
leadingContent?: never;
|
|
100
|
+
badge?: never;
|
|
101
|
+
badgePosition?: never;
|
|
102
|
+
numericValue?: never;
|
|
82
103
|
}
|
|
83
104
|
|
|
105
|
+
type ExpandableCardProps = ExpandableCardSharedProps &
|
|
106
|
+
(ExpandableCardDefaultTriggerProps | ExpandableCardCustomTriggerProps);
|
|
107
|
+
|
|
84
108
|
export default ExpandableCardProps;
|
|
@@ -7,11 +7,15 @@ import {
|
|
|
7
7
|
SettingsMediumIcon,
|
|
8
8
|
} from '@utilitywarehouse/hearth-react-native-icons';
|
|
9
9
|
import React from 'react';
|
|
10
|
+
import { View } from 'react-native';
|
|
10
11
|
import { Badge, BodyText, IconContainer, Link } from '../../components';
|
|
11
12
|
import ExpandableCard from './ExpandableCard';
|
|
13
|
+
import ExpandableCardContent from './ExpandableCardContent';
|
|
12
14
|
import ExpandableCardExpandedContent from './ExpandableCardExpandedContent';
|
|
13
15
|
import ExpandableCardGroup from './ExpandableCardGroup';
|
|
16
|
+
import ExpandableCardHelperText from './ExpandableCardHelperText';
|
|
14
17
|
import ExpandableCardIcon from './ExpandableCardIcon';
|
|
18
|
+
import ExpandableCardText from './ExpandableCardText';
|
|
15
19
|
import ExpandableCardTrigger from './ExpandableCardTrigger';
|
|
16
20
|
|
|
17
21
|
const meta = {
|
|
@@ -161,6 +165,26 @@ export const WithNumericValue: Story = {
|
|
|
161
165
|
),
|
|
162
166
|
};
|
|
163
167
|
|
|
168
|
+
export const WithCustomTriggerContent: Story = {
|
|
169
|
+
render: () => (
|
|
170
|
+
<ExpandableCard
|
|
171
|
+
triggerContent={
|
|
172
|
+
<View style={{ flex: 1, gap: 4 }}>
|
|
173
|
+
<BodyText weight="semibold">March bill ready</BodyText>
|
|
174
|
+
<BodyText color="secondary">Due on 18 April 2026</BodyText>
|
|
175
|
+
</View>
|
|
176
|
+
}
|
|
177
|
+
expandedContent={
|
|
178
|
+
<>
|
|
179
|
+
<BodyText>Balance: £89.50</BodyText>
|
|
180
|
+
<BodyText>Payment method: Direct Debit</BodyText>
|
|
181
|
+
</>
|
|
182
|
+
}
|
|
183
|
+
style={{ width: 350 }}
|
|
184
|
+
/>
|
|
185
|
+
),
|
|
186
|
+
};
|
|
187
|
+
|
|
164
188
|
export const Disabled: Story = {
|
|
165
189
|
render: () => (
|
|
166
190
|
<ExpandableCard
|
|
@@ -300,27 +324,53 @@ export const ColorSchemes: Story = {
|
|
|
300
324
|
|
|
301
325
|
export const AdvancedComposition: Story = {
|
|
302
326
|
render: () => {
|
|
303
|
-
const [
|
|
327
|
+
const [triggerContentExpanded, setTriggerContentExpanded] = React.useState(false);
|
|
328
|
+
const [customChildrenExpanded, setCustomChildrenExpanded] = React.useState(false);
|
|
304
329
|
|
|
305
330
|
return (
|
|
306
|
-
<
|
|
307
|
-
<
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
331
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 16, width: 350 }}>
|
|
332
|
+
<ExpandableCard
|
|
333
|
+
expanded={triggerContentExpanded}
|
|
334
|
+
onExpandedChange={setTriggerContentExpanded}
|
|
335
|
+
expandedContent={
|
|
336
|
+
<>
|
|
337
|
+
<BodyText>Account Number: 1234567890</BodyText>
|
|
338
|
+
<BodyText>Sort Code: 12-34-56</BodyText>
|
|
339
|
+
<BodyText>Balance: £123.45</BodyText>
|
|
340
|
+
</>
|
|
341
|
+
}
|
|
342
|
+
triggerContent={
|
|
343
|
+
<View style={{ flex: 1, gap: 4 }}>
|
|
344
|
+
<BodyText weight="semibold">Use `triggerContent`</BodyText>
|
|
345
|
+
<BodyText color="secondary">Replace the built-in trigger layout</BodyText>
|
|
346
|
+
</View>
|
|
313
347
|
}
|
|
314
|
-
numericValue="£123.45"
|
|
315
|
-
isExpanded={expanded}
|
|
316
348
|
/>
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
349
|
+
|
|
350
|
+
<ExpandableCard
|
|
351
|
+
expanded={customChildrenExpanded}
|
|
352
|
+
onExpandedChange={setCustomChildrenExpanded}
|
|
353
|
+
>
|
|
354
|
+
<ExpandableCardTrigger
|
|
355
|
+
onPress={() => setCustomChildrenExpanded(!customChildrenExpanded)}
|
|
356
|
+
isExpanded={customChildrenExpanded}
|
|
357
|
+
showChevron
|
|
358
|
+
>
|
|
359
|
+
<ExpandableCardContent>
|
|
360
|
+
<ExpandableCardText>Use `children` + `showChevron`</ExpandableCardText>
|
|
361
|
+
<ExpandableCardHelperText>
|
|
362
|
+
Build the trigger yourself and keep the chevron
|
|
363
|
+
</ExpandableCardHelperText>
|
|
364
|
+
</ExpandableCardContent>
|
|
365
|
+
</ExpandableCardTrigger>
|
|
366
|
+
<ExpandableCardExpandedContent isExpanded={customChildrenExpanded}>
|
|
367
|
+
<BodyText>Account Number: 1234567890</BodyText>
|
|
368
|
+
<BodyText>Sort Code: 12-34-56</BodyText>
|
|
369
|
+
<BodyText>Balance: £123.45</BodyText>
|
|
370
|
+
<BodyText>Last Updated: 12/11/25</BodyText>
|
|
371
|
+
</ExpandableCardExpandedContent>
|
|
372
|
+
</ExpandableCard>
|
|
373
|
+
</div>
|
|
324
374
|
);
|
|
325
375
|
},
|
|
326
376
|
};
|
|
@@ -14,6 +14,7 @@ const ExpandableCard = ({
|
|
|
14
14
|
badge,
|
|
15
15
|
badgePosition = 'bottom',
|
|
16
16
|
numericValue,
|
|
17
|
+
triggerContent,
|
|
17
18
|
expandedContent,
|
|
18
19
|
duration = 200,
|
|
19
20
|
animateOpacity = true,
|
|
@@ -39,18 +40,25 @@ const ExpandableCard = ({
|
|
|
39
40
|
onExpandedChange?.(newExpanded);
|
|
40
41
|
};
|
|
41
42
|
|
|
43
|
+
const triggerProps =
|
|
44
|
+
triggerContent !== undefined
|
|
45
|
+
? { triggerContent }
|
|
46
|
+
: {
|
|
47
|
+
heading,
|
|
48
|
+
helperText,
|
|
49
|
+
leadingIcon,
|
|
50
|
+
leadingContent,
|
|
51
|
+
badge,
|
|
52
|
+
badgePosition,
|
|
53
|
+
numericValue,
|
|
54
|
+
};
|
|
55
|
+
|
|
42
56
|
const renderDefaultContent = () => (
|
|
43
57
|
<>
|
|
44
58
|
<ExpandableCardTrigger
|
|
45
59
|
onPress={handlePress}
|
|
46
60
|
disabled={disabled}
|
|
47
|
-
|
|
48
|
-
helperText={helperText}
|
|
49
|
-
leadingIcon={leadingIcon}
|
|
50
|
-
leadingContent={leadingContent}
|
|
51
|
-
badge={badge}
|
|
52
|
-
badgePosition={badgePosition}
|
|
53
|
-
numericValue={numericValue}
|
|
61
|
+
{...triggerProps}
|
|
54
62
|
isExpanded={isExpanded}
|
|
55
63
|
testID={`${testID}-trigger`}
|
|
56
64
|
/>
|
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import type { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import type { PressableProps } from 'react-native';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
interface ExpandableCardTriggerSharedProps extends Omit<PressableProps, 'children'> {
|
|
5
|
+
/**
|
|
6
|
+
* Whether the expandable card is expanded
|
|
7
|
+
*/
|
|
8
|
+
isExpanded: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Whether to show the chevron when providing custom children.
|
|
11
|
+
* @default true
|
|
12
|
+
*/
|
|
13
|
+
showChevron?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Whether the trigger is disabled
|
|
16
|
+
*/
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
/* Optional children */
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ExpandableCardTriggerDefaultContentProps {
|
|
5
23
|
/**
|
|
6
24
|
* The main heading text
|
|
7
25
|
*/
|
|
@@ -31,16 +49,28 @@ export interface ExpandableCardTriggerProps extends Omit<PressableProps, 'childr
|
|
|
31
49
|
* Optional numeric value to display
|
|
32
50
|
*/
|
|
33
51
|
numericValue?: string | number;
|
|
52
|
+
|
|
34
53
|
/**
|
|
35
|
-
*
|
|
54
|
+
* Custom trigger content that replaces the default trigger layout.
|
|
36
55
|
*/
|
|
37
|
-
|
|
56
|
+
triggerContent?: never;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface ExpandableCardTriggerCustomContentProps {
|
|
38
60
|
/**
|
|
39
|
-
*
|
|
61
|
+
* Custom trigger content that replaces the default trigger layout.
|
|
40
62
|
*/
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
triggerContent: ReactNode;
|
|
64
|
+
heading?: never;
|
|
65
|
+
helperText?: never;
|
|
66
|
+
leadingIcon?: never;
|
|
67
|
+
leadingContent?: never;
|
|
68
|
+
badge?: never;
|
|
69
|
+
badgePosition?: never;
|
|
70
|
+
numericValue?: never;
|
|
44
71
|
}
|
|
45
72
|
|
|
73
|
+
type ExpandableCardTriggerProps = ExpandableCardTriggerSharedProps &
|
|
74
|
+
(ExpandableCardTriggerDefaultContentProps | ExpandableCardTriggerCustomContentProps);
|
|
75
|
+
|
|
46
76
|
export default ExpandableCardTriggerProps;
|
|
@@ -22,7 +22,9 @@ const ExpandableCardTriggerRoot = ({
|
|
|
22
22
|
badge,
|
|
23
23
|
badgePosition = 'bottom',
|
|
24
24
|
numericValue,
|
|
25
|
+
triggerContent,
|
|
25
26
|
isExpanded,
|
|
27
|
+
showChevron = true,
|
|
26
28
|
disabled,
|
|
27
29
|
states,
|
|
28
30
|
children,
|
|
@@ -53,6 +55,8 @@ const ExpandableCardTriggerRoot = ({
|
|
|
53
55
|
return null;
|
|
54
56
|
};
|
|
55
57
|
|
|
58
|
+
const defaultAccessibilityLabel = [heading, helperText].filter(Boolean).join(', ');
|
|
59
|
+
|
|
56
60
|
const renderDefaultContent = () => (
|
|
57
61
|
<>
|
|
58
62
|
{renderLeadingContent()}
|
|
@@ -73,6 +77,36 @@ const ExpandableCardTriggerRoot = ({
|
|
|
73
77
|
</>
|
|
74
78
|
);
|
|
75
79
|
|
|
80
|
+
const renderChevron = () => (
|
|
81
|
+
<ExpandableCardTrailingContent style={styles.chevron}>
|
|
82
|
+
<ExpandableCardTrailingIcon as={isExpanded ? ChevronUpSmallIcon : ChevronDownSmallIcon} />
|
|
83
|
+
</ExpandableCardTrailingContent>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const renderCustomTriggerContent = () => (
|
|
87
|
+
<>
|
|
88
|
+
{triggerContent}
|
|
89
|
+
{showChevron ? renderChevron() : null}
|
|
90
|
+
</>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const renderChildrenContent = () => (
|
|
94
|
+
<>
|
|
95
|
+
{children}
|
|
96
|
+
{showChevron ? renderChevron() : null}
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
let triggerBody = renderDefaultContent();
|
|
101
|
+
|
|
102
|
+
if (triggerContent !== undefined) {
|
|
103
|
+
triggerBody = renderCustomTriggerContent();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (children) {
|
|
107
|
+
triggerBody = renderChildrenContent();
|
|
108
|
+
}
|
|
109
|
+
|
|
76
110
|
return (
|
|
77
111
|
<Pressable
|
|
78
112
|
{...props}
|
|
@@ -81,9 +115,9 @@ const ExpandableCardTriggerRoot = ({
|
|
|
81
115
|
disabled={disabled}
|
|
82
116
|
accessibilityRole="button"
|
|
83
117
|
accessibilityState={{ expanded: isExpanded, disabled }}
|
|
84
|
-
accessibilityLabel={
|
|
118
|
+
accessibilityLabel={props.accessibilityLabel || defaultAccessibilityLabel || undefined}
|
|
85
119
|
>
|
|
86
|
-
{
|
|
120
|
+
{triggerBody}
|
|
87
121
|
</Pressable>
|
|
88
122
|
);
|
|
89
123
|
};
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import type { FlexAlignType, ViewProps, ViewStyle } from 'react-native';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DisplayProps,
|
|
4
|
+
FlexLayoutProps,
|
|
5
|
+
GapProps,
|
|
6
|
+
MarginProps,
|
|
7
|
+
PaddingProps,
|
|
8
|
+
SpacingValues,
|
|
9
|
+
} from '../../types';
|
|
3
10
|
|
|
4
|
-
interface FlexProps
|
|
11
|
+
interface FlexProps
|
|
12
|
+
extends
|
|
13
|
+
ViewProps,
|
|
14
|
+
MarginProps,
|
|
15
|
+
PaddingProps,
|
|
16
|
+
FlexLayoutProps,
|
|
17
|
+
GapProps,
|
|
18
|
+
Omit<DisplayProps, 'direction'> {
|
|
5
19
|
direction?: ViewStyle['flexDirection'];
|
|
6
20
|
align?: FlexAlignType;
|
|
7
21
|
justify?: ViewStyle['justifyContent'];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createFormControl } from '@gluestack-ui/form-control';
|
|
2
2
|
import { useMemo, useState } from 'react';
|
|
3
3
|
import { View } from 'react-native';
|
|
4
|
+
import { BodyText } from '../BodyText';
|
|
4
5
|
import { HelperIcon, HelperText } from '../Helper';
|
|
5
6
|
import { FormFieldContext } from './FormField.context';
|
|
6
7
|
import FormFieldProps from './FormField.props';
|
|
@@ -94,7 +95,7 @@ const FormField = ({
|
|
|
94
95
|
accessibilityElementsHidden={shouldHandleAccessibility}
|
|
95
96
|
>
|
|
96
97
|
{label}
|
|
97
|
-
{!required ?
|
|
98
|
+
{!required ? <BodyText weight="regular"> (Optional)</BodyText> : ''}
|
|
98
99
|
</FormFieldLabelText>
|
|
99
100
|
)}
|
|
100
101
|
{!!helperText && (
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
UserMediumIcon,
|
|
11
11
|
} from '@utilitywarehouse/hearth-react-native-icons';
|
|
12
12
|
import { useState } from 'react';
|
|
13
|
+
import { FlatList } from 'react-native';
|
|
13
14
|
import { List, ListAction, ListItem, ListItemIcon, ListItemTrailingIcon } from '.';
|
|
14
15
|
import { VariantTitle } from '../../../docs/components';
|
|
15
16
|
import { Badge } from '../Badge';
|
|
@@ -398,6 +399,40 @@ export const Loading: Story = {
|
|
|
398
399
|
),
|
|
399
400
|
};
|
|
400
401
|
|
|
402
|
+
export const WithFlatList: Story = {
|
|
403
|
+
parameters: {
|
|
404
|
+
controls: { include: [] },
|
|
405
|
+
},
|
|
406
|
+
render: () => {
|
|
407
|
+
const listData = Array.from({ length: 200 }).map((_, index) => ({
|
|
408
|
+
id: index.toString(),
|
|
409
|
+
heading: `List Item ${index + 1}`,
|
|
410
|
+
helperText: `Supporting Text ${index + 1}`,
|
|
411
|
+
}));
|
|
412
|
+
|
|
413
|
+
return (
|
|
414
|
+
<List
|
|
415
|
+
container="subtleWhite"
|
|
416
|
+
heading="FlatList Example"
|
|
417
|
+
helperText="This list is rendered using FlatList for performance"
|
|
418
|
+
>
|
|
419
|
+
<FlatList
|
|
420
|
+
data={listData}
|
|
421
|
+
keyExtractor={item => item.id}
|
|
422
|
+
renderItem={({ item }) => (
|
|
423
|
+
<ListItem
|
|
424
|
+
heading={item.heading}
|
|
425
|
+
helperText={item.helperText}
|
|
426
|
+
leadingContent={<ListItemIcon as={SettingsMediumIcon} />}
|
|
427
|
+
onPress={() => console.log(`${item.heading} pressed`)}
|
|
428
|
+
/>
|
|
429
|
+
)}
|
|
430
|
+
/>
|
|
431
|
+
</List>
|
|
432
|
+
);
|
|
433
|
+
},
|
|
434
|
+
};
|
|
435
|
+
|
|
401
436
|
export const KitchenSink: Story = {
|
|
402
437
|
parameters: {
|
|
403
438
|
controls: { include: [] },
|