@utilitywarehouse/hearth-react-native 0.30.4 → 0.31.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.
Files changed (121) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +15 -18
  3. package/CHANGELOG.md +165 -0
  4. package/build/components/Badge/Badge.js +2 -2
  5. package/build/components/Badge/Badge.props.d.ts +1 -0
  6. package/build/components/Badge/BadgeText.d.ts +1 -1
  7. package/build/components/Badge/BadgeText.js +2 -2
  8. package/build/components/Container/Container.props.d.ts +2 -2
  9. package/build/components/ExpandableCard/ExpandableCard.d.ts +1 -1
  10. package/build/components/ExpandableCard/ExpandableCard.js +13 -2
  11. package/build/components/ExpandableCard/ExpandableCard.props.d.ts +43 -23
  12. package/build/components/ExpandableCard/ExpandableCardText.js +1 -1
  13. package/build/components/ExpandableCard/ExpandableCardTrigger.d.ts +3 -3
  14. package/build/components/ExpandableCard/ExpandableCardTrigger.props.d.ts +31 -6
  15. package/build/components/ExpandableCard/ExpandableCardTriggerRoot.d.ts +1 -1
  16. package/build/components/ExpandableCard/ExpandableCardTriggerRoot.js +13 -2
  17. package/build/components/Flex/Flex.props.d.ts +2 -2
  18. package/build/components/FormField/FormField.d.ts +5 -5
  19. package/build/components/FormField/FormField.js +3 -2
  20. package/build/components/Modal/Modal.d.ts +1 -1
  21. package/build/components/Modal/Modal.js +33 -39
  22. package/build/components/Modal/Modal.props.d.ts +8 -3
  23. package/build/components/Modal/Modal.shared.types.d.ts +19 -4
  24. package/build/components/Modal/Modal.web.d.ts +1 -1
  25. package/build/components/Modal/Modal.web.js +6 -3
  26. package/build/components/NavModal/NavModal.d.ts +1 -1
  27. package/build/components/NavModal/NavModal.js +10 -7
  28. package/build/components/NavModal/NavModal.props.d.ts +4 -3
  29. package/build/components/Table/TableHeaderCell.js +10 -1
  30. package/build/components/Textarea/Textarea.d.ts +1 -1
  31. package/build/components/Textarea/Textarea.js +64 -5
  32. package/build/components/Textarea/Textarea.props.d.ts +10 -0
  33. package/build/components/Textarea/TextareaRoot.js +4 -1
  34. package/build/core/themes.d.ts +92 -88
  35. package/build/tokens/color.d.ts +82 -80
  36. package/build/tokens/color.js +41 -40
  37. package/build/tokens/components/dark/alert.d.ts +6 -6
  38. package/build/tokens/components/dark/alert.js +6 -6
  39. package/build/tokens/components/dark/bottom-navigation.d.ts +2 -2
  40. package/build/tokens/components/dark/bottom-navigation.js +2 -2
  41. package/build/tokens/components/dark/checkbox.d.ts +1 -1
  42. package/build/tokens/components/dark/checkbox.js +1 -1
  43. package/build/tokens/components/dark/icon-button.d.ts +3 -3
  44. package/build/tokens/components/dark/icon-button.js +3 -3
  45. package/build/tokens/components/dark/inline-link.d.ts +1 -1
  46. package/build/tokens/components/dark/inline-link.js +1 -1
  47. package/build/tokens/components/dark/link.d.ts +3 -3
  48. package/build/tokens/components/dark/link.js +3 -3
  49. package/build/tokens/components/dark/navigation.d.ts +2 -2
  50. package/build/tokens/components/dark/navigation.js +2 -2
  51. package/build/tokens/components/dark/parts.d.ts +2 -2
  52. package/build/tokens/components/dark/parts.js +2 -2
  53. package/build/tokens/components/dark/progress-bar.d.ts +3 -3
  54. package/build/tokens/components/dark/progress-bar.js +3 -3
  55. package/build/tokens/components/dark/progress-stepper.d.ts +1 -1
  56. package/build/tokens/components/dark/progress-stepper.js +1 -1
  57. package/build/tokens/components/dark/spinner.d.ts +1 -1
  58. package/build/tokens/components/dark/spinner.js +1 -1
  59. package/build/tokens/components/dark/table.d.ts +2 -0
  60. package/build/tokens/components/dark/table.js +2 -0
  61. package/build/tokens/components/dark/time-picker.d.ts +1 -0
  62. package/build/tokens/components/dark/time-picker.js +1 -0
  63. package/build/tokens/components/light/parts.d.ts +3 -3
  64. package/build/tokens/components/light/parts.js +3 -3
  65. package/build/tokens/components/light/table.d.ts +2 -0
  66. package/build/tokens/components/light/table.js +2 -0
  67. package/build/tokens/components/light/time-picker.d.ts +1 -0
  68. package/build/tokens/components/light/time-picker.js +1 -0
  69. package/build/tokens/semantic-dark.d.ts +40 -40
  70. package/build/tokens/semantic-dark.js +40 -40
  71. package/docs/changelog.mdx +170 -0
  72. package/package.json +3 -3
  73. package/src/components/Badge/Badge.props.ts +1 -0
  74. package/src/components/Badge/Badge.tsx +6 -1
  75. package/src/components/Badge/BadgeText.tsx +8 -2
  76. package/src/components/Container/Container.props.ts +10 -1
  77. package/src/components/ExpandableCard/ExpandableCard.docs.mdx +89 -37
  78. package/src/components/ExpandableCard/ExpandableCard.props.ts +51 -27
  79. package/src/components/ExpandableCard/ExpandableCard.stories.tsx +67 -17
  80. package/src/components/ExpandableCard/ExpandableCard.tsx +15 -7
  81. package/src/components/ExpandableCard/ExpandableCardText.tsx +1 -1
  82. package/src/components/ExpandableCard/ExpandableCardTrigger.props.ts +37 -7
  83. package/src/components/ExpandableCard/ExpandableCardTriggerRoot.tsx +36 -2
  84. package/src/components/Flex/Flex.props.ts +16 -2
  85. package/src/components/FormField/FormField.tsx +2 -1
  86. package/src/components/List/List.stories.tsx +35 -0
  87. package/src/components/Modal/Modal.docs.mdx +52 -1
  88. package/src/components/Modal/Modal.props.ts +21 -6
  89. package/src/components/Modal/Modal.shared.types.ts +23 -4
  90. package/src/components/Modal/Modal.stories.tsx +165 -1
  91. package/src/components/Modal/Modal.tsx +101 -81
  92. package/src/components/Modal/Modal.web.tsx +29 -23
  93. package/src/components/NavModal/NavModal.docs.mdx +29 -0
  94. package/src/components/NavModal/NavModal.props.ts +11 -3
  95. package/src/components/NavModal/NavModal.stories.tsx +29 -0
  96. package/src/components/NavModal/NavModal.tsx +39 -33
  97. package/src/components/Table/TableHeaderCell.tsx +10 -1
  98. package/src/components/Textarea/Textarea.docs.mdx +33 -1
  99. package/src/components/Textarea/Textarea.props.ts +11 -2
  100. package/src/components/Textarea/Textarea.stories.tsx +21 -1
  101. package/src/components/Textarea/Textarea.tsx +107 -3
  102. package/src/components/Textarea/TextareaRoot.tsx +6 -2
  103. package/src/tokens/color.ts +41 -40
  104. package/src/tokens/components/dark/alert.ts +6 -6
  105. package/src/tokens/components/dark/bottom-navigation.ts +2 -2
  106. package/src/tokens/components/dark/checkbox.ts +1 -1
  107. package/src/tokens/components/dark/icon-button.ts +3 -3
  108. package/src/tokens/components/dark/inline-link.ts +1 -1
  109. package/src/tokens/components/dark/link.ts +3 -3
  110. package/src/tokens/components/dark/navigation.ts +2 -2
  111. package/src/tokens/components/dark/parts.ts +2 -2
  112. package/src/tokens/components/dark/progress-bar.ts +3 -3
  113. package/src/tokens/components/dark/progress-stepper.ts +1 -1
  114. package/src/tokens/components/dark/spinner.ts +1 -1
  115. package/src/tokens/components/dark/table.ts +2 -0
  116. package/src/tokens/components/dark/time-picker.ts +1 -0
  117. package/src/tokens/components/light/parts.ts +3 -3
  118. package/src/tokens/components/light/table.ts +2 -0
  119. package/src/tokens/components/light/time-picker.ts +1 -0
  120. package/src/tokens/semantic-dark.ts +40 -40
  121. package/vercel.json +0 -21
