@utilitywarehouse/hearth-react-native 0.2.0 → 0.3.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 (289) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +3 -1
  3. package/CHANGELOG.md +34 -0
  4. package/build/components/Alert/AlertCloseButton.js +25 -3
  5. package/build/components/Alert/AlertIcon.js +17 -1
  6. package/build/components/Alert/AlertIconButton.js +27 -1
  7. package/build/components/Alert/AlertLink.js +47 -1
  8. package/build/components/Alert/AlertText.d.ts +1 -1
  9. package/build/components/Alert/AlertText.js +26 -2
  10. package/build/components/Alert/AlertTitle.d.ts +1 -1
  11. package/build/components/Alert/AlertTitle.js +26 -2
  12. package/build/components/Badge/Badge.js +101 -14
  13. package/build/components/Badge/Badge.props.d.ts +2 -2
  14. package/build/components/Badge/BadgeIcon.js +27 -29
  15. package/build/components/Badge/BadgeText.js +29 -31
  16. package/build/components/Button/Button.d.ts +2 -2
  17. package/build/components/Button/ButtonGroupRoot.d.ts +3 -2
  18. package/build/components/Button/ButtonGroupRoot.js +9 -0
  19. package/build/components/Button/ButtonRoot.js +1 -0
  20. package/build/components/Card/Card.props.d.ts +2 -2
  21. package/build/components/Checkbox/Checkbox.d.ts +2 -2
  22. package/build/components/Checkbox/Checkbox.js +11 -10
  23. package/build/components/Checkbox/Checkbox.props.d.ts +3 -1
  24. package/build/components/Checkbox/CheckboxIcon.js +1 -1
  25. package/build/components/Checkbox/CheckboxImage.d.ts +6 -0
  26. package/build/components/Checkbox/CheckboxImage.js +5 -0
  27. package/build/components/Checkbox/CheckboxTileRoot.js +1 -1
  28. package/build/components/Checkbox/index.d.ts +3 -2
  29. package/build/components/Checkbox/index.js +2 -1
  30. package/build/components/CurrencyInput/CurrencyInput.d.ts +6 -0
  31. package/build/components/CurrencyInput/CurrencyInput.js +47 -0
  32. package/build/components/CurrencyInput/CurrencyInput.props.d.ts +14 -0
  33. package/build/components/CurrencyInput/CurrencyInput.props.js +1 -0
  34. package/build/components/CurrencyInput/index.d.ts +1 -0
  35. package/build/components/CurrencyInput/index.js +1 -0
  36. package/build/components/DescriptionList/DescriptionList.context.d.ts +6 -0
  37. package/build/components/DescriptionList/DescriptionList.context.js +9 -0
  38. package/build/components/DescriptionList/DescriptionList.d.ts +6 -0
  39. package/build/components/DescriptionList/DescriptionList.js +25 -0
  40. package/build/components/DescriptionList/DescriptionList.props.d.ts +18 -0
  41. package/build/components/DescriptionList/DescriptionList.props.js +1 -0
  42. package/build/components/DescriptionList/DescriptionListItem.d.ts +6 -0
  43. package/build/components/DescriptionList/DescriptionListItem.js +49 -0
  44. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +17 -0
  45. package/build/components/DescriptionList/DescriptionListItem.props.js +1 -0
  46. package/build/components/DescriptionList/index.d.ts +4 -0
  47. package/build/components/DescriptionList/index.js +2 -0
  48. package/build/components/Divider/Divider.js +46 -0
  49. package/build/components/Divider/Divider.props.d.ts +2 -2
  50. package/build/components/Flex/Flex.props.d.ts +3 -2
  51. package/build/components/Grid/Grid.props.d.ts +2 -2
  52. package/build/components/IconContainer/IconContainer.d.ts +5 -0
  53. package/build/components/IconContainer/IconContainer.js +161 -0
  54. package/build/components/IconContainer/IconContainer.props.d.ts +15 -0
  55. package/build/components/IconContainer/IconContainer.props.js +1 -0
  56. package/build/components/IconContainer/index.d.ts +2 -0
  57. package/build/components/IconContainer/index.js +1 -0
  58. package/build/components/Icons/CircleIcon.js +3 -3
  59. package/build/components/Input/Input.js +2 -34
  60. package/build/components/Input/Input.props.d.ts +1 -17
  61. package/build/components/Input/InputField.js +0 -7
  62. package/build/components/Link/Link.d.ts +1 -1
  63. package/build/components/Link/Link.js +4 -4
  64. package/build/components/Link/Link.props.d.ts +3 -0
  65. package/build/components/Modal/Modal.js +17 -1
  66. package/build/components/Radio/Radio.d.ts +2 -2
  67. package/build/components/Radio/Radio.js +9 -8
  68. package/build/components/Radio/Radio.props.d.ts +3 -1
  69. package/build/components/Radio/RadioImage.d.ts +6 -0
  70. package/build/components/Radio/RadioImage.js +5 -0
  71. package/build/components/Radio/RadioTileRoot.js +1 -1
  72. package/build/components/Radio/index.d.ts +3 -2
  73. package/build/components/Radio/index.js +2 -1
  74. package/build/components/SectionHeader/SectionHeader.js +1 -0
  75. package/build/components/Select/SelectOption.js +1 -7
  76. package/build/components/Tabs/Tab.d.ts +18 -0
  77. package/build/components/Tabs/Tab.js +74 -0
  78. package/build/components/Tabs/Tab.props.d.ts +14 -0
  79. package/build/components/Tabs/Tab.props.js +1 -0
  80. package/build/components/Tabs/TabPanel.d.ts +3 -0
  81. package/build/components/Tabs/TabPanel.js +34 -0
  82. package/build/components/Tabs/TabPanel.props.d.ts +8 -0
  83. package/build/components/Tabs/TabPanel.props.js +1 -0
  84. package/build/components/Tabs/Tabs.context.d.ts +23 -0
  85. package/build/components/Tabs/Tabs.context.js +8 -0
  86. package/build/components/Tabs/Tabs.d.ts +6 -0
  87. package/build/components/Tabs/Tabs.js +114 -0
  88. package/build/components/Tabs/Tabs.props.d.ts +19 -0
  89. package/build/components/Tabs/Tabs.props.js +1 -0
  90. package/build/components/Tabs/TabsList.d.ts +6 -0
  91. package/build/components/Tabs/TabsList.js +112 -0
  92. package/build/components/Tabs/TabsList.props.d.ts +6 -0
  93. package/build/components/Tabs/TabsList.props.js +1 -0
  94. package/build/components/Tabs/index.d.ts +8 -0
  95. package/build/components/Tabs/index.js +4 -0
  96. package/build/components/UnstyledIconButton/UnstyledIconButton.d.ts +1 -1
  97. package/build/components/UnstyledIconButton/UnstyledIconButton.js +4 -4
  98. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +2 -1
  99. package/build/components/index.d.ts +4 -0
  100. package/build/components/index.js +4 -0
  101. package/build/core/themes.d.ts +428 -160
  102. package/build/core/themes.js +57 -1
  103. package/build/tokens/color.d.ts +88 -80
  104. package/build/tokens/color.js +44 -40
  105. package/build/tokens/components/dark/alert.d.ts +13 -0
  106. package/build/tokens/components/dark/alert.js +13 -0
  107. package/build/tokens/components/dark/button.d.ts +1 -0
  108. package/build/tokens/components/dark/button.js +1 -0
  109. package/build/tokens/components/dark/checkbox.d.ts +4 -1
  110. package/build/tokens/components/dark/checkbox.js +4 -1
  111. package/build/tokens/components/dark/icon-button.d.ts +10 -3
  112. package/build/tokens/components/dark/icon-button.js +10 -3
  113. package/build/tokens/components/dark/index.d.ts +1 -0
  114. package/build/tokens/components/dark/index.js +1 -0
  115. package/build/tokens/components/dark/link.d.ts +5 -0
  116. package/build/tokens/components/dark/link.js +5 -0
  117. package/build/tokens/components/dark/progress-bar.d.ts +41 -0
  118. package/build/tokens/components/dark/progress-bar.js +40 -0
  119. package/build/tokens/components/dark/radio.d.ts +1 -1
  120. package/build/tokens/components/dark/radio.js +1 -1
  121. package/build/tokens/components/dark/tabs.d.ts +2 -0
  122. package/build/tokens/components/dark/tabs.js +2 -0
  123. package/build/tokens/components/light/alert.d.ts +13 -0
  124. package/build/tokens/components/light/alert.js +13 -0
  125. package/build/tokens/components/light/badge.d.ts +1 -1
  126. package/build/tokens/components/light/badge.js +1 -1
  127. package/build/tokens/components/light/button.d.ts +1 -0
  128. package/build/tokens/components/light/button.js +1 -0
  129. package/build/tokens/components/light/checkbox.d.ts +6 -3
  130. package/build/tokens/components/light/checkbox.js +6 -3
  131. package/build/tokens/components/light/icon-button.d.ts +8 -1
  132. package/build/tokens/components/light/icon-button.js +8 -1
  133. package/build/tokens/components/light/index.d.ts +1 -0
  134. package/build/tokens/components/light/index.js +1 -0
  135. package/build/tokens/components/light/link.d.ts +5 -0
  136. package/build/tokens/components/light/link.js +5 -0
  137. package/build/tokens/components/light/progress-bar.d.ts +41 -0
  138. package/build/tokens/components/light/progress-bar.js +40 -0
  139. package/build/tokens/components/light/radio.d.ts +3 -3
  140. package/build/tokens/components/light/radio.js +3 -3
  141. package/build/tokens/components/light/tabs.d.ts +2 -0
  142. package/build/tokens/components/light/tabs.js +2 -0
  143. package/build/tokens/index.d.ts +1 -0
  144. package/build/tokens/index.js +1 -0
  145. package/build/tokens/layout.d.ts +48 -30
  146. package/build/tokens/layout.js +24 -15
  147. package/build/tokens/motion.d.ts +23 -0
  148. package/build/tokens/motion.js +22 -0
  149. package/build/tokens/primitive.d.ts +19 -0
  150. package/build/tokens/primitive.js +19 -0
  151. package/build/tokens/semantic-dark.d.ts +26 -24
  152. package/build/tokens/semantic-dark.js +26 -24
  153. package/build/tokens/semantic-light.d.ts +18 -16
  154. package/build/tokens/semantic-light.js +18 -16
  155. package/build/types/values.d.ts +2 -1
  156. package/build/utils/formatThousands.d.ts +2 -0
  157. package/build/utils/formatThousands.js +16 -0
  158. package/build/utils/index.d.ts +1 -0
  159. package/build/utils/index.js +1 -0
  160. package/docs/assets/bank-logo.png +0 -0
  161. package/docs/assets/bank-logo1.png +0 -0
  162. package/docs/components/AllComponents.web.tsx +97 -8
  163. package/docs/components/NextPrevPage.tsx +11 -3
  164. package/docs/components/UsageWrap.tsx +2 -2
  165. package/docs/components/index.ts +6 -7
  166. package/docs/heplers/addReactNativePrefix.ts +8 -0
  167. package/docs/heplers/index.ts +1 -0
  168. package/docs/introduction.mdx +3 -3
  169. package/docs/theme-tokens.mdx +42 -0
  170. package/package.json +13 -13
  171. package/src/components/Alert/AlertCloseButton.tsx +33 -5
  172. package/src/components/Alert/AlertIcon.tsx +17 -1
  173. package/src/components/Alert/AlertIconButton.tsx +37 -4
  174. package/src/components/Alert/AlertLink.tsx +52 -1
  175. package/src/components/Alert/AlertText.tsx +28 -3
  176. package/src/components/Alert/AlertTitle.tsx +28 -3
  177. package/src/components/Badge/Badge.docs.mdx +7 -7
  178. package/src/components/Badge/Badge.props.ts +3 -2
  179. package/src/components/Badge/Badge.stories.tsx +81 -92
  180. package/src/components/Badge/Badge.tsx +101 -14
  181. package/src/components/Badge/BadgeIcon.tsx +27 -29
  182. package/src/components/Badge/BadgeText.tsx +29 -31
  183. package/src/components/Button/ButtonGroupRoot.tsx +12 -2
  184. package/src/components/Button/ButtonRoot.tsx +1 -0
  185. package/src/components/Card/Card.docs.mdx +1 -1
  186. package/src/components/Card/Card.props.ts +2 -2
  187. package/src/components/Checkbox/Checkbox.docs.mdx +45 -7
  188. package/src/components/Checkbox/Checkbox.props.ts +3 -1
  189. package/src/components/Checkbox/Checkbox.stories.tsx +37 -1
  190. package/src/components/Checkbox/Checkbox.tsx +12 -9
  191. package/src/components/Checkbox/CheckboxIcon.tsx +1 -1
  192. package/src/components/Checkbox/CheckboxImage.tsx +9 -0
  193. package/src/components/Checkbox/CheckboxTileRoot.tsx +1 -1
  194. package/src/components/Checkbox/index.ts +3 -2
  195. package/src/components/CurrencyInput/CurrencyInput.docs.mdx +120 -0
  196. package/src/components/CurrencyInput/CurrencyInput.props.ts +19 -0
  197. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +116 -0
  198. package/src/components/CurrencyInput/CurrencyInput.tsx +91 -0
  199. package/src/components/CurrencyInput/index.ts +1 -0
  200. package/src/components/DescriptionList/DescriptionList.context.ts +18 -0
  201. package/src/components/DescriptionList/DescriptionList.docs.mdx +98 -0
  202. package/src/components/DescriptionList/DescriptionList.props.ts +20 -0
  203. package/src/components/DescriptionList/DescriptionList.stories.tsx +154 -0
  204. package/src/components/DescriptionList/DescriptionList.tsx +64 -0
  205. package/src/components/DescriptionList/DescriptionListItem.props.ts +19 -0
  206. package/src/components/DescriptionList/DescriptionListItem.tsx +101 -0
  207. package/src/components/DescriptionList/index.ts +4 -0
  208. package/src/components/Divider/Divider.props.ts +2 -2
  209. package/src/components/Divider/Divider.stories.tsx +3 -3
  210. package/src/components/Divider/Divider.tsx +46 -0
  211. package/src/components/Flex/Flex.docs.mdx +4 -4
  212. package/src/components/Flex/Flex.props.ts +3 -2
  213. package/src/components/Flex/Flex.stories.tsx +1 -1
  214. package/src/components/Grid/Grid.docs.mdx +12 -12
  215. package/src/components/Grid/Grid.props.ts +2 -2
  216. package/src/components/Grid/Grid.stories.tsx +2 -2
  217. package/src/components/IconContainer/IconContainer.docs.mdx +90 -0
  218. package/src/components/IconContainer/IconContainer.props.ts +17 -0
  219. package/src/components/IconContainer/IconContainer.stories.tsx +130 -0
  220. package/src/components/IconContainer/IconContainer.tsx +180 -0
  221. package/src/components/IconContainer/index.tsx +2 -0
  222. package/src/components/Icons/CircleIcon.tsx +9 -11
  223. package/src/components/Input/Input.docs.mdx +3 -3
  224. package/src/components/Input/Input.props.ts +0 -20
  225. package/src/components/Input/Input.stories.tsx +0 -6
  226. package/src/components/Input/Input.tsx +2 -49
  227. package/src/components/Input/InputField.tsx +0 -7
  228. package/src/components/Link/Link.props.ts +3 -0
  229. package/src/components/Link/Link.tsx +12 -6
  230. package/src/components/List/List.docs.mdx +24 -23
  231. package/src/components/Modal/Modal.tsx +18 -0
  232. package/src/components/Radio/Radio.docs.mdx +96 -124
  233. package/src/components/Radio/Radio.props.ts +3 -1
  234. package/src/components/Radio/Radio.stories.tsx +47 -0
  235. package/src/components/Radio/Radio.tsx +10 -7
  236. package/src/components/Radio/RadioImage.tsx +9 -0
  237. package/src/components/Radio/RadioTileRoot.tsx +1 -1
  238. package/src/components/Radio/index.ts +3 -2
  239. package/src/components/SectionHeader/SectionHeader.tsx +1 -0
  240. package/src/components/Select/Select.docs.mdx +6 -6
  241. package/src/components/Select/Select.stories.tsx +7 -7
  242. package/src/components/Select/SelectOption.tsx +4 -10
  243. package/src/components/Tabs/Tab.props.ts +16 -0
  244. package/src/components/Tabs/Tab.tsx +113 -0
  245. package/src/components/Tabs/TabPanel.props.ts +10 -0
  246. package/src/components/Tabs/TabPanel.tsx +46 -0
  247. package/src/components/Tabs/Tabs.context.ts +26 -0
  248. package/src/components/Tabs/Tabs.docs.mdx +214 -0
  249. package/src/components/Tabs/Tabs.props.ts +21 -0
  250. package/src/components/Tabs/Tabs.stories.tsx +270 -0
  251. package/src/components/Tabs/Tabs.tsx +139 -0
  252. package/src/components/Tabs/TabsList.props.ts +8 -0
  253. package/src/components/Tabs/TabsList.tsx +194 -0
  254. package/src/components/Tabs/index.ts +8 -0
  255. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  256. package/src/components/UnstyledIconButton/UnstyledIconButton.tsx +9 -3
  257. package/src/components/index.ts +4 -0
  258. package/src/core/themes.ts +57 -1
  259. package/src/tokens/color.ts +44 -40
  260. package/src/tokens/components/dark/alert.ts +13 -0
  261. package/src/tokens/components/dark/button.ts +1 -0
  262. package/src/tokens/components/dark/checkbox.ts +4 -1
  263. package/src/tokens/components/dark/icon-button.ts +10 -3
  264. package/src/tokens/components/dark/index.ts +1 -0
  265. package/src/tokens/components/dark/link.ts +5 -0
  266. package/src/tokens/components/dark/progress-bar.ts +41 -0
  267. package/src/tokens/components/dark/radio.ts +1 -1
  268. package/src/tokens/components/dark/tabs.ts +2 -0
  269. package/src/tokens/components/light/alert.ts +13 -0
  270. package/src/tokens/components/light/badge.ts +1 -1
  271. package/src/tokens/components/light/button.ts +1 -0
  272. package/src/tokens/components/light/checkbox.ts +6 -3
  273. package/src/tokens/components/light/icon-button.ts +8 -1
  274. package/src/tokens/components/light/index.ts +1 -0
  275. package/src/tokens/components/light/link.ts +5 -0
  276. package/src/tokens/components/light/progress-bar.ts +41 -0
  277. package/src/tokens/components/light/radio.ts +3 -3
  278. package/src/tokens/components/light/tabs.ts +2 -0
  279. package/src/tokens/index.ts +1 -0
  280. package/src/tokens/layout.ts +24 -15
  281. package/src/tokens/motion.ts +23 -0
  282. package/src/tokens/primitive.ts +19 -0
  283. package/src/tokens/semantic-dark.ts +26 -24
  284. package/src/tokens/semantic-light.ts +18 -16
  285. package/src/types/values.ts +3 -1
  286. package/src/utils/formatThousands.ts +14 -0
  287. package/src/utils/index.ts +1 -0
  288. package/docs/assets/react-native-pig.png +0 -0
  289. package/docs/components/AdvancedRadioExample.tsx +0 -126
