@utilitywarehouse/hearth-react-native 0.4.2 → 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 (180) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +44 -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/DescriptionList/DescriptionList.d.ts +1 -1
  25. package/build/components/DescriptionList/DescriptionList.js +2 -2
  26. package/build/components/DescriptionList/DescriptionList.props.d.ts +1 -8
  27. package/build/components/DescriptionList/DescriptionListItem.d.ts +1 -1
  28. package/build/components/DescriptionList/DescriptionListItem.js +4 -3
  29. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +3 -8
  30. package/build/components/IndicatorIconButton/IndicatorIconButton.d.ts +6 -0
  31. package/build/components/IndicatorIconButton/IndicatorIconButton.js +26 -0
  32. package/build/components/IndicatorIconButton/IndicatorIconButton.props.d.ts +8 -0
  33. package/build/components/IndicatorIconButton/IndicatorIconButton.props.js +1 -0
  34. package/build/components/IndicatorIconButton/index.d.ts +2 -0
  35. package/build/components/IndicatorIconButton/index.js +1 -0
  36. package/build/components/Link/LinkText.js +3 -3
  37. package/build/components/List/List.context.d.ts +0 -2
  38. package/build/components/List/List.d.ts +1 -1
  39. package/build/components/List/List.js +5 -5
  40. package/build/components/List/List.props.d.ts +1 -9
  41. package/build/components/List/ListAction/ListAction.d.ts +18 -0
  42. package/build/components/List/ListAction/ListAction.js +103 -0
  43. package/build/components/List/ListAction/ListAction.props.d.ts +8 -0
  44. package/build/components/List/ListAction/ListAction.props.js +1 -0
  45. package/build/components/List/ListAction/ListActionContent.d.ts +6 -0
  46. package/build/components/List/ListAction/ListActionContent.js +14 -0
  47. package/build/components/List/ListAction/ListActionText.d.ts +6 -0
  48. package/build/components/List/ListAction/ListActionText.js +7 -0
  49. package/build/components/List/ListAction/ListActionTrailingContent.d.ts +6 -0
  50. package/build/components/List/ListAction/ListActionTrailingContent.js +5 -0
  51. package/build/components/List/ListAction/ListActionTrailingIcon.d.ts +9 -0
  52. package/build/components/List/ListAction/ListActionTrailingIcon.js +18 -0
  53. package/build/components/List/ListAction/index.d.ts +6 -0
  54. package/build/components/List/ListAction/index.js +5 -0
  55. package/build/components/List/ListItem/ListItem.context.d.ts +1 -1
  56. package/build/components/List/ListItem/ListItem.props.d.ts +9 -5
  57. package/build/components/List/ListItem/ListItemRoot.d.ts +1 -1
  58. package/build/components/List/ListItem/ListItemRoot.js +10 -12
  59. package/build/components/List/ListItem/index.d.ts +4 -4
  60. package/build/components/List/ListItem/index.js +3 -3
  61. package/build/components/List/index.d.ts +1 -0
  62. package/build/components/List/index.js +1 -0
  63. package/build/components/ProgressStepper/ProgressStep.d.ts +10 -0
  64. package/build/components/ProgressStepper/ProgressStep.js +100 -0
  65. package/build/components/ProgressStepper/ProgressStepper.d.ts +6 -0
  66. package/build/components/ProgressStepper/ProgressStepper.js +22 -0
  67. package/build/components/ProgressStepper/ProgressStepper.props.d.ts +22 -0
  68. package/build/components/ProgressStepper/ProgressStepper.props.js +1 -0
  69. package/build/components/ProgressStepper/ProgressStepperRoot.d.ts +6 -0
  70. package/build/components/ProgressStepper/ProgressStepperRoot.js +16 -0
  71. package/build/components/ProgressStepper/index.d.ts +3 -0
  72. package/build/components/ProgressStepper/index.js +2 -0
  73. package/build/components/SectionHeader/SectionHeader.d.ts +1 -1
  74. package/build/components/SectionHeader/SectionHeader.js +6 -3
  75. package/build/components/SectionHeader/SectionHeader.props.d.ts +9 -16
  76. package/build/components/SectionHeader/SectionHeaderTrailingContent.d.ts +6 -0
  77. package/build/components/SectionHeader/SectionHeaderTrailingContent.js +13 -0
  78. package/build/components/SectionHeader/index.d.ts +1 -0
  79. package/build/components/SectionHeader/index.js +1 -0
  80. package/build/components/Tabs/Tab.js +2 -2
  81. package/build/components/ToggleButton/ToggleButtonText.js +2 -2
  82. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +4 -1
  83. package/build/components/index.d.ts +3 -0
  84. package/build/components/index.js +3 -0
  85. package/build/core/themes.d.ts +12 -24
  86. package/build/tokens/components/dark/button.d.ts +1 -1
  87. package/build/tokens/components/dark/button.js +1 -1
  88. package/build/tokens/components/dark/dialog.d.ts +1 -0
  89. package/build/tokens/components/dark/dialog.js +1 -0
  90. package/build/tokens/components/dark/illustrations.d.ts +1 -0
  91. package/build/tokens/components/dark/illustrations.js +1 -0
  92. package/build/tokens/components/dark/toast.d.ts +4 -1
  93. package/build/tokens/components/dark/toast.js +4 -1
  94. package/build/tokens/components/light/button.d.ts +1 -1
  95. package/build/tokens/components/light/button.js +1 -1
  96. package/build/tokens/components/light/dialog.d.ts +1 -0
  97. package/build/tokens/components/light/dialog.js +1 -0
  98. package/build/tokens/components/light/illustrations.d.ts +1 -0
  99. package/build/tokens/components/light/illustrations.js +1 -0
  100. package/build/tokens/components/light/toast.d.ts +4 -1
  101. package/build/tokens/components/light/toast.js +4 -1
  102. package/build/tokens/layout.d.ts +6 -12
  103. package/build/tokens/layout.js +3 -6
  104. package/docs/components/AllComponents.web.tsx +86 -4
  105. package/docs/components/BadgeList.tsx +20 -56
  106. package/docs/components/SwitchList.tsx +4 -8
  107. package/docs/getting-started.mdx +30 -14
  108. package/docs/introduction.mdx +1 -1
  109. package/package.json +4 -4
  110. package/src/components/Alert/AlertTitle.tsx +7 -7
  111. package/src/components/Badge/Badge.props.ts +1 -0
  112. package/src/components/Badge/Badge.tsx +3 -2
  113. package/src/components/Button/ButtonRoot.tsx +4 -0
  114. package/src/components/Button/ButtonText.tsx +3 -3
  115. package/src/components/Card/CardRoot.tsx +2 -0
  116. package/src/components/Carousel/Carousel.context.tsx +8 -0
  117. package/src/components/Carousel/Carousel.docs.mdx +389 -0
  118. package/src/components/Carousel/Carousel.props.ts +89 -0
  119. package/src/components/Carousel/Carousel.stories.tsx +317 -0
  120. package/src/components/Carousel/Carousel.tsx +444 -0
  121. package/src/components/Carousel/CarouselControlItem.tsx +87 -0
  122. package/src/components/Carousel/CarouselControls.tsx +150 -0
  123. package/src/components/Carousel/CarouselItem.tsx +68 -0
  124. package/src/components/Carousel/index.ts +6 -0
  125. package/src/components/DescriptionList/DescriptionList.docs.mdx +24 -27
  126. package/src/components/DescriptionList/DescriptionList.props.ts +1 -8
  127. package/src/components/DescriptionList/DescriptionList.stories.tsx +13 -19
  128. package/src/components/DescriptionList/DescriptionList.tsx +2 -14
  129. package/src/components/DescriptionList/DescriptionListItem.props.ts +3 -8
  130. package/src/components/DescriptionList/DescriptionListItem.tsx +13 -21
  131. package/src/components/IndicatorIconButton/IndicatorIconButton.docs.mdx +85 -0
  132. package/src/components/IndicatorIconButton/IndicatorIconButton.props.ts +12 -0
  133. package/src/components/IndicatorIconButton/IndicatorIconButton.stories.tsx +142 -0
  134. package/src/components/IndicatorIconButton/IndicatorIconButton.tsx +36 -0
  135. package/src/components/IndicatorIconButton/index.tsx +2 -0
  136. package/src/components/Link/LinkText.tsx +4 -4
  137. package/src/components/List/List.context.ts +0 -1
  138. package/src/components/List/List.docs.mdx +376 -179
  139. package/src/components/List/List.props.ts +1 -9
  140. package/src/components/List/List.stories.tsx +289 -38
  141. package/src/components/List/List.tsx +5 -26
  142. package/src/components/List/ListAction/ListAction.props.ts +10 -0
  143. package/src/components/List/ListAction/ListAction.tsx +133 -0
  144. package/src/components/List/ListAction/ListActionContent.tsx +21 -0
  145. package/src/components/List/ListAction/ListActionText.tsx +14 -0
  146. package/src/components/List/ListAction/ListActionTrailingContent.tsx +9 -0
  147. package/src/components/List/ListAction/ListActionTrailingIcon.tsx +32 -0
  148. package/src/components/List/ListAction/index.ts +6 -0
  149. package/src/components/List/ListItem/ListItem.context.ts +1 -1
  150. package/src/components/List/ListItem/ListItem.props.ts +9 -5
  151. package/src/components/List/ListItem/ListItemRoot.tsx +18 -14
  152. package/src/components/List/ListItem/index.ts +4 -4
  153. package/src/components/List/index.ts +1 -0
  154. package/src/components/ProgressStepper/ProgressStep.tsx +134 -0
  155. package/src/components/ProgressStepper/ProgressStepper.docs.mdx +87 -0
  156. package/src/components/ProgressStepper/ProgressStepper.props.ts +27 -0
  157. package/src/components/ProgressStepper/ProgressStepper.stories.tsx +108 -0
  158. package/src/components/ProgressStepper/ProgressStepper.tsx +26 -0
  159. package/src/components/ProgressStepper/ProgressStepperRoot.tsx +32 -0
  160. package/src/components/ProgressStepper/index.ts +3 -0
  161. package/src/components/SectionHeader/SectionHeader.props.ts +9 -16
  162. package/src/components/SectionHeader/SectionHeader.stories.tsx +28 -18
  163. package/src/components/SectionHeader/SectionHeader.tsx +18 -19
  164. package/src/components/SectionHeader/SectionHeaderTrailingContent.tsx +20 -0
  165. package/src/components/SectionHeader/Sectionheader.docs.mdx +9 -24
  166. package/src/components/SectionHeader/index.ts +1 -0
  167. package/src/components/Switch/Switch.docs.mdx +0 -4
  168. package/src/components/Tabs/Tab.tsx +4 -2
  169. package/src/components/ToggleButton/ToggleButtonText.tsx +3 -3
  170. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  171. package/src/components/index.ts +3 -0
  172. package/src/tokens/components/dark/button.ts +1 -1
  173. package/src/tokens/components/dark/dialog.ts +1 -0
  174. package/src/tokens/components/dark/illustrations.ts +1 -0
  175. package/src/tokens/components/dark/toast.ts +4 -1
  176. package/src/tokens/components/light/button.ts +1 -1
  177. package/src/tokens/components/light/dialog.ts +1 -0
  178. package/src/tokens/components/light/illustrations.ts +1 -0
  179. package/src/tokens/components/light/toast.ts +4 -1
  180. package/src/tokens/layout.ts +3 -6