@@ -9,6 +9,176 @@ import { BackToTopButton, NextPrevPage } from './components';
9
9
  The changelog for the Hearth React Native library. Here you can find all the changes, improvements, and bug fixes for each version.
10
10
 
11
11
 
12
+ ## 0.31.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [#1108](https://github.com/utilitywarehouse/hearth/pull/1108) [`8c2f3b5`](https://github.com/utilitywarehouse/hearth/commit/8c2f3b5334de83a7dd799b857394a34209b748e7) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE]: Add margin, padding, and layout utility props to `Flex`.
17
+
18
+ `Flex` now exposes the shared margin and padding utility props, along with the existing layout utility prop surface, so it can be used more like other layout primitives such as `Card`.
19
+
20
+ **Component affected**:
21
+ - `Flex`
22
+
23
+ **Developer changes**:
24
+
25
+ You can now apply spacing and layout utility props directly on `Flex`:
26
+
27
+ ```tsx
28
+ <Flex direction="row" spacing="md" padding="300" marginTop="200" flex={1}>
29
+ <Button>Back</Button>
30
+ <Button>Continue</Button>
31
+ </Flex>
32
+ ```
33
+
34
+ - [#1104](https://github.com/utilitywarehouse/hearth/pull/1104) [`91feeab`](https://github.com/utilitywarehouse/hearth/commit/91feeab091ae6bf80e543f9196214066ce8b29b0) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE]: Add custom trigger content options to `ExpandableCard`.
35
+
36
+ `ExpandableCard` now supports a `triggerContent` prop for replacing the default trigger layout while keeping the chevron. `ExpandableCardTrigger` also now supports `children` with an optional `showChevron` prop, so you can fully compose the trigger content yourself and still opt in to the built-in expand/collapse chevron.
37
+
38
+ **Components affected**:
39
+ - `ExpandableCard`
40
+ - `ExpandableCardTrigger`
41
+
42
+ **Developer changes**:
43
+
44
+ Use `triggerContent` when you want a single prop on `ExpandableCard` to replace the standard trigger content:
45
+
46
+ ```tsx
47
+ <ExpandableCard
48
+ triggerContent={<BodyText weight="semibold">March bill ready</BodyText>}
49
+ expandedContent={<BodyText>Balance: £89.50</BodyText>}
50
+ />
51
+ ```
52
+
53
+ Use `ExpandableCardTrigger` children when you want full control over the trigger structure. Add `showChevron` if you still want the built-in chevron:
54
+
55
+ ```tsx
56
+ <ExpandableCardTrigger isExpanded={expanded} onPress={toggleExpanded} showChevron>
57
+ <ExpandableCardContent>
58
+ <ExpandableCardText>Custom trigger</ExpandableCardText>
59
+ </ExpandableCardContent>
60
+ </ExpandableCardTrigger>
61
+ ```
62
+
63
+ - [#1109](https://github.com/utilitywarehouse/hearth/pull/1109) [`8215550`](https://github.com/utilitywarehouse/hearth/commit/8215550745d08a8b4c18a6902e39d3199018092a) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE]: Add `numberOfLines` support to `Badge` text.
64
+
65
+ `Badge` now renders its text content on a single line by default and supports a new `numberOfLines` prop when you want to allow more lines.
66
+
67
+ **Components affected**:
68
+ - `Badge`
69
+
70
+ **Developer changes**:
71
+
72
+ No changes are required unless you want a `Badge` to wrap onto more than one line. To opt in, pass `numberOfLines`:
73
+
74
+ ```tsx
75
+ <Badge numberOfLines={2} text="Long badge text that can wrap onto a second line" />
76
+ ```
77
+
78
+ - [#1108](https://github.com/utilitywarehouse/hearth/pull/1108) [`8c2f3b5`](https://github.com/utilitywarehouse/hearth/commit/8c2f3b5334de83a7dd799b857394a34209b748e7) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE]: Add custom footer support to `Modal` and `NavModal`.
79
+
80
+ `Modal` and `NavModal` now support a `footer` prop for replacing the built-in primary and secondary action buttons with custom content, plus a `footerStyle` prop for styling the footer container. When `footer` is provided, the button props are now disallowed at the type level.
81
+
82
+ **Components affected**:
83
+ - `Modal`
84
+ - `NavModal`
85
+
86
+ **Developer changes**:
87
+
88
+ Use `footer` when you need a custom footer layout, such as horizontal actions or extra decoration:
89
+
90
+ ```tsx
91
+ <Modal
92
+ heading="Update billing details"
93
+ footer={
94
+ <Flex direction="row" spacing="md" pt="250">
95
+ <Button variant="outline" colorScheme="functional" style={{ flex: 1 }}>
96
+ Cancel
97
+ </Button>
98
+ <Button style={{ flex: 1 }}>Save changes</Button>
99
+ </Flex>
100
+ }
101
+ footerStyle={{
102
+ boxShadow: '0px -6px 12px rgba(16, 24, 40, 0.12)',
103
+ }}
104
+ />
105
+ ```
106
+
107
+ - [#1103](https://github.com/utilitywarehouse/hearth/pull/1103) [`e375a44`](https://github.com/utilitywarehouse/hearth/commit/e375a442c507da1807e49f4d78e44edfff51d245) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE]: Add optional vertical resizing to `Textarea`.
108
+
109
+ `Textarea` now supports a new `resizable` prop that adds a bottom-right drag handle so people can increase the field height when they need more space for longer content.
110
+
111
+ **Components affected**:
112
+ - `Textarea`
113
+
114
+ **Developer changes**:
115
+
116
+ No changes are required unless you want to enable resizing for a textarea. To opt in, pass the new `resizable` prop:
117
+
118
+ ```tsx
119
+ <Textarea
120
+ label="Additional notes"
121
+ helperText="Drag the corner handle to resize"
122
+ placeholder="Enter your text here..."
123
+ resizable
124
+ />
125
+ ```
126
+
127
+ - [#1108](https://github.com/utilitywarehouse/hearth/pull/1108) [`8c2f3b5`](https://github.com/utilitywarehouse/hearth/commit/8c2f3b5334de83a7dd799b857394a34209b748e7) Thanks [@jordmccord](https://github.com/jordmccord)! - 🌟 [FEATURE]: Add flex utility props to `Container`.
128
+
129
+ `Container` now exposes shared flex layout utility props, allowing layout properties such as `flex`, `alignItems`, `justifyContent`, and `flexDirection` to be applied directly through its public prop API.
130
+
131
+ **Component affected**:
132
+ - `Container`
133
+
134
+ **Developer changes**:
135
+
136
+ You can now combine `Container`'s existing spacing props with flex layout props:
137
+
138
+ ```tsx
139
+ <Container flex={1} justifyContent="center" alignItems="stretch" gap="200">
140
+ <BodyText>Content</BodyText>
141
+ </Container>
142
+ ```
143
+
144
+ ### Patch Changes
145
+
146
+ - [#1103](https://github.com/utilitywarehouse/hearth/pull/1103) [`e375a44`](https://github.com/utilitywarehouse/hearth/commit/e375a442c507da1807e49f4d78e44edfff51d245) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Render FormField optional text with regular weight instead of inheriting the label weight.
147
+
148
+ This fixes FormField labels that append `(Optional)` so the optional text no longer inherits the main label weight.
149
+
150
+ **Components affected**:
151
+ - `FormField`
152
+ - `Textarea`
153
+ - `Input`
154
+
155
+ **Developer changes**:
156
+
157
+ No changes are required.
158
+
159
+ - [#1104](https://github.com/utilitywarehouse/hearth/pull/1104) [`91feeab`](https://github.com/utilitywarehouse/hearth/commit/91feeab091ae6bf80e543f9196214066ce8b29b0) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: `ExpandableCard` heading font size and weight
160
+
161
+ ## 0.30.4
162
+
163
+ ### Patch Changes
164
+
165
+ - [#1096](https://github.com/utilitywarehouse/hearth/pull/1096) [`6fd9021`](https://github.com/utilitywarehouse/hearth/commit/6fd9021a91ef38b56b78755965515a0807b34fa1) Thanks [@fillyD](https://github.com/fillyD)! - 🐛 [FIX]: Restore visibility of bottom sheet content in iOS accessibility tree, enabling automated tests to interact with `Select` options
166
+
167
+ ## 0.30.3
168
+
169
+ ### Patch Changes
170
+
171
+ - [#1094](https://github.com/utilitywarehouse/hearth/pull/1094) [`a9d8e66`](https://github.com/utilitywarehouse/hearth/commit/a9d8e660b7efa23c7a573af2658fc10ab6c043b9) Thanks [@jordmccord](https://github.com/jordmccord)! - 🐛 [FIX]: Make horizontal pressable `Banner` content flex correctly on native and web.
172
+
173
+ This fixes horizontal pressable `Banner` layouts where the content area did not expand consistently, which could misplace the chevron and action content.
174
+
175
+ **Components affected**:
176
+ - `Banner`
177
+
178
+ **Developer changes**:
179
+
180
+ No changes are required.
181
+
12
182
  ## 0.30.2
13
183
 
14
184
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utilitywarehouse/hearth-react-native",
3
- "version": "0.30.4",
3
+ "version": "0.31.1",
4
4
  "description": "Utility Warehouse React Native UI library",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -58,10 +58,10 @@
58
58
  "vite-plugin-svgr": "^4.5.0",
59
59
  "vitest": "^3.2.4",
60
60
  "@utilitywarehouse/hearth-fonts": "^0.0.4",
61
- "@utilitywarehouse/hearth-react-icons": "^0.8.0",
62
61
  "@utilitywarehouse/hearth-react-native-icons": "^0.8.0",
63
62
  "@utilitywarehouse/hearth-svg-assets": "^0.6.0",
64
- "@utilitywarehouse/hearth-tokens": "^0.2.4"
63
+ "@utilitywarehouse/hearth-tokens": "^0.2.4",
64
+ "@utilitywarehouse/hearth-react-icons": "^0.8.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@gorhom/bottom-sheet": ">=5.0.0",
@@ -20,6 +20,7 @@ interface BadgeProps extends ViewProps {
20
20
  icon?: ComponentType;
21
21
  flatBase?: boolean;
22
22
  text?: string | number;
23
+ numberOfLines?: number;
23
24
  }
24
25
 
25
26
  export default BadgeProps;
@@ -15,6 +15,7 @@ const Badge = ({ children, ...props }: BadgeProps) => {
15
15
  size = 'sm',
16
16
  style,
17
17
  text,
18
+ numberOfLines,
18
19
  ...rest
19
20
  } = props;
20
21
 
@@ -31,7 +32,11 @@ const Badge = ({ children, ...props }: BadgeProps) => {
31
32
  <BadgeContext.Provider value={value}>
32
33
  <View {...rest} style={[styles.container, style]}>
33
34
  {!!icon && <BadgeIcon as={icon} />}
34
- {childIsText ? <BadgeText>{text ?? children}</BadgeText> : children}
35
+ {childIsText ? (
36
+ <BadgeText numberOfLines={numberOfLines}>{text ?? children}</BadgeText>
37
+ ) : (
38
+ children
39
+ )}
35
40
  </View>
36
41
  </BadgeContext.Provider>
37
42
  );
@@ -3,11 +3,17 @@ import { StyleSheet } from 'react-native-unistyles';
3
3
  import { BodyText } from '../BodyText';
4
4
  import { useBadgeContext } from './Badge.context';
5
5
 
6
- const BadgeText = ({ children, style, ...props }: TextProps) => {
6
+ const BadgeText = ({ children, style, numberOfLines = 1, ...props }: TextProps) => {
7
7
  const { variant, colorScheme, size } = useBadgeContext();
8
8
  styles.useVariants({ variant, colorScheme });
9
9
  return (
10
- <BodyText {...props} size={size} weight="semibold" style={[styles.text, style]}>
10
+ <BodyText
11
+ {...props}
12
+ numberOfLines={numberOfLines}
13
+ size={size}
14
+ weight="semibold"
15
+ style={[styles.text, style]}
16
+ >
11
17
  {children}
12
18
  </BodyText>
13
19
  );
@@ -1,6 +1,8 @@
1
1
  import type { ViewProps } from 'react-native';
2
2
  import type {
3
3
  BackgroundColorProps,
4
+ DisplayProps,
5
+ FlexLayoutProps,
4
6
  GapProps,
5
7
  MarginProps,
6
8
  PaddingProps,
@@ -8,7 +10,14 @@ import type {
8
10
  } from '../../types';
9
11
 
10
12
  interface ContainerProps
11
- extends ViewProps, MarginProps, PaddingProps, GapProps, BackgroundColorProps {
13
+ extends
14
+ ViewProps,
15
+ MarginProps,
16
+ PaddingProps,
17
+ GapProps,
18
+ BackgroundColorProps,
19
+ FlexLayoutProps,
20
+ DisplayProps {
12
21
  /**
13
22
  * The spacing between child elements (gap).
14
23
  */
@@ -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
- | `expandedContent` | `ReactNode` | - | The content to show when expanded |
71
- | `expanded` | `boolean` | - | Whether the card is expanded (controlled) |
72
- | `onExpandedChange` | `(expanded: boolean) => void` | - | Callback when expanded state changes |
73
- | `duration` | `number` | `200` | Duration of expansion animation in milliseconds |
74
- | `animateOpacity` | `boolean` | `true` | Whether to animate opacity during expansion |
75
- | `disabled` | `boolean` | `false` | Whether the card is disabled |
76
- | `colorScheme` | `CardColorScheme` | - | Color scheme (inherits from Card component) |
77
- | `variant` | `CardVariant` | - | Variant (inherits from Card component) |
78
- | `testID` | `string` | `'expandable-card'` | Test ID for testing |
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
- IconContainer,
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 [expanded, setExpanded] = useState(false);
282
+ const [triggerContentExpanded, setTriggerContentExpanded] = useState(false);
283
+ const [customChildrenExpanded, setCustomChildrenExpanded] = useState(false);
250
284
 
251
285
  return (
252
- <ExpandableCard expanded={expanded} onExpandedChange={setExpanded}>
253
- <ExpandableCardTrigger
254
- onPress={() => setExpanded(!expanded)}
255
- heading="Custom Account Details"
256
- helperText="View your detailed account information"
257
- leadingContent={
258
- <IconContainer icon={BillMediumIcon} size="md" variant="emphasis" color="pig" />
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
- <ExpandableCardExpandedContent isExpanded={expanded}>
264
- <BodyText>Account Number: 1234567890</BodyText>
265
- <BodyText>Sort Code: 12-34-56</BodyText>
266
- <BodyText>Balance: £123.45</BodyText>
267
- <BodyText>Last Updated: 12/11/25</BodyText>
268
- </ExpandableCardExpandedContent>
269
- </ExpandableCard>
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
- export interface ExpandableCardProps extends Omit<CardProps, 'children'> {
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
- * The content to show when expanded
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
- disabled?: boolean;
72
-
73
- /**
74
- * Test ID for testing
75
- */
76
- testID?: string;
88
+ triggerContent?: never;
89
+ }
77
90
 
91
+ interface ExpandableCardCustomTriggerProps {
78
92
  /**
79
- * Custom children for advanced composition
93
+ * Custom trigger content that replaces the default trigger layout.
80
94
  */
81
- children?: ReactNode;
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 [expanded, setExpanded] = React.useState(false);
327
+ const [triggerContentExpanded, setTriggerContentExpanded] = React.useState(false);
328
+ const [customChildrenExpanded, setCustomChildrenExpanded] = React.useState(false);
304
329
 
305
330
  return (
306
- <ExpandableCard expanded={expanded} onExpandedChange={setExpanded}>
307
- <ExpandableCardTrigger
308
- onPress={() => setExpanded(!expanded)}
309
- heading="Custom Account Details"
310
- helperText="View your detailed account information"
311
- leadingContent={
312
- <IconContainer icon={BillMediumIcon} size="md" variant="emphasis" color="pig" />
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
- <ExpandableCardExpandedContent isExpanded={expanded}>
318
- <BodyText>Account Number: 1234567890</BodyText>
319
- <BodyText>Sort Code: 12-34-56</BodyText>
320
- <BodyText>Balance: £123.45</BodyText>
321
- <BodyText>Last Updated: 12/11/25</BodyText>
322
- </ExpandableCardExpandedContent>
323
- </ExpandableCard>
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
  };