@utilitywarehouse/hearth-react-native 0.4.1 → 0.5.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 (205) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +54 -0
  4. package/build/components/Alert/AlertTitle.js +6 -6
  5. package/build/components/Badge/Badge.js +3 -3
  6. package/build/components/Badge/Badge.props.d.ts +1 -0
  7. package/build/components/Button/ButtonRoot.js +4 -0
  8. package/build/components/Button/ButtonText.js +2 -2
  9. package/build/components/Card/CardRoot.js +1 -1
  10. package/build/components/Carousel/Carousel.context.d.ts +4 -0
  11. package/build/components/Carousel/Carousel.context.js +4 -0
  12. package/build/components/Carousel/Carousel.d.ts +6 -0
  13. package/build/components/Carousel/Carousel.js +278 -0
  14. package/build/components/Carousel/Carousel.props.d.ts +65 -0
  15. package/build/components/Carousel/Carousel.props.js +1 -0
  16. package/build/components/Carousel/CarouselControlItem.d.ts +24 -0
  17. package/build/components/Carousel/CarouselControlItem.js +64 -0
  18. package/build/components/Carousel/CarouselControls.d.ts +4 -0
  19. package/build/components/Carousel/CarouselControls.js +74 -0
  20. package/build/components/Carousel/CarouselItem.d.ts +6 -0
  21. package/build/components/Carousel/CarouselItem.js +38 -0
  22. package/build/components/Carousel/index.d.ts +5 -0
  23. package/build/components/Carousel/index.js +5 -0
  24. package/build/components/Checkbox/CheckboxTextContent.d.ts +1 -1
  25. package/build/components/Checkbox/CheckboxTextContent.js +9 -2
  26. package/build/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  27. package/build/components/CurrencyInput/CurrencyInput.js +3 -3
  28. package/build/components/CurrencyInput/CurrencyInput.props.d.ts +2 -2
  29. package/build/components/DescriptionList/DescriptionList.d.ts +1 -1
  30. package/build/components/DescriptionList/DescriptionList.js +2 -2
  31. package/build/components/DescriptionList/DescriptionList.props.d.ts +1 -8
  32. package/build/components/DescriptionList/DescriptionListItem.d.ts +1 -1
  33. package/build/components/DescriptionList/DescriptionListItem.js +4 -3
  34. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +3 -8
  35. package/build/components/IndicatorIconButton/IndicatorIconButton.d.ts +6 -0
  36. package/build/components/IndicatorIconButton/IndicatorIconButton.js +26 -0
  37. package/build/components/IndicatorIconButton/IndicatorIconButton.props.d.ts +8 -0
  38. package/build/components/IndicatorIconButton/IndicatorIconButton.props.js +1 -0
  39. package/build/components/IndicatorIconButton/index.d.ts +2 -0
  40. package/build/components/IndicatorIconButton/index.js +1 -0
  41. package/build/components/Link/LinkText.js +3 -3
  42. package/build/components/List/List.context.d.ts +0 -2
  43. package/build/components/List/List.d.ts +1 -1
  44. package/build/components/List/List.js +5 -5
  45. package/build/components/List/List.props.d.ts +1 -9
  46. package/build/components/List/ListAction/ListAction.d.ts +18 -0
  47. package/build/components/List/ListAction/ListAction.js +103 -0
  48. package/build/components/List/ListAction/ListAction.props.d.ts +8 -0
  49. package/build/components/List/ListAction/ListAction.props.js +1 -0
  50. package/build/components/List/ListAction/ListActionContent.d.ts +6 -0
  51. package/build/components/List/ListAction/ListActionContent.js +14 -0
  52. package/build/components/List/ListAction/ListActionText.d.ts +6 -0
  53. package/build/components/List/ListAction/ListActionText.js +7 -0
  54. package/build/components/List/ListAction/ListActionTrailingContent.d.ts +6 -0
  55. package/build/components/List/ListAction/ListActionTrailingContent.js +5 -0
  56. package/build/components/List/ListAction/ListActionTrailingIcon.d.ts +9 -0
  57. package/build/components/List/ListAction/ListActionTrailingIcon.js +18 -0
  58. package/build/components/List/ListAction/index.d.ts +6 -0
  59. package/build/components/List/ListAction/index.js +5 -0
  60. package/build/components/List/ListItem/ListItem.context.d.ts +1 -1
  61. package/build/components/List/ListItem/ListItem.props.d.ts +9 -5
  62. package/build/components/List/ListItem/ListItemRoot.d.ts +1 -1
  63. package/build/components/List/ListItem/ListItemRoot.js +10 -12
  64. package/build/components/List/ListItem/index.d.ts +4 -4
  65. package/build/components/List/ListItem/index.js +3 -3
  66. package/build/components/List/index.d.ts +1 -0
  67. package/build/components/List/index.js +1 -0
  68. package/build/components/ProgressStepper/ProgressStep.d.ts +10 -0
  69. package/build/components/ProgressStepper/ProgressStep.js +100 -0
  70. package/build/components/ProgressStepper/ProgressStepper.d.ts +6 -0
  71. package/build/components/ProgressStepper/ProgressStepper.js +22 -0
  72. package/build/components/ProgressStepper/ProgressStepper.props.d.ts +22 -0
  73. package/build/components/ProgressStepper/ProgressStepper.props.js +1 -0
  74. package/build/components/ProgressStepper/ProgressStepperRoot.d.ts +6 -0
  75. package/build/components/ProgressStepper/ProgressStepperRoot.js +16 -0
  76. package/build/components/ProgressStepper/index.d.ts +3 -0
  77. package/build/components/ProgressStepper/index.js +2 -0
  78. package/build/components/Radio/RadioTextContent.d.ts +1 -1
  79. package/build/components/Radio/RadioTextContent.js +9 -2
  80. package/build/components/SectionHeader/SectionHeader.d.ts +1 -1
  81. package/build/components/SectionHeader/SectionHeader.js +6 -3
  82. package/build/components/SectionHeader/SectionHeader.props.d.ts +9 -16
  83. package/build/components/SectionHeader/SectionHeaderTrailingContent.d.ts +6 -0
  84. package/build/components/SectionHeader/SectionHeaderTrailingContent.js +13 -0
  85. package/build/components/SectionHeader/index.d.ts +1 -0
  86. package/build/components/SectionHeader/index.js +1 -0
  87. package/build/components/Tabs/Tab.js +2 -2
  88. package/build/components/ToggleButton/ToggleButtonText.js +2 -2
  89. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +4 -1
  90. package/build/components/index.d.ts +3 -0
  91. package/build/components/index.js +3 -0
  92. package/build/core/themes.d.ts +12 -24
  93. package/build/tokens/components/dark/button.d.ts +1 -1
  94. package/build/tokens/components/dark/button.js +1 -1
  95. package/build/tokens/components/dark/dialog.d.ts +1 -0
  96. package/build/tokens/components/dark/dialog.js +1 -0
  97. package/build/tokens/components/dark/illustrations.d.ts +1 -0
  98. package/build/tokens/components/dark/illustrations.js +1 -0
  99. package/build/tokens/components/dark/toast.d.ts +4 -1
  100. package/build/tokens/components/dark/toast.js +4 -1
  101. package/build/tokens/components/light/button.d.ts +1 -1
  102. package/build/tokens/components/light/button.js +1 -1
  103. package/build/tokens/components/light/dialog.d.ts +1 -0
  104. package/build/tokens/components/light/dialog.js +1 -0
  105. package/build/tokens/components/light/illustrations.d.ts +1 -0
  106. package/build/tokens/components/light/illustrations.js +1 -0
  107. package/build/tokens/components/light/toast.d.ts +4 -1
  108. package/build/tokens/components/light/toast.js +4 -1
  109. package/build/tokens/layout.d.ts +6 -12
  110. package/build/tokens/layout.js +3 -6
  111. package/build/utils/getFlattenedColorValue.js +2 -19
  112. package/build/utils/index.d.ts +1 -0
  113. package/build/utils/index.js +1 -0
  114. package/build/utils/styleUtils.d.ts +0 -4
  115. package/build/utils/styleUtils.js +0 -50
  116. package/build/utils/themeValueHelpers.d.ts +17 -0
  117. package/build/utils/themeValueHelpers.js +54 -0
  118. package/docs/components/AllComponents.web.tsx +86 -4
  119. package/docs/components/BadgeList.tsx +20 -56
  120. package/docs/components/SwitchList.tsx +4 -8
  121. package/docs/getting-started.mdx +37 -13
  122. package/docs/introduction.mdx +51 -6
  123. package/package.json +7 -7
  124. package/src/components/Alert/AlertTitle.tsx +7 -7
  125. package/src/components/Badge/Badge.props.ts +1 -0
  126. package/src/components/Badge/Badge.tsx +3 -2
  127. package/src/components/Button/ButtonRoot.tsx +4 -0
  128. package/src/components/Button/ButtonText.tsx +3 -3
  129. package/src/components/Card/CardRoot.tsx +2 -0
  130. package/src/components/Carousel/Carousel.context.tsx +8 -0
  131. package/src/components/Carousel/Carousel.docs.mdx +389 -0
  132. package/src/components/Carousel/Carousel.props.ts +89 -0
  133. package/src/components/Carousel/Carousel.stories.tsx +317 -0
  134. package/src/components/Carousel/Carousel.tsx +444 -0
  135. package/src/components/Carousel/CarouselControlItem.tsx +87 -0
  136. package/src/components/Carousel/CarouselControls.tsx +150 -0
  137. package/src/components/Carousel/CarouselItem.tsx +68 -0
  138. package/src/components/Carousel/index.ts +6 -0
  139. package/src/components/Checkbox/CheckboxTextContent.tsx +11 -3
  140. package/src/components/CurrencyInput/CurrencyInput.docs.mdx +4 -4
  141. package/src/components/CurrencyInput/CurrencyInput.props.ts +2 -2
  142. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +17 -15
  143. package/src/components/CurrencyInput/CurrencyInput.tsx +3 -3
  144. package/src/components/DescriptionList/DescriptionList.docs.mdx +24 -27
  145. package/src/components/DescriptionList/DescriptionList.props.ts +1 -8
  146. package/src/components/DescriptionList/DescriptionList.stories.tsx +13 -19
  147. package/src/components/DescriptionList/DescriptionList.tsx +2 -14
  148. package/src/components/DescriptionList/DescriptionListItem.props.ts +3 -8
  149. package/src/components/DescriptionList/DescriptionListItem.tsx +13 -21
  150. package/src/components/IndicatorIconButton/IndicatorIconButton.docs.mdx +85 -0
  151. package/src/components/IndicatorIconButton/IndicatorIconButton.props.ts +12 -0
  152. package/src/components/IndicatorIconButton/IndicatorIconButton.stories.tsx +142 -0
  153. package/src/components/IndicatorIconButton/IndicatorIconButton.tsx +36 -0
  154. package/src/components/IndicatorIconButton/index.tsx +2 -0
  155. package/src/components/Link/LinkText.tsx +4 -4
  156. package/src/components/List/List.context.ts +0 -1
  157. package/src/components/List/List.docs.mdx +376 -179
  158. package/src/components/List/List.props.ts +1 -9
  159. package/src/components/List/List.stories.tsx +289 -38
  160. package/src/components/List/List.tsx +5 -26
  161. package/src/components/List/ListAction/ListAction.props.ts +10 -0
  162. package/src/components/List/ListAction/ListAction.tsx +133 -0
  163. package/src/components/List/ListAction/ListActionContent.tsx +21 -0
  164. package/src/components/List/ListAction/ListActionText.tsx +14 -0
  165. package/src/components/List/ListAction/ListActionTrailingContent.tsx +9 -0
  166. package/src/components/List/ListAction/ListActionTrailingIcon.tsx +32 -0
  167. package/src/components/List/ListAction/index.ts +6 -0
  168. package/src/components/List/ListItem/ListItem.context.ts +1 -1
  169. package/src/components/List/ListItem/ListItem.props.ts +9 -5
  170. package/src/components/List/ListItem/ListItemRoot.tsx +18 -14
  171. package/src/components/List/ListItem/index.ts +4 -4
  172. package/src/components/List/index.ts +1 -0
  173. package/src/components/ProgressStepper/ProgressStep.tsx +134 -0
  174. package/src/components/ProgressStepper/ProgressStepper.docs.mdx +87 -0
  175. package/src/components/ProgressStepper/ProgressStepper.props.ts +27 -0
  176. package/src/components/ProgressStepper/ProgressStepper.stories.tsx +108 -0
  177. package/src/components/ProgressStepper/ProgressStepper.tsx +26 -0
  178. package/src/components/ProgressStepper/ProgressStepperRoot.tsx +32 -0
  179. package/src/components/ProgressStepper/index.ts +3 -0
  180. package/src/components/Radio/RadioTextContent.tsx +11 -3
  181. package/src/components/SectionHeader/SectionHeader.props.ts +9 -16
  182. package/src/components/SectionHeader/SectionHeader.stories.tsx +28 -18
  183. package/src/components/SectionHeader/SectionHeader.tsx +18 -19
  184. package/src/components/SectionHeader/SectionHeaderTrailingContent.tsx +20 -0
  185. package/src/components/SectionHeader/Sectionheader.docs.mdx +9 -24
  186. package/src/components/SectionHeader/index.ts +1 -0
  187. package/src/components/Switch/Switch.docs.mdx +0 -4
  188. package/src/components/Tabs/Tab.tsx +4 -2
  189. package/src/components/ToggleButton/ToggleButtonText.tsx +3 -3
  190. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  191. package/src/components/index.ts +3 -0
  192. package/src/tokens/components/dark/button.ts +1 -1
  193. package/src/tokens/components/dark/dialog.ts +1 -0
  194. package/src/tokens/components/dark/illustrations.ts +1 -0
  195. package/src/tokens/components/dark/toast.ts +4 -1
  196. package/src/tokens/components/light/button.ts +1 -1
  197. package/src/tokens/components/light/dialog.ts +1 -0
  198. package/src/tokens/components/light/illustrations.ts +1 -0
  199. package/src/tokens/components/light/toast.ts +4 -1
  200. package/src/tokens/layout.ts +3 -6
  201. package/src/utils/getFlattenedColorValue.ts +2 -21
  202. package/src/utils/getStyleValue.ts +0 -3
  203. package/src/utils/index.ts +1 -0
  204. package/src/utils/styleUtils.ts +0 -57
  205. package/src/utils/themeValueHelpers.ts +60 -0