@@ -0,0 +1,116 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { useState } from 'react';
3
+ import { CurrencyInput } from '..';
4
+ import { VariantTitle } from '../../../docs/components';
5
+ import { Flex } from '../Flex';
6
+
7
+ const meta = {
8
+ title: 'Stories / CurrencyInput',
9
+ component: CurrencyInput,
10
+ parameters: {
11
+ layout: 'centered',
12
+ },
13
+ argTypes: {
14
+ placeholder: {
15
+ control: 'text',
16
+ description: 'The CurrencyInput field placeholder',
17
+ defaultValue: '0.00',
18
+ },
19
+ validationStatus: {
20
+ control: 'select',
21
+ options: ['initial', 'valid', 'invalid'],
22
+ description: 'The validation status',
23
+ defaultValue: 'initial',
24
+ },
25
+ disabled: {
26
+ control: 'boolean',
27
+ description: 'Disable the input',
28
+ defaultValue: false,
29
+ },
30
+ readonly: {
31
+ control: 'boolean',
32
+ description: 'Read only',
33
+ defaultValue: false,
34
+ },
35
+ focused: {
36
+ control: 'boolean',
37
+ description: 'Focused',
38
+ defaultValue: false,
39
+ },
40
+ autoFormatThousands: {
41
+ control: 'boolean',
42
+ description:
43
+ 'Automatically add thousand separators while typing _(Only works with controlled components via onTextChange)_',
44
+ defaultValue: false,
45
+ },
46
+ },
47
+ args: {
48
+ placeholder: '0.00',
49
+ validationStatus: 'initial',
50
+ disabled: false,
51
+ readonly: false,
52
+ focused: false,
53
+ autoFormatThousands: false,
54
+ },
55
+ } satisfies Meta<typeof CurrencyInput>;
56
+
57
+ export default meta;
58
+ type Story = StoryObj<typeof meta>;
59
+
60
+ export const Playground: Story = {};
61
+
62
+ export const AutoFormatThousands: Story = {
63
+ parameters: {
64
+ controls: { include: ['autoFormatThousands'] },
65
+ },
66
+ args: { autoFormatThousands: true },
67
+ 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
+ },
74
+ };
75
+
76
+ export const States: Story = {
77
+ parameters: {
78
+ controls: { include: [] },
79
+ },
80
+ render: () => {
81
+ return (
82
+ <Flex direction="column" space="lg">
83
+ <VariantTitle title="Default">
84
+ <CurrencyInput />
85
+ </VariantTitle>
86
+ <VariantTitle title="With placeholder">
87
+ <CurrencyInput placeholder="0.00" />
88
+ </VariantTitle>
89
+ <VariantTitle title="Focused">
90
+ <CurrencyInput focused value="12.34" />
91
+ </VariantTitle>
92
+ <VariantTitle title="Valid">
93
+ <CurrencyInput validationStatus="valid" />
94
+ </VariantTitle>
95
+ <VariantTitle title="Invalid">
96
+ <CurrencyInput validationStatus="invalid" />
97
+ </VariantTitle>
98
+ <VariantTitle title="Valid - Focused">
99
+ <CurrencyInput validationStatus="valid" focused />
100
+ </VariantTitle>
101
+ <VariantTitle title="Invalid - Focused">
102
+ <CurrencyInput validationStatus="invalid" focused />
103
+ </VariantTitle>
104
+ <VariantTitle title="Disabled">
105
+ <CurrencyInput disabled />
106
+ </VariantTitle>
107
+ <VariantTitle title="Readonly">
108
+ <CurrencyInput readonly />
109
+ </VariantTitle>
110
+ <VariantTitle title="Auto format thousands">
111
+ <CurrencyInput autoFormatThousands value="1234.56" />
112
+ </VariantTitle>
113
+ </Flex>
114
+ );
115
+ },
116
+ };
@@ -0,0 +1,91 @@
1
+ import { Platform } from 'react-native';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
+ import { formatThousands } from '../../utils';
4
+ import { DetailText } from '../DetailText';
5
+ import { useFormFieldContext } from '../FormField';
6
+ import { Input, InputField, InputSlot } from '../Input';
7
+ import type CurrencyInputProps from './CurrencyInput.props';
8
+
9
+ const CurrencyInput = ({
10
+ validationStatus = 'initial',
11
+ disabled,
12
+ focused,
13
+ readonly,
14
+ placeholder,
15
+ inBottomSheet = false,
16
+ required,
17
+ autoFormatThousands = false,
18
+ value,
19
+ onChangeText,
20
+ ...rest
21
+ }: CurrencyInputProps) => {
22
+ const formFieldContext = useFormFieldContext();
23
+ const { disabled: formFieldDisabled } = formFieldContext;
24
+ const validationStatusFromContext = formFieldContext?.validationStatus ?? validationStatus;
25
+
26
+ const defaultFormat = '0.00';
27
+ const getPlaceholder = placeholder ?? defaultFormat;
28
+
29
+ const handleChangeText = (text: string) => {
30
+ if (autoFormatThousands) {
31
+ const formatted = formatThousands(text);
32
+ onChangeText?.(formatted);
33
+ } else {
34
+ onChangeText?.(text);
35
+ }
36
+ };
37
+
38
+ const displayValue =
39
+ autoFormatThousands && typeof value === 'string' ? formatThousands(value) : value;
40
+
41
+ return (
42
+ <Input
43
+ validationStatus={validationStatusFromContext}
44
+ disabled={formFieldDisabled ?? disabled}
45
+ readonly={readonly}
46
+ focused={focused}
47
+ style={styles.wrap}
48
+ >
49
+ <InputSlot>
50
+ <DetailText size="4xl" style={styles.text}>
51
+ £
52
+ </DetailText>
53
+ </InputSlot>
54
+ <InputField
55
+ inputMode="decimal"
56
+ inBottomSheet={inBottomSheet}
57
+ {...rest}
58
+ placeholder={getPlaceholder}
59
+ keyboardType="decimal-pad"
60
+ style={styles.input}
61
+ value={displayValue as any}
62
+ onChangeText={handleChangeText}
63
+ />
64
+ </Input>
65
+ );
66
+ };
67
+
68
+ CurrencyInput.displayName = 'CurrencyInput';
69
+
70
+ const styles = StyleSheet.create(theme => ({
71
+ wrap: {
72
+ height: theme.components.input.currency.height,
73
+ gap: theme.components.input.currency.gap,
74
+ },
75
+ text: {
76
+ ...(Platform.OS === 'ios' && { lineHeight: 46 }),
77
+ _web: {
78
+ marginTop: 1,
79
+ },
80
+ },
81
+ input: {
82
+ fontSize: theme.typography.mobile.detailText['4xl'].fontSize,
83
+ fontFamily: theme.typography.mobile.detailText.fontFamily,
84
+ fontWeight: theme.typography.mobile.detailText.fontWeight,
85
+ paddingTop: 0,
86
+ paddingBottom: 0,
87
+ paddingLeft: 0,
88
+ },
89
+ }));
90
+
91
+ export default CurrencyInput;
@@ -0,0 +1 @@
1
+ export { default as CurrencyInput } from './CurrencyInput';
@@ -0,0 +1,18 @@
1
+ import { createContext, useContext } from 'react';
2
+
3
+ export interface DescriptionListContextValue {
4
+ direction: 'row' | 'column';
5
+ itemHeadingWidth?: number;
6
+ }
7
+
8
+ export const DescriptionListContext = createContext<DescriptionListContextValue | undefined>(
9
+ undefined
10
+ );
11
+
12
+ export const useDescriptionListContext = () => {
13
+ const ctx = useContext(DescriptionListContext);
14
+ if (!ctx) {
15
+ throw new Error('DescriptionListItem must be used within a DescriptionList');
16
+ }
17
+ return ctx;
18
+ };
@@ -0,0 +1,98 @@
1
+ import { Canvas, Controls, Meta } from '@storybook/addon-docs/blocks';
2
+ import { DescriptionList, DescriptionListItem } from '../../';
3
+ import { BackToTopButton, UsageWrap } from '../../../docs/components';
4
+ import * as Stories from './DescriptionList.stories';
5
+
6
+ <BackToTopButton />
7
+
8
+ <Meta title="Components / Description List" />
9
+
10
+ # Description List
11
+
12
+ Display pairs of related metadata (heading + description). Supports column (stacked) and row (two-column) layouts, optional SectionHeader (heading + helper text), and per-item action links.
13
+
14
+ - [Playground](#playground)
15
+ - [Usage](#usage)
16
+ - [Props](#props)
17
+ - [Variants](#variants)
18
+ - [Accessibility](#accessibility)
19
+
20
+ ## Playground
21
+
22
+ <Canvas of={Stories.Playground} />
23
+ <Controls of={Stories.Playground} />
24
+
25
+ ## Usage
26
+
27
+ <UsageWrap>
28
+ <DescriptionList
29
+ direction="column"
30
+ heading="Account details"
31
+ helperText="Static account metadata"
32
+ >
33
+ <DescriptionListItem heading="Account Number" description="123456789" />
34
+ <DescriptionListItem heading="Sort Code" description="12-34-56" />
35
+ <DescriptionListItem heading="Status" description="Active" />
36
+ </DescriptionList>
37
+ </UsageWrap>
38
+
39
+ ```tsx
40
+ import { DescriptionList, DescriptionListItem } from '@utilitywarehouse/hearth-react-native';
41
+
42
+ <DescriptionList direction="column" heading="Account details" helperText="Static account metadata">
43
+ <DescriptionListItem heading="Account Number" description="123456789" />
44
+ <DescriptionListItem heading="Sort Code" description="12-34-56" />
45
+ <DescriptionListItem heading="Status" description="Active" />
46
+ </DescriptionList>;
47
+ ```
48
+
49
+ ## Props
50
+
51
+ ### DescriptionList
52
+
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` |
66
+
67
+ ### DescriptionListItem
68
+
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` |
81
+
82
+ > Uses `theme.components.descriptionList` tokens for spacing & column width.
83
+
84
+ ## Variants
85
+
86
+ <Canvas of={Stories.KitchenSink} />
87
+ <Canvas of={Stories.WithLinks} />
88
+
89
+ ## Accessibility
90
+
91
+ The component applies the following roles:
92
+
93
+ | Element | Role |
94
+ | ----------------------------- | ------------------------------------- |
95
+ | `DescriptionList` root | `list` |
96
+ | `DescriptionListItem` wrapper | `text` (combined label when possible) |
97
+
98
+ When both heading and description are plain text they are merged into one accessibility node (e.g. “Account Number: 123456789”) and the child elements are hidden from the a11y tree to avoid duplicate reading (TalkBack / VoiceOver). If either part is non‑text the children remain individually exposed.
@@ -0,0 +1,20 @@
1
+ import { ComponentType } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+
4
+ export interface DescriptionListProps extends ViewProps {
5
+ /** Direction orientation for items */
6
+ direction?: 'row' | 'column';
7
+ /** Override heading/term column width when layout is row (defaults to token) */
8
+ itemHeadingWidth?: number;
9
+ heading?: string;
10
+ 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;
18
+ }
19
+
20
+ export default DescriptionListProps;
@@ -0,0 +1,154 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { DescriptionList, DescriptionListItem } from '.';
3
+ import { VariantTitle } from '../../../docs/components';
4
+ import { Flex } from '../Flex';
5
+
6
+ const meta: Meta<typeof DescriptionList> = {
7
+ title: 'Stories / DescriptionList',
8
+ component: DescriptionList,
9
+ parameters: { layout: 'centered' },
10
+ argTypes: {
11
+ direction: {
12
+ control: 'select',
13
+ options: ['row', 'column'],
14
+ description: 'Orientation of heading & description pairs',
15
+ },
16
+ itemHeadingWidth: {
17
+ control: 'number',
18
+ description: 'Override width (px) for heading column when direction is row',
19
+ },
20
+ heading: {
21
+ control: 'text',
22
+ description:
23
+ 'Optional overall heading (renders a SectionHeader). Playground only – prefer SectionHeader directly when composing.',
24
+ },
25
+ helperText: {
26
+ control: 'text',
27
+ description: 'Supporting text shown under heading (Playground only).',
28
+ },
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
+ },
43
+ args: {
44
+ direction: 'column',
45
+ heading: 'Account details',
46
+ helperText: 'Static account metadata',
47
+ },
48
+ };
49
+
50
+ export default meta;
51
+ type Story = StoryObj<typeof meta>;
52
+
53
+ const sampleData = [
54
+ { heading: 'Account Number', description: '123456789' },
55
+ { heading: 'Sort Code', description: '12-34-56' },
56
+ { heading: 'Status', description: 'Active' },
57
+ ];
58
+
59
+ export const Playground: Story = {
60
+ render: args => (
61
+ <DescriptionList {...args}>
62
+ {sampleData.map(item => (
63
+ <DescriptionListItem
64
+ key={item.heading}
65
+ heading={item.heading}
66
+ description={item.description}
67
+ />
68
+ ))}
69
+ </DescriptionList>
70
+ ),
71
+ };
72
+
73
+ export const Row: Story = {
74
+ args: { direction: 'row' },
75
+ render: args => (
76
+ <DescriptionList {...args}>
77
+ {sampleData.map(item => (
78
+ <DescriptionListItem
79
+ key={item.heading}
80
+ heading={item.heading}
81
+ description={item.description}
82
+ />
83
+ ))}
84
+ </DescriptionList>
85
+ ),
86
+ };
87
+
88
+ export const Column: Story = {
89
+ args: { direction: 'column' },
90
+ render: args => (
91
+ <DescriptionList {...args}>
92
+ {sampleData.map(item => (
93
+ <DescriptionListItem
94
+ key={item.heading}
95
+ heading={item.heading}
96
+ description={item.description}
97
+ />
98
+ ))}
99
+ </DescriptionList>
100
+ ),
101
+ };
102
+
103
+ export const KitchenSink: Story = {
104
+ parameters: { controls: { include: [] } },
105
+ render: () => (
106
+ <Flex direction="column" space="lg" style={{ width: '100%' }}>
107
+ <VariantTitle title="Row direction">
108
+ <DescriptionList direction="row">
109
+ {sampleData.map(item => (
110
+ <DescriptionListItem
111
+ key={item.heading}
112
+ heading={item.heading}
113
+ description={item.description}
114
+ />
115
+ ))}
116
+ </DescriptionList>
117
+ </VariantTitle>
118
+ <VariantTitle title="Column direction">
119
+ <DescriptionList direction="column">
120
+ {sampleData.map(item => (
121
+ <DescriptionListItem
122
+ key={item.heading}
123
+ heading={item.heading}
124
+ description={item.description}
125
+ />
126
+ ))}
127
+ </DescriptionList>
128
+ </VariantTitle>
129
+ </Flex>
130
+ ),
131
+ };
132
+
133
+ export const WithLinks: Story = {
134
+ parameters: { controls: { include: ['direction', 'itemHeadingWidth'] } },
135
+ args: { direction: 'row' },
136
+ render: args => (
137
+ <DescriptionList {...args}>
138
+ <DescriptionListItem
139
+ heading="Account Number"
140
+ description="123456789"
141
+ linkText="Manage"
142
+ linkHref="https://example.com/account"
143
+ linkShowIcon
144
+ />
145
+ <DescriptionListItem
146
+ heading="Status"
147
+ description="Active"
148
+ linkText="Change"
149
+ linkHref="https://example.com/status"
150
+ />
151
+ <DescriptionListItem heading="Region" description="United Kingdom" />
152
+ </DescriptionList>
153
+ ),
154
+ };
@@ -0,0 +1,64 @@
1
+ import { useMemo } from 'react';
2
+ import { View } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { SectionHeader } from '../SectionHeader';
5
+ import { DescriptionListContext } from './DescriptionList.context';
6
+ import type DescriptionListProps from './DescriptionList.props';
7
+
8
+ const DescriptionList = ({
9
+ direction = 'column',
10
+ itemHeadingWidth,
11
+ heading,
12
+ helperText,
13
+ linkText,
14
+ linkHref,
15
+ linkIcon,
16
+ linkIconPosition,
17
+ linkOnPress,
18
+ linkTarget,
19
+ linkShowIcon,
20
+ children,
21
+ style,
22
+ ...props
23
+ }: DescriptionListProps) => {
24
+ styles.useVariants({ direction });
25
+ const value = useMemo(() => ({ direction, itemHeadingWidth }), [direction, itemHeadingWidth]);
26
+
27
+ return (
28
+ <DescriptionListContext.Provider value={value}>
29
+ <View accessibilityRole="list" {...props} style={[styles.container, style]}>
30
+ {heading ? (
31
+ <SectionHeader
32
+ heading={heading}
33
+ helperText={helperText}
34
+ linkText={linkText}
35
+ linkHref={linkHref}
36
+ linkIcon={linkIcon}
37
+ linkIconPosition={linkIconPosition}
38
+ linkOnPress={linkOnPress}
39
+ linkTarget={linkTarget}
40
+ linkShowIcon={linkShowIcon}
41
+ />
42
+ ) : null}
43
+ {children}
44
+ </View>
45
+ </DescriptionListContext.Provider>
46
+ );
47
+ };
48
+
49
+ DescriptionList.displayName = 'DescriptionList';
50
+
51
+ const styles = StyleSheet.create(theme => ({
52
+ container: {
53
+ width: theme.space.full,
54
+ gap: theme.components.descriptionList.gap,
55
+ variants: {
56
+ direction: {
57
+ row: {},
58
+ column: {},
59
+ },
60
+ },
61
+ },
62
+ }));
63
+
64
+ export default DescriptionList;
@@ -0,0 +1,19 @@
1
+ import type { ComponentType, ReactNode } from 'react';
2
+ import type { ViewProps } from 'react-native';
3
+
4
+ export interface DescriptionListItemProps extends ViewProps {
5
+ /** Heading / label part */
6
+ heading: ReactNode;
7
+ /** Description / value part */
8
+ description: ReactNode;
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;
17
+ }
18
+
19
+ export default DescriptionListItemProps;
@@ -0,0 +1,101 @@
1
+ import { View } from 'react-native';
2
+ import { StyleSheet } from 'react-native-unistyles';
3
+ import { useTheme } from '../../hooks';
4
+ import { BodyText } from '../BodyText';
5
+ import { Link } from '../Link';
6
+ import { useDescriptionListContext } from './DescriptionList.context';
7
+ import type DescriptionListItemProps from './DescriptionListItem.props';
8
+
9
+ const DescriptionListItem = ({
10
+ heading,
11
+ description,
12
+ headingWidth,
13
+ linkText,
14
+ linkHref,
15
+ linkIcon,
16
+ linkIconPosition,
17
+ linkOnPress,
18
+ linkTarget,
19
+ linkShowIcon,
20
+ style,
21
+ ...props
22
+ }: DescriptionListItemProps) => {
23
+ const { components } = useTheme();
24
+ const { direction, itemHeadingWidth = components.descriptionList.item.row.headingWidth } =
25
+ useDescriptionListContext();
26
+ styles.useVariants({ direction });
27
+ const headingIsText = typeof heading === 'string' || typeof heading === 'number';
28
+ const descIsText = typeof description === 'string' || typeof description === 'number';
29
+ const combinedLabel = headingIsText && descIsText ? `${heading}: ${description}` : undefined;
30
+ const hideDescendants = !!combinedLabel;
31
+ return (
32
+ <View
33
+ accessibilityRole="text"
34
+ accessible={!!combinedLabel}
35
+ accessibilityLabel={combinedLabel}
36
+ {...props}
37
+ style={[styles.item, style]}
38
+ >
39
+ <View
40
+ style={styles.textWrap}
41
+ importantForAccessibility={hideDescendants ? 'no-hide-descendants' : undefined}
42
+ accessibilityElementsHidden={hideDescendants || undefined}
43
+ >
44
+ <View style={[direction === 'row' && { width: headingWidth || itemHeadingWidth }]}>
45
+ {headingIsText ? <BodyText style={styles.headingText}>{heading}</BodyText> : heading}
46
+ </View>
47
+ <View style={styles.descriptionWrapper}>
48
+ {descIsText ? <BodyText>{description}</BodyText> : description}
49
+ </View>
50
+ </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}
64
+ </View>
65
+ );
66
+ };
67
+
68
+ DescriptionListItem.displayName = 'DescriptionListItem';
69
+
70
+ const styles = StyleSheet.create(theme => ({
71
+ item: {
72
+ width: theme.space.full,
73
+ flexDirection: 'row',
74
+ alignItems: 'flex-start',
75
+ gap: theme.components.descriptionList.item.gap,
76
+ },
77
+ textWrap: {
78
+ flex: 1,
79
+ variants: {
80
+ direction: {
81
+ row: {
82
+ flexDirection: 'row',
83
+ alignItems: 'flex-start',
84
+ gap: theme.components.descriptionList.item.row.gap,
85
+ },
86
+ column: {
87
+ flexDirection: 'column',
88
+ gap: theme.components.descriptionList.item.column.gap,
89
+ },
90
+ },
91
+ },
92
+ },
93
+ headingText: {
94
+ color: theme.color.text.secondary,
95
+ },
96
+ descriptionWrapper: {
97
+ flex: 1,
98
+ },
99
+ }));
100
+
101
+ export default DescriptionListItem;