@@ -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';
@@ -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
+ };
@@ -0,0 +1,142 @@
1
+ import { Meta, StoryObj } from '@storybook/react-vite';
2
+ import * as Icons from '@utilitywarehouse/hearth-react-native-icons';
3
+ import { ComponentType } from 'react';
4
+ import { VariantTitle } from '../../../docs/components';
5
+ import { Box } from '../Box';
6
+ import { Flex } from '../Flex';
7
+ import { useTheme } from '../../hooks';
8
+ import IndicatorIconButton from './IndicatorIconButton';
9
+
10
+ const meta = {
11
+ title: 'Stories / IndicatorIconButton',
12
+ component: IndicatorIconButton,
13
+ parameters: {
14
+ layout: 'centered',
15
+ },
16
+ argTypes: {
17
+ inverted: {
18
+ type: 'boolean',
19
+ control: 'boolean',
20
+ description: 'To set the button to be inverted. (To only be used on `purple` backgrounds)',
21
+ },
22
+ icon: {
23
+ options: ['none', ...Object.keys(Icons).filter(icon => icon.includes('Medium'))],
24
+ control: 'select',
25
+ description: 'The icon component for the button.',
26
+ },
27
+ pressed: {
28
+ type: 'boolean',
29
+ control: 'boolean',
30
+ description: 'To set the button to be pressed.',
31
+ },
32
+ indicator: {
33
+ type: 'boolean',
34
+ control: 'boolean',
35
+ description: 'To show or hide the red dot indicator.',
36
+ },
37
+ },
38
+ args: {
39
+ icon: 'BellMediumIcon' as unknown as ComponentType,
40
+ inverted: false,
41
+ pressed: false,
42
+ indicator: true,
43
+ },
44
+ } satisfies Meta<typeof IndicatorIconButton>;
45
+
46
+ export default meta;
47
+ type Story = StoryObj<typeof meta>;
48
+
49
+ export const Playground: Story = {
50
+ render: args => {
51
+ const icon = typeof args.icon === 'string' ? Icons[args.icon as keyof typeof Icons] : args.icon;
52
+ return <IndicatorIconButton {...args} icon={icon} />;
53
+ },
54
+ };
55
+
56
+ export const Variants: Story = {
57
+ render: args => {
58
+ const icon = typeof args.icon === 'string' ? Icons[args.icon as keyof typeof Icons] : args.icon;
59
+ return (
60
+ <Flex direction="column" space="xl">
61
+ <Flex direction="row" space="lg">
62
+ <Box>
63
+ <VariantTitle title="Without Indicator" invert={args.inverted}>
64
+ <IndicatorIconButton {...args} icon={icon} indicator={false} />
65
+ </VariantTitle>
66
+ </Box>
67
+ <Box>
68
+ <VariantTitle title="With Indicator" invert={args.inverted}>
69
+ <IndicatorIconButton {...args} icon={icon} indicator={true} />
70
+ </VariantTitle>
71
+ </Box>
72
+ </Flex>
73
+ <Flex direction="row" space="lg">
74
+ <Box style={{ backgroundColor: 'purple', padding: 10, borderRadius: 8 }}>
75
+ <VariantTitle title="Inverted" invert={!args.inverted}>
76
+ <IndicatorIconButton {...args} icon={icon} inverted={true} />
77
+ </VariantTitle>
78
+ </Box>
79
+ </Flex>
80
+ </Flex>
81
+ );
82
+ },
83
+ };
84
+
85
+ export const Colourful: Story = {
86
+ render: args => {
87
+ const icon = typeof args.icon === 'string' ? Icons[args.icon as keyof typeof Icons] : args.icon;
88
+ const theme = useTheme();
89
+
90
+ return (
91
+ <Flex direction="column" space="xl">
92
+ <Flex direction="row" space="lg">
93
+ <Box>
94
+ <IndicatorIconButton {...args} icon={icon} iconStyle={{ color: theme.color.energyBlue[500] }} />
95
+ </Box>
96
+ <Box>
97
+ <IndicatorIconButton {...args} icon={icon} iconStyle={{ color: theme.color.cashbackLilac[500] }} />
98
+ </Box>
99
+ </Flex>
100
+ </Flex>
101
+ );
102
+ },
103
+ };
104
+
105
+ export const WithAccessibilityLabel: Story = {
106
+ render: args => {
107
+ return (
108
+ <Flex direction="column" space="xl">
109
+ <Box>
110
+ <VariantTitle title="Notification label" invert={args.inverted}>
111
+ <IndicatorIconButton
112
+ {...args}
113
+ icon={Icons.BellMediumIcon}
114
+ indicator={true}
115
+ accessibilityLabel="New notifications available"
116
+ />
117
+ </VariantTitle>
118
+ </Box>
119
+ <Box>
120
+ <VariantTitle title="No new actions, cashback section" invert={args.inverted}>
121
+ <IndicatorIconButton
122
+ {...args}
123
+ icon={Icons.CashbackCardMediumIcon}
124
+ indicator={false}
125
+ accessibilityLabel="Cashback section"
126
+ />
127
+ </VariantTitle>
128
+ </Box>
129
+ <Box>
130
+ <VariantTitle title="New action, cashback section" invert={args.inverted}>
131
+ <IndicatorIconButton
132
+ {...args}
133
+ icon={Icons.CashbackCardMediumIcon}
134
+ indicator={true}
135
+ accessibilityLabel="Cashback section, new transactions"
136
+ />
137
+ </VariantTitle>
138
+ </Box>
139
+ </Flex>
140
+ );
141
+ },
142
+ };
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { UnstyledIconButton } from '../UnstyledIconButton';
5
+ import { IndicatorIconButtonProps } from './IndicatorIconButton.props';
6
+
7
+ const IndicatorIconButton = ({ indicator = false, ...props }: IndicatorIconButtonProps) => {
8
+ return (
9
+ <View style={styles.container}>
10
+ <UnstyledIconButton size="md" {...props} />
11
+ {indicator && <View style={styles.indicator} />}
12
+ </View>
13
+ );
14
+ };
15
+
16
+ IndicatorIconButton.displayName = 'IndicatorIconButton';
17
+
18
+ const styles = StyleSheet.create(theme => ({
19
+ container: {
20
+ position: 'relative',
21
+ width: theme.components.iconButton.unstyled.md.width,
22
+ height: theme.components.iconButton.unstyled.md.height,
23
+ },
24
+ indicator: {
25
+ position: 'absolute',
26
+ top: 2,
27
+ right: 1,
28
+ width: theme.components.indicatorIconButton.indicator.width,
29
+ height: theme.components.indicatorIconButton.indicator.height,
30
+ borderRadius: theme.components.indicatorIconButton.indicator.radius,
31
+ backgroundColor: theme.color.interactive.destructive.surface.strong.default,
32
+ zIndex: 1,
33
+ },
34
+ }));
35
+
36
+ export default IndicatorIconButton;
@@ -0,0 +1,2 @@
1
+ export { default as IndicatorIconButton } from './IndicatorIconButton';
2
+ export type { IndicatorIconButtonProps } from './IndicatorIconButton.props';