@@ -0,0 +1,68 @@
1
+ import { useEffect } from 'react';
2
+ import { Platform, View } from 'react-native';
3
+ import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
4
+ import { StyleSheet } from 'react-native-unistyles';
5
+
6
+ import { CarouselItemProps } from './Carousel.props';
7
+
8
+ export const CarouselItem = ({
9
+ active,
10
+ children,
11
+ inactiveOpacity = 1,
12
+ style,
13
+ width,
14
+ ...props
15
+ }: CarouselItemProps) => {
16
+ const isWeb = Platform.OS === 'web';
17
+ const opacity = useSharedValue<number>(inactiveOpacity);
18
+
19
+ const animatedStyles = useAnimatedStyle(
20
+ () => ({
21
+ opacity: opacity.value,
22
+ width,
23
+ }),
24
+ [opacity, width]
25
+ );
26
+
27
+ useEffect(() => {
28
+ opacity.value = withTiming(active ? 1 : inactiveOpacity, { duration: 200 });
29
+ }, [active, inactiveOpacity, opacity]);
30
+
31
+ // For web, use a regular View with CSS transitions
32
+ if (isWeb) {
33
+ return (
34
+ <View
35
+ style={[
36
+ styles.container,
37
+ style,
38
+ {
39
+ opacity: active ? 1 : inactiveOpacity,
40
+ width,
41
+ },
42
+ ]}
43
+ {...props}
44
+ >
45
+ {children}
46
+ </View>
47
+ );
48
+ }
49
+
50
+ // For native, use Animated.View with reanimated
51
+ return (
52
+ <Animated.View style={[styles.container, style as false, animatedStyles]} {...props}>
53
+ {children}
54
+ </Animated.View>
55
+ );
56
+ };
57
+
58
+ CarouselItem.displayName = 'CarouselItem';
59
+
60
+ const styles = StyleSheet.create(() => ({
61
+ container: {
62
+ _web: {
63
+ transition: 'opacity 200ms ease-in-out',
64
+ },
65
+ },
66
+ }));
67
+
68
+ export default CarouselItem;
@@ -0,0 +1,6 @@
1
+ export * from './Carousel';
2
+ export * from './Carousel.props';
3
+ export * from './CarouselControls';
4
+ export * from './CarouselItem';
5
+
6
+ export { default as Carousel } from './Carousel';
@@ -1,14 +1,22 @@
1
- import FlexProps from '../Flex/Flex.props';
1
+ import { StyleSheet } from 'react-native-unistyles';
2
2
  import { Flex } from '../Flex';
3
+ import FlexProps from '../Flex/Flex.props';
3
4
 
4
- const CheckboxTextContent = ({ children, ...props }: FlexProps) => {
5
+ const CheckboxTextContent = ({ children, style, ...props }: FlexProps) => {
5
6
  return (
6
- <Flex direction="column" space="none" {...props}>
7
+ <Flex style={[styles.content, style]} direction="column" space="none" {...props}>
7
8
  {children}
8
9
  </Flex>
9
10
  );
10
11
  };
11
12
 
13
+ const styles = StyleSheet.create({
14
+ content: {
15
+ flex: 1,
16
+ flexShrink: 1,
17
+ },
18
+ });
19
+
12
20
  CheckboxTextContent.displayName = 'CheckboxTextContent';
13
21
 
14
22
  export default CheckboxTextContent;
@@ -56,7 +56,7 @@ When using `CurrencyInput`, the component inherits React Native TextInput props
56
56
  | focused | boolean | `false` | Forces the focused visual state |
57
57
  | inBottomSheet | boolean | `false` | Use BottomSheetTextInput when true |
58
58
  | placeholder | string | `'0.00'` | Placeholder text |
59
- | autoFormatThousands | boolean | `false` | Automatically inserts thousand separators while the user types _(Only works with controlled components via onTextChange)_ |
59
+ | disableGroupSeparator | boolean | `false` | Disables automatic inserting of thousand separators while the user types _(Formatting only works with controlled components via onTextChange)_ |
60
60
 
61
61
  Note: When used inside `FormField`, `validationStatus` and `disabled` are read from the context unless explicitly overridden.
62
62
 
@@ -97,12 +97,12 @@ const MyComponent = () => {
97
97
 
98
98
  ## Formatting
99
99
 
100
- Enable automatic thousand separator formatting by setting `autoFormatThousands`.
100
+ Automatic thousand separator formatting can be disabled by setting `disableGroupSeparator`.
101
101
 
102
- <Canvas of={Stories.AutoFormatThousands} />
102
+ <Canvas of={Stories.DisableGroupSeparator} />
103
103
 
104
104
  ```tsx
105
- <CurrencyInput autoFormatThousands value="1234567.89" /> // displays 1,234,567.89
105
+ <CurrencyInput disableGroupSeparator value="1234567.89" /> // displays 1234567.89
106
106
  ```
107
107
 
108
108
  Notes:
@@ -8,8 +8,8 @@ export interface CurrencyInputBaseProps {
8
8
  placeholder?: string;
9
9
  inBottomSheet?: boolean;
10
10
  required?: boolean;
11
- /** When true, automatically formats the numeric value with thousand separators (e.g. 1234 -> 1,234). */
12
- autoFormatThousands?: boolean;
11
+ /** When not specifically disabled, the numeric value is automatically formatted with thousand separators (e.g. 1234 -> 1,234). */
12
+ disableGroupSeparator?: boolean;
13
13
  }
14
14
 
15
15
  export type CurrencyInputProps = CurrencyInputBaseProps &
@@ -37,10 +37,10 @@ const meta = {
37
37
  description: 'Focused',
38
38
  defaultValue: false,
39
39
  },
40
- autoFormatThousands: {
40
+ disableGroupSeparator: {
41
41
  control: 'boolean',
42
42
  description:
43
- 'Automatically add thousand separators while typing _(Only works with controlled components via onTextChange)_',
43
+ 'Disable automatic adding of thousand separators while typing _(Formatting only works with controlled components via onTextChange)_',
44
44
  defaultValue: false,
45
45
  },
46
46
  },
@@ -50,26 +50,28 @@ const meta = {
50
50
  disabled: false,
51
51
  readonly: false,
52
52
  focused: false,
53
- autoFormatThousands: false,
53
+ disableGroupSeparator: false,
54
54
  },
55
55
  } satisfies Meta<typeof CurrencyInput>;
56
56
 
57
57
  export default meta;
58
58
  type Story = StoryObj<typeof meta>;
59
59
 
60
- export const Playground: Story = {};
60
+ export const Playground: Story = {
61
+ render: args => {
62
+ const [value, setValue] = useState('12345.67');
63
+ return <CurrencyInput {...args} value={value} onChangeText={setValue} />;
64
+ },
65
+ };
61
66
 
62
- export const AutoFormatThousands: Story = {
67
+ export const DisableGroupSeparator: Story = {
63
68
  parameters: {
64
- controls: { include: ['autoFormatThousands'] },
69
+ controls: { include: ['disableGroupSeparator'] },
65
70
  },
66
- args: { autoFormatThousands: true },
71
+ args: { disableGroupSeparator: true },
67
72
  render: args => {
68
- const [value, setValue] = useState('1234.56');
69
- const handleChange = (val: string) => {
70
- setValue(val);
71
- };
72
- return <CurrencyInput {...args} value={value} onChangeText={handleChange} />;
73
+ const [value, setValue] = useState('12345.67');
74
+ return <CurrencyInput {...args} value={value} onChangeText={setValue} />;
73
75
  },
74
76
  };
75
77
 
@@ -105,10 +107,10 @@ export const States: Story = {
105
107
  <CurrencyInput disabled />
106
108
  </VariantTitle>
107
109
  <VariantTitle title="Readonly">
108
- <CurrencyInput readonly />
110
+ <CurrencyInput readonly value="11666"/>
109
111
  </VariantTitle>
110
- <VariantTitle title="Auto format thousands">
111
- <CurrencyInput autoFormatThousands value="1234.56" />
112
+ <VariantTitle title="Disable auto format thousands">
113
+ <CurrencyInput disableGroupSeparator value="1234.56" />
112
114
  </VariantTitle>
113
115
  </Flex>
114
116
  );
@@ -14,7 +14,7 @@ const CurrencyInput = ({
14
14
  placeholder,
15
15
  inBottomSheet = false,
16
16
  required,
17
- autoFormatThousands = false,
17
+ disableGroupSeparator = false,
18
18
  value,
19
19
  onChangeText,
20
20
  ...rest
@@ -27,7 +27,7 @@ const CurrencyInput = ({
27
27
  const getPlaceholder = placeholder ?? defaultFormat;
28
28
 
29
29
  const handleChangeText = (text: string) => {
30
- if (autoFormatThousands) {
30
+ if (!disableGroupSeparator) {
31
31
  const formatted = formatThousands(text);
32
32
  onChangeText?.(formatted);
33
33
  } else {
@@ -36,7 +36,7 @@ const CurrencyInput = ({
36
36
  };
37
37
 
38
38
  const displayValue =
39
- autoFormatThousands && typeof value === 'string' ? formatThousands(value) : value;
39
+ !disableGroupSeparator && typeof value === 'string' ? formatThousands(value) : value;
40
40
 
41
41
  return (
42
42
  <Input
@@ -32,7 +32,11 @@ Display pairs of related metadata (heading + description). Supports column (stac
32
32
  >
33
33
  <DescriptionListItem heading="Account Number" description="123456789" />
34
34
  <DescriptionListItem heading="Sort Code" description="12-34-56" />
35
- <DescriptionListItem heading="Status" description="Active" />
35
+ <DescriptionListItem
36
+ heading="Status"
37
+ description="Inactive"
38
+ invalidText="Status cannot be inactive"
39
+ />
36
40
  </DescriptionList>
37
41
  </UsageWrap>
38
42
 
@@ -42,7 +46,11 @@ import { DescriptionList, DescriptionListItem } from '@utilitywarehouse/hearth-r
42
46
  <DescriptionList direction="column" heading="Account details" helperText="Static account metadata">
43
47
  <DescriptionListItem heading="Account Number" description="123456789" />
44
48
  <DescriptionListItem heading="Sort Code" description="12-34-56" />
45
- <DescriptionListItem heading="Status" description="Active" />
49
+ <DescriptionListItem
50
+ heading="Status"
51
+ description="Inactive"
52
+ invalidText="Status cannot be inactive"
53
+ />
46
54
  </DescriptionList>;
47
55
  ```
48
56
 
@@ -50,34 +58,23 @@ import { DescriptionList, DescriptionListItem } from '@utilitywarehouse/hearth-r
50
58
 
51
59
  ### DescriptionList
52
60
 
53
- | Prop | Type | Description | Default |
54
- | ------------------ | -------------------------------------------- | ------------------------------------------------ | ----------- |
55
- | `direction` | `'row' \| 'column'` | Layout orientation | `column` |
56
- | `itemHeadingWidth` | `number` | Override heading column width in row layout | token value |
57
- | `heading` | `string` | Optional overall heading (renders SectionHeader) | - |
58
- | `helperText` | `string` | Supporting text under heading | - |
59
- | `linkText` | `string` | Section header link text | - |
60
- | `linkHref` | `string` | Header link URL (web) | - |
61
- | `linkIcon` | `ComponentType` | Header link icon | - |
62
- | `linkIconPosition` | `'left' \| 'right'` | Header link icon position | `right` |
63
- | `linkOnPress` | `() => void` | Header link press handler | - |
64
- | `linkTarget` | `'_blank' \| '_self' \| '_parent' \| '_top'` | Header link target | `_self` |
65
- | `linkShowIcon` | `boolean` | Whether header link icon is shown | `false` |
61
+ | Prop | Type | Description | Default |
62
+ | ----------------------- | ------------------- | ------------------------------------------------ | ----------- |
63
+ | `direction` | `'row' \| 'column'` | Layout orientation | `column` |
64
+ | `itemHeadingWidth` | `number` | Override heading column width in row layout | token value |
65
+ | `heading` | `string` | Optional overall heading (renders SectionHeader) | - |
66
+ | `helperText` | `string` | Supporting text under heading | - |
67
+ | `headerTrailingContent` | `ReactNode` | Custom trailing content for heading (e.g. Link) | - |
66
68
 
67
69
  ### DescriptionListItem
68
70
 
69
- | Prop | Type | Description | Default |
70
- | ------------------ | -------------------------------------------- | -------------------------------------------- | ---------- |
71
- | `heading` | `ReactNode` | Heading (label) content | (required) |
72
- | `description` | `ReactNode` | Description (value) content | (required) |
73
- | `headingWidth` | `number` | Per-item heading width override (row layout) | inherits |
74
- | `linkText` | `string` | Inline action link text | - |
75
- | `linkHref` | `string` | Inline action link URL (web) | - |
76
- | `linkIcon` | `ComponentType` | Inline action link icon | - |
77
- | `linkIconPosition` | `'left' \| 'right'` | Inline link icon position | `right` |
78
- | `linkOnPress` | `() => void` | Inline link press handler | - |
79
- | `linkTarget` | `'_blank' \| '_self' \| '_parent' \| '_top'` | Inline link target | `_self` |
80
- | `linkShowIcon` | `boolean` | Show inline link icon | `false` |
71
+ | Prop | Type | Description | Default |
72
+ | ----------------- | ----------- | ------------------------------------------------ | ---------- |
73
+ | `heading` | `ReactNode` | Heading (label) content | (required) |
74
+ | `description` | `ReactNode` | Description (value) content | (required) |
75
+ | `headingWidth` | `number` | Per-item heading width override (row layout) | inherits |
76
+ | `trailingContent` | `ReactNode` | Optional trailing content (e.g. Link, Button) | - |
77
+ | `invalidText` | `string` | Text to show under description for invalid value | - |
81
78
 
82
79
  > Uses `theme.components.descriptionList` tokens for spacing & column width.
83
80
 
@@ -1,4 +1,3 @@
1
- import { ComponentType } from 'react';
2
1
  import type { ViewProps } from 'react-native';
3
2
 
4
3
  export interface DescriptionListProps extends ViewProps {
@@ -8,13 +7,7 @@ export interface DescriptionListProps extends ViewProps {
8
7
  itemHeadingWidth?: number;
9
8
  heading?: string;
10
9
  helperText?: string;
11
- linkText?: string;
12
- linkHref?: string;
13
- linkIcon?: ComponentType;
14
- linkIconPosition?: 'left' | 'right';
15
- linkOnPress?: () => void;
16
- linkTarget?: '_blank' | '_self' | '_parent' | '_top';
17
- linkShowIcon?: boolean;
10
+ headerTrailingContent?: React.ReactNode;
18
11
  }
19
12
 
20
13
  export default DescriptionListProps;
@@ -2,6 +2,7 @@ import { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import { DescriptionList, DescriptionListItem } from '.';
3
3
  import { VariantTitle } from '../../../docs/components';
4
4
  import { Flex } from '../Flex';
5
+ import { Link } from '../Link';
5
6
 
6
7
  const meta: Meta<typeof DescriptionList> = {
7
8
  title: 'Stories / DescriptionList',
@@ -26,19 +27,6 @@ const meta: Meta<typeof DescriptionList> = {
26
27
  control: 'text',
27
28
  description: 'Supporting text shown under heading (Playground only).',
28
29
  },
29
- linkText: { control: 'text', description: 'Header link text (Playground only).' },
30
- linkHref: { control: 'text', description: 'Header link href (web).' },
31
- linkIconPosition: {
32
- control: 'select',
33
- options: ['left', 'right'],
34
- description: 'Header link icon position.',
35
- },
36
- linkShowIcon: { control: 'boolean', description: 'Show header link icon.' },
37
- linkTarget: {
38
- control: 'select',
39
- options: ['_blank', '_self', '_parent', '_top'],
40
- description: 'Header link target (web).',
41
- },
42
30
  },
43
31
  args: {
44
32
  direction: 'column',
@@ -53,7 +41,7 @@ type Story = StoryObj<typeof meta>;
53
41
  const sampleData = [
54
42
  { heading: 'Account Number', description: '123456789' },
55
43
  { heading: 'Sort Code', description: '12-34-56' },
56
- { heading: 'Status', description: 'Active' },
44
+ { heading: 'Status', description: 'Inactive', invalidText: 'Status cannot be inactive' },
57
45
  ];
58
46
 
59
47
  export const Playground: Story = {
@@ -64,6 +52,7 @@ export const Playground: Story = {
64
52
  key={item.heading}
65
53
  heading={item.heading}
66
54
  description={item.description}
55
+ invalidText={item.invalidText}
67
56
  />
68
57
  ))}
69
58
  </DescriptionList>
@@ -79,6 +68,7 @@ export const Row: Story = {
79
68
  key={item.heading}
80
69
  heading={item.heading}
81
70
  description={item.description}
71
+ invalidText={item.invalidText}
82
72
  />
83
73
  ))}
84
74
  </DescriptionList>
@@ -94,6 +84,7 @@ export const Column: Story = {
94
84
  key={item.heading}
95
85
  heading={item.heading}
96
86
  description={item.description}
87
+ invalidText={item.invalidText}
97
88
  />
98
89
  ))}
99
90
  </DescriptionList>
@@ -111,6 +102,7 @@ export const KitchenSink: Story = {
111
102
  key={item.heading}
112
103
  heading={item.heading}
113
104
  description={item.description}
105
+ invalidText={item.invalidText}
114
106
  />
115
107
  ))}
116
108
  </DescriptionList>
@@ -122,6 +114,7 @@ export const KitchenSink: Story = {
122
114
  key={item.heading}
123
115
  heading={item.heading}
124
116
  description={item.description}
117
+ invalidText={item.invalidText}
125
118
  />
126
119
  ))}
127
120
  </DescriptionList>
@@ -138,15 +131,16 @@ export const WithLinks: Story = {
138
131
  <DescriptionListItem
139
132
  heading="Account Number"
140
133
  description="123456789"
141
- linkText="Manage"
142
- linkHref="https://example.com/account"
143
- linkShowIcon
134
+ trailingContent={<Link href="https://example.com/account">Manage account</Link>}
144
135
  />
145
136
  <DescriptionListItem
146
137
  heading="Status"
147
138
  description="Active"
148
- linkText="Change"
149
- linkHref="https://example.com/status"
139
+ trailingContent={
140
+ <Link href="https://example.com/status" showIcon={false}>
141
+ Change
142
+ </Link>
143
+ }
150
144
  />
151
145
  <DescriptionListItem heading="Region" description="United Kingdom" />
152
146
  </DescriptionList>
@@ -10,13 +10,7 @@ const DescriptionList = ({
10
10
  itemHeadingWidth,
11
11
  heading,
12
12
  helperText,
13
- linkText,
14
- linkHref,
15
- linkIcon,
16
- linkIconPosition,
17
- linkOnPress,
18
- linkTarget,
19
- linkShowIcon,
13
+ headerTrailingContent,
20
14
  children,
21
15
  style,
22
16
  ...props
@@ -31,13 +25,7 @@ const DescriptionList = ({
31
25
  <SectionHeader
32
26
  heading={heading}
33
27
  helperText={helperText}
34
- linkText={linkText}
35
- linkHref={linkHref}
36
- linkIcon={linkIcon}
37
- linkIconPosition={linkIconPosition}
38
- linkOnPress={linkOnPress}
39
- linkTarget={linkTarget}
40
- linkShowIcon={linkShowIcon}
28
+ trailingContent={headerTrailingContent}
41
29
  />
42
30
  ) : null}
43
31
  {children}
@@ -1,4 +1,4 @@
1
- import type { ComponentType, ReactNode } from 'react';
1
+ import type { ReactNode } from 'react';
2
2
  import type { ViewProps } from 'react-native';
3
3
 
4
4
  export interface DescriptionListItemProps extends ViewProps {
@@ -7,13 +7,8 @@ export interface DescriptionListItemProps extends ViewProps {
7
7
  /** Description / value part */
8
8
  description: ReactNode;
9
9
  headingWidth?: number;
10
- linkText?: string;
11
- linkHref?: string;
12
- linkIcon?: ComponentType;
13
- linkIconPosition?: 'left' | 'right';
14
- linkOnPress?: () => void;
15
- linkTarget?: '_blank' | '_self' | '_parent' | '_top';
16
- linkShowIcon?: boolean;
10
+ trailingContent?: ReactNode;
11
+ invalidText?: string;
17
12
  }
18
13
 
19
14
  export default DescriptionListItemProps;
@@ -1,8 +1,9 @@
1
+ import { ErrorCircleSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
1
2
  import { View } from 'react-native';
2
3
  import { StyleSheet } from 'react-native-unistyles';
3
4
  import { useTheme } from '../../hooks';
4
5
  import { BodyText } from '../BodyText';
5
- import { Link } from '../Link';
6
+ import Helper from '../Helper/Helper';
6
7
  import { useDescriptionListContext } from './DescriptionList.context';
7
8
  import type DescriptionListItemProps from './DescriptionListItem.props';
8
9
 
@@ -10,13 +11,8 @@ const DescriptionListItem = ({
10
11
  heading,
11
12
  description,
12
13
  headingWidth,
13
- linkText,
14
- linkHref,
15
- linkIcon,
16
- linkIconPosition,
17
- linkOnPress,
18
- linkTarget,
19
- linkShowIcon,
14
+ trailingContent,
15
+ invalidText,
20
16
  style,
21
17
  ...props
22
18
  }: DescriptionListItemProps) => {
@@ -46,21 +42,17 @@ const DescriptionListItem = ({
46
42
  </View>
47
43
  <View style={styles.descriptionWrapper}>
48
44
  {descIsText ? <BodyText>{description}</BodyText> : description}
45
+ {!!invalidText && (
46
+ <Helper
47
+ validationStatus="invalid"
48
+ showIcon
49
+ icon={ErrorCircleSmallIcon}
50
+ text={invalidText || ''}
51
+ />
52
+ )}
49
53
  </View>
50
54
  </View>
51
- {linkText ? (
52
- <Link
53
- href={linkHref}
54
- onPress={linkOnPress}
55
- target={linkTarget}
56
- showIcon={linkShowIcon}
57
- icon={linkIcon}
58
- iconPosition={linkIconPosition}
59
- accessibilityRole="link"
60
- >
61
- {linkText}
62
- </Link>
63
- ) : null}
55
+ {trailingContent ? trailingContent : null}
64
56
  </View>
65
57
  );
66
58
  };
@@ -0,0 +1,85 @@
1
+ import { Canvas, Controls, Meta, Story } from '@storybook/addon-docs/blocks';
2
+ import { BellMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
3
+ import { Box, Button, ButtonIcon, ButtonText, Center, IconButton } from '../../';
4
+ import { BackToTopButton, UsageWrap, ViewFigmaButton } from '../../../docs/components';
5
+ import { IndicatorIconButton } from './';
6
+ import * as Stories from './IndicatorIconButton.stories';
7
+
8
+ <Meta title="Components / Indicator Icon Button" />
9
+
10
+ <BackToTopButton />
11
+
12
+ <ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components?node-id=1-162" />
13
+
14
+ # Indicator Icon Button
15
+
16
+ The `IndicatorIconButton` component is used to trigger an action or event, such as opening a dialog or navigating to other screen. It extends the regular `UnstyledIconButton` with an optional red dot indicator in the top-right corner, useful for showing notifications or unread status.
17
+
18
+ - [Playground](#playground)
19
+ - [Usage](#usage)
20
+ - [Props](#props)
21
+ - [Examples](#examples)
22
+
23
+ ## Playground
24
+
25
+ <Canvas of={Stories.Playground} />
26
+
27
+ <Controls of={Stories.Playground} />
28
+
29
+ ## Usage
30
+
31
+ <UsageWrap>
32
+ <Center>
33
+ <IndicatorIconButton
34
+ icon={BellMediumIcon}
35
+ onPress={() => console.log('Button pressed')}
36
+ indicator={true}
37
+ />
38
+ </Center>
39
+ </UsageWrap>
40
+
41
+ ```tsx
42
+ import { IndicatorIconButton } from '@utilitywarehouse/hearth-react-native';
43
+ import { BellMediumIcon } from '@utilitywarehouse/hearth-react-native-icons';
44
+
45
+ const MyComponent = () => {
46
+ const handlePress = () => {
47
+ console.log('Button pressed');
48
+ };
49
+ return <IndicatorIconButton icon={BellMediumIcon} onPress={handlePress} indicator={true} />;
50
+ };
51
+ ```
52
+
53
+ ## Props
54
+
55
+ | Property | Type | Description | Default |
56
+ | ----------- | ------------ | ---------------------------------------------------------- | ------- |
57
+ | `onPress` | `() => void` | Callback function to be called when the button is pressed. | - |
58
+ | `pressed` | `boolean` | Changes the button to a pressed state. | `false` |
59
+ | `indicator` | `boolean` | Shows a red dot indicator in the top-right corner. | `false` |
60
+
61
+ ## Examples
62
+
63
+ ### Variants
64
+
65
+ <Canvas of={Stories.Variants} />
66
+
67
+ ### Colour icons
68
+
69
+ You can pass color as the iconStyle props:
70
+
71
+ ```javascript
72
+ const { color } = useTheme();
73
+
74
+ return (
75
+ <IndicatorIconButton {...props} iconStyle={{ color: color.energyBlue[500] }} />
76
+ )
77
+ ```
78
+
79
+ It does not work on Storybook on web, only mobile.
80
+
81
+ <Canvas of={Stories.Colourful} />
82
+
83
+ ### Adding accessability label
84
+
85
+ <Canvas of={Stories.WithAccessibilityLabel} />
@@ -0,0 +1,12 @@
1
+ import { UnstyledIconButtonProps } from '../UnstyledIconButton';
2
+
3
+ export type IndicatorIconButtonProps = Omit<
4
+ UnstyledIconButtonProps,
5
+ 'size' | 'disabled' | 'loading'
6
+ > & {
7
+ /**
8
+ * If `true`, displays a red dot indicator in the top-right corner.
9
+ * @default false
10
+ */
11
+ indicator?: boolean;
12
+ };