@utilitywarehouse/hearth-react-native 0.4.2 → 0.6.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 (207) hide show
  1. package/.storybook/main.ts +21 -1
  2. package/.turbo/turbo-build.log +1 -1
  3. package/.turbo/turbo-lint.log +1 -1
  4. package/CHANGELOG.md +52 -0
  5. package/build/components/Alert/AlertTitle.js +6 -6
  6. package/build/components/Badge/Badge.js +3 -3
  7. package/build/components/Badge/Badge.props.d.ts +1 -0
  8. package/build/components/Button/ButtonRoot.js +4 -0
  9. package/build/components/Button/ButtonText.js +2 -2
  10. package/build/components/Card/CardRoot.js +1 -1
  11. package/build/components/Carousel/Carousel.context.d.ts +4 -0
  12. package/build/components/Carousel/Carousel.context.js +4 -0
  13. package/build/components/Carousel/Carousel.d.ts +6 -0
  14. package/build/components/Carousel/Carousel.js +278 -0
  15. package/build/components/Carousel/Carousel.props.d.ts +65 -0
  16. package/build/components/Carousel/Carousel.props.js +1 -0
  17. package/build/components/Carousel/CarouselControlItem.d.ts +24 -0
  18. package/build/components/Carousel/CarouselControlItem.js +64 -0
  19. package/build/components/Carousel/CarouselControls.d.ts +4 -0
  20. package/build/components/Carousel/CarouselControls.js +74 -0
  21. package/build/components/Carousel/CarouselItem.d.ts +6 -0
  22. package/build/components/Carousel/CarouselItem.js +38 -0
  23. package/build/components/Carousel/index.d.ts +5 -0
  24. package/build/components/Carousel/index.js +5 -0
  25. package/build/components/Container/Container.d.ts +6 -0
  26. package/build/components/Container/Container.js +40 -0
  27. package/build/components/Container/Container.props.d.ts +85 -0
  28. package/build/components/Container/Container.props.js +1 -0
  29. package/build/components/Container/index.d.ts +2 -0
  30. package/build/components/Container/index.js +1 -0
  31. package/build/components/DescriptionList/DescriptionList.d.ts +1 -1
  32. package/build/components/DescriptionList/DescriptionList.js +2 -2
  33. package/build/components/DescriptionList/DescriptionList.props.d.ts +1 -8
  34. package/build/components/DescriptionList/DescriptionListItem.d.ts +1 -1
  35. package/build/components/DescriptionList/DescriptionListItem.js +4 -3
  36. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +3 -8
  37. package/build/components/IndicatorIconButton/IndicatorIconButton.d.ts +6 -0
  38. package/build/components/IndicatorIconButton/IndicatorIconButton.js +26 -0
  39. package/build/components/IndicatorIconButton/IndicatorIconButton.props.d.ts +8 -0
  40. package/build/components/IndicatorIconButton/IndicatorIconButton.props.js +1 -0
  41. package/build/components/IndicatorIconButton/index.d.ts +2 -0
  42. package/build/components/IndicatorIconButton/index.js +1 -0
  43. package/build/components/Link/LinkText.js +3 -3
  44. package/build/components/List/List.context.d.ts +0 -2
  45. package/build/components/List/List.d.ts +1 -1
  46. package/build/components/List/List.js +5 -5
  47. package/build/components/List/List.props.d.ts +1 -9
  48. package/build/components/List/ListAction/ListAction.d.ts +18 -0
  49. package/build/components/List/ListAction/ListAction.js +103 -0
  50. package/build/components/List/ListAction/ListAction.props.d.ts +8 -0
  51. package/build/components/List/ListAction/ListAction.props.js +1 -0
  52. package/build/components/List/ListAction/ListActionContent.d.ts +6 -0
  53. package/build/components/List/ListAction/ListActionContent.js +14 -0
  54. package/build/components/List/ListAction/ListActionText.d.ts +6 -0
  55. package/build/components/List/ListAction/ListActionText.js +7 -0
  56. package/build/components/List/ListAction/ListActionTrailingContent.d.ts +6 -0
  57. package/build/components/List/ListAction/ListActionTrailingContent.js +5 -0
  58. package/build/components/List/ListAction/ListActionTrailingIcon.d.ts +9 -0
  59. package/build/components/List/ListAction/ListActionTrailingIcon.js +18 -0
  60. package/build/components/List/ListAction/index.d.ts +6 -0
  61. package/build/components/List/ListAction/index.js +5 -0
  62. package/build/components/List/ListItem/ListItem.context.d.ts +1 -1
  63. package/build/components/List/ListItem/ListItem.props.d.ts +9 -5
  64. package/build/components/List/ListItem/ListItemRoot.d.ts +1 -1
  65. package/build/components/List/ListItem/ListItemRoot.js +10 -12
  66. package/build/components/List/ListItem/index.d.ts +4 -4
  67. package/build/components/List/ListItem/index.js +3 -3
  68. package/build/components/List/index.d.ts +1 -0
  69. package/build/components/List/index.js +1 -0
  70. package/build/components/ProgressStepper/ProgressStep.d.ts +10 -0
  71. package/build/components/ProgressStepper/ProgressStep.js +100 -0
  72. package/build/components/ProgressStepper/ProgressStepper.d.ts +6 -0
  73. package/build/components/ProgressStepper/ProgressStepper.js +22 -0
  74. package/build/components/ProgressStepper/ProgressStepper.props.d.ts +22 -0
  75. package/build/components/ProgressStepper/ProgressStepper.props.js +1 -0
  76. package/build/components/ProgressStepper/ProgressStepperRoot.d.ts +6 -0
  77. package/build/components/ProgressStepper/ProgressStepperRoot.js +16 -0
  78. package/build/components/ProgressStepper/index.d.ts +3 -0
  79. package/build/components/ProgressStepper/index.js +2 -0
  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/ThemedImage/ThemedImage.d.ts +12 -0
  89. package/build/components/ThemedImage/ThemedImage.js +27 -0
  90. package/build/components/ThemedImage/ThemedImage.props.d.ts +13 -0
  91. package/build/components/ThemedImage/ThemedImage.props.js +1 -0
  92. package/build/components/ThemedImage/index.d.ts +2 -0
  93. package/build/components/ThemedImage/index.js +1 -0
  94. package/build/components/ToggleButton/ToggleButtonText.js +2 -2
  95. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +4 -1
  96. package/build/components/index.d.ts +5 -0
  97. package/build/components/index.js +5 -0
  98. package/build/core/themes.d.ts +12 -24
  99. package/build/hooks/useStyleProps.js +1 -1
  100. package/build/tokens/components/dark/button.d.ts +1 -1
  101. package/build/tokens/components/dark/button.js +1 -1
  102. package/build/tokens/components/dark/dialog.d.ts +1 -0
  103. package/build/tokens/components/dark/dialog.js +1 -0
  104. package/build/tokens/components/dark/illustrations.d.ts +1 -0
  105. package/build/tokens/components/dark/illustrations.js +1 -0
  106. package/build/tokens/components/dark/toast.d.ts +4 -1
  107. package/build/tokens/components/dark/toast.js +4 -1
  108. package/build/tokens/components/light/button.d.ts +1 -1
  109. package/build/tokens/components/light/button.js +1 -1
  110. package/build/tokens/components/light/dialog.d.ts +1 -0
  111. package/build/tokens/components/light/dialog.js +1 -0
  112. package/build/tokens/components/light/illustrations.d.ts +1 -0
  113. package/build/tokens/components/light/illustrations.js +1 -0
  114. package/build/tokens/components/light/toast.d.ts +4 -1
  115. package/build/tokens/components/light/toast.js +4 -1
  116. package/build/tokens/layout.d.ts +6 -12
  117. package/build/tokens/layout.js +3 -6
  118. package/docs/components/AllComponents.web.tsx +122 -5
  119. package/docs/components/BadgeList.tsx +20 -56
  120. package/docs/components/SwitchList.tsx +4 -8
  121. package/docs/getting-started.mdx +30 -14
  122. package/docs/introduction.mdx +1 -1
  123. package/docs/layout-components.docs.mdx +30 -0
  124. package/package.json +6 -4
  125. package/src/components/Alert/AlertTitle.tsx +7 -7
  126. package/src/components/Badge/Badge.props.ts +1 -0
  127. package/src/components/Badge/Badge.tsx +3 -2
  128. package/src/components/Button/ButtonRoot.tsx +4 -0
  129. package/src/components/Button/ButtonText.tsx +3 -3
  130. package/src/components/Card/CardRoot.tsx +2 -0
  131. package/src/components/Carousel/Carousel.context.tsx +8 -0
  132. package/src/components/Carousel/Carousel.docs.mdx +389 -0
  133. package/src/components/Carousel/Carousel.props.ts +89 -0
  134. package/src/components/Carousel/Carousel.stories.tsx +317 -0
  135. package/src/components/Carousel/Carousel.tsx +444 -0
  136. package/src/components/Carousel/CarouselControlItem.tsx +87 -0
  137. package/src/components/Carousel/CarouselControls.tsx +150 -0
  138. package/src/components/Carousel/CarouselItem.tsx +68 -0
  139. package/src/components/Carousel/index.ts +6 -0
  140. package/src/components/Container/Container.docs.mdx +168 -0
  141. package/src/components/Container/Container.props.ts +89 -0
  142. package/src/components/Container/Container.stories.tsx +274 -0
  143. package/src/components/Container/Container.tsx +52 -0
  144. package/src/components/Container/index.tsx +2 -0
  145. package/src/components/DescriptionList/DescriptionList.docs.mdx +24 -27
  146. package/src/components/DescriptionList/DescriptionList.props.ts +1 -8
  147. package/src/components/DescriptionList/DescriptionList.stories.tsx +13 -19
  148. package/src/components/DescriptionList/DescriptionList.tsx +2 -14
  149. package/src/components/DescriptionList/DescriptionListItem.props.ts +3 -8
  150. package/src/components/DescriptionList/DescriptionListItem.tsx +13 -21
  151. package/src/components/IndicatorIconButton/IndicatorIconButton.docs.mdx +85 -0
  152. package/src/components/IndicatorIconButton/IndicatorIconButton.props.ts +12 -0
  153. package/src/components/IndicatorIconButton/IndicatorIconButton.stories.tsx +142 -0
  154. package/src/components/IndicatorIconButton/IndicatorIconButton.tsx +36 -0
  155. package/src/components/IndicatorIconButton/index.tsx +2 -0
  156. package/src/components/Link/LinkText.tsx +4 -4
  157. package/src/components/List/List.context.ts +0 -1
  158. package/src/components/List/List.docs.mdx +376 -179
  159. package/src/components/List/List.props.ts +1 -9
  160. package/src/components/List/List.stories.tsx +289 -38
  161. package/src/components/List/List.tsx +5 -26
  162. package/src/components/List/ListAction/ListAction.props.ts +10 -0
  163. package/src/components/List/ListAction/ListAction.tsx +133 -0
  164. package/src/components/List/ListAction/ListActionContent.tsx +21 -0
  165. package/src/components/List/ListAction/ListActionText.tsx +14 -0
  166. package/src/components/List/ListAction/ListActionTrailingContent.tsx +9 -0
  167. package/src/components/List/ListAction/ListActionTrailingIcon.tsx +32 -0
  168. package/src/components/List/ListAction/index.ts +6 -0
  169. package/src/components/List/ListItem/ListItem.context.ts +1 -1
  170. package/src/components/List/ListItem/ListItem.props.ts +9 -5
  171. package/src/components/List/ListItem/ListItemRoot.tsx +18 -14
  172. package/src/components/List/ListItem/index.ts +4 -4
  173. package/src/components/List/index.ts +1 -0
  174. package/src/components/ProgressStepper/ProgressStep.tsx +134 -0
  175. package/src/components/ProgressStepper/ProgressStepper.docs.mdx +87 -0
  176. package/src/components/ProgressStepper/ProgressStepper.props.ts +27 -0
  177. package/src/components/ProgressStepper/ProgressStepper.stories.tsx +108 -0
  178. package/src/components/ProgressStepper/ProgressStepper.tsx +26 -0
  179. package/src/components/ProgressStepper/ProgressStepperRoot.tsx +32 -0
  180. package/src/components/ProgressStepper/index.ts +3 -0
  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/ThemedImage/ThemedImage.docs.mdx +208 -0
  190. package/src/components/ThemedImage/ThemedImage.props.ts +15 -0
  191. package/src/components/ThemedImage/ThemedImage.stories.tsx +175 -0
  192. package/src/components/ThemedImage/ThemedImage.tsx +34 -0
  193. package/src/components/ThemedImage/index.tsx +2 -0
  194. package/src/components/ToggleButton/ToggleButtonText.tsx +3 -3
  195. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  196. package/src/components/index.ts +5 -0
  197. package/src/hooks/useStyleProps.ts +1 -1
  198. package/src/tokens/components/dark/button.ts +1 -1
  199. package/src/tokens/components/dark/dialog.ts +1 -0
  200. package/src/tokens/components/dark/illustrations.ts +1 -0
  201. package/src/tokens/components/dark/toast.ts +4 -1
  202. package/src/tokens/components/light/button.ts +1 -1
  203. package/src/tokens/components/light/dialog.ts +1 -0
  204. package/src/tokens/components/light/illustrations.ts +1 -0
  205. package/src/tokens/components/light/toast.ts +4 -1
  206. package/src/tokens/layout.ts +3 -6
  207. package/src/vite-env.d.ts +6 -0
@@ -0,0 +1,6 @@
1
+ export { default as ListAction } from './ListAction';
2
+ export type { default as ListActionProps } from './ListAction.props';
3
+ export { default as ListActionContent } from './ListActionContent';
4
+ export { default as ListActionText } from './ListActionText';
5
+ export { default as ListActionTrailingContent } from './ListActionTrailingContent';
6
+ export { default as ListActionTrailingIcon } from './ListActionTrailingIcon';
@@ -1,7 +1,7 @@
1
1
  import { createContext, useContext } from 'react';
2
2
  import ListItemProps from './ListItem.props';
3
3
 
4
- export interface IListItemContext extends Pick<ListItemProps, 'divider' | 'loading' | 'disabled'> {
4
+ export interface IListItemContext extends Pick<ListItemProps, 'loading' | 'disabled'> {
5
5
  showPressed?: boolean;
6
6
  active?: boolean;
7
7
  }
@@ -1,9 +1,7 @@
1
1
  import type { PressableProps, ViewProps } from 'react-native';
2
- import type { ColorValue } from '../../../types';
2
+ import BadgeProps from '../../Badge/Badge.props';
3
3
 
4
4
  interface ListItemBaseProps extends Omit<PressableProps, 'children'> {
5
- divider?: boolean;
6
- dividerColor?: ColorValue;
7
5
  loading?: boolean;
8
6
  disabled?: boolean;
9
7
  variant?: 'subtle' | 'emphasis';
@@ -12,18 +10,24 @@ interface ListItemBaseProps extends Omit<PressableProps, 'children'> {
12
10
 
13
11
  export interface ListItemWithChildren extends ListItemBaseProps {
14
12
  children: ViewProps['children'];
15
- text?: never;
13
+ heading?: never;
16
14
  helperText?: never;
17
15
  leadingContent?: never;
18
16
  trailingContent?: never;
17
+ numericValue?: never;
18
+ badge?: never;
19
+ badgePosition?: never;
19
20
  }
20
21
 
21
22
  export interface ListItemWithoutChildren extends ListItemBaseProps {
22
23
  children?: never;
23
- text: string;
24
+ heading: string;
24
25
  helperText?: string;
25
26
  leadingContent?: ViewProps['children'];
26
27
  trailingContent?: ViewProps['children'];
28
+ numericValue?: string | number;
29
+ badge?: BadgeProps;
30
+ badgePosition?: 'top' | 'bottom';
27
31
  }
28
32
 
29
33
  type ListItemProps = ListItemWithChildren | ListItemWithoutChildren;
@@ -2,6 +2,8 @@ import { ChevronRightSmallIcon } from '@utilitywarehouse/hearth-react-native-ico
2
2
  import { useMemo } from 'react';
3
3
  import { Pressable, ViewStyle } from 'react-native';
4
4
  import { StyleSheet } from 'react-native-unistyles';
5
+ import { Badge } from '../../Badge';
6
+ import { DetailText } from '../../DetailText';
5
7
  import { Skeleton } from '../../Skeleton';
6
8
  import { useListContext } from '../List.context';
7
9
  import { IListItemContext, ListItemContext } from './ListItem.context';
@@ -14,16 +16,18 @@ import ListItemTrailingContent from './ListItemTrailingContent';
14
16
  import ListItemTrailingIcon from './ListItemTrailingIcon';
15
17
 
16
18
  const ListItemRoot = ({
17
- text,
19
+ heading,
18
20
  helperText,
19
21
  leadingContent,
20
22
  trailingContent,
21
23
  disabled,
22
- divider,
23
24
  loading,
24
25
  children,
25
26
  states,
26
27
  variant = 'subtle',
28
+ badge,
29
+ badgePosition = 'bottom',
30
+ numericValue,
27
31
  ...props
28
32
  }: ListItemProps & { states?: { active?: boolean; disabled?: boolean } }) => {
29
33
  const { onPress } = props;
@@ -42,7 +46,6 @@ const ListItemRoot = ({
42
46
 
43
47
  const isLoading = loading || listContext?.loading;
44
48
  const showPressed = isLoading ? false : !!onPress;
45
- const showDivider = listContext?.divider ?? divider;
46
49
  const isDisabled = disabled || listContext?.disabled || false;
47
50
  const listItemVariant = getListContainer() || variant;
48
51
 
@@ -50,7 +53,6 @@ const ListItemRoot = ({
50
53
  const loadingTestID = isLoading ? `${testID}-loading` : testID;
51
54
 
52
55
  styles.useVariants({
53
- divider: showDivider,
54
56
  variant: listItemVariant,
55
57
  showPressed,
56
58
  active,
@@ -63,12 +65,11 @@ const ListItemRoot = ({
63
65
  const value: IListItemContext = useMemo(() => {
64
66
  return {
65
67
  showPressed,
66
- divider: showDivider,
67
68
  active,
68
69
  loading: isLoading,
69
70
  disabled: isDisabled,
70
71
  };
71
- }, [active, showPressed, showDivider, isLoading, isDisabled]);
72
+ }, [active, showPressed, isLoading, isDisabled]);
72
73
 
73
74
  if (loading || listContext?.loading) {
74
75
  return (
@@ -95,6 +96,7 @@ const ListItemRoot = ({
95
96
  testID={testID}
96
97
  style={[styles.container, props.style as ViewStyle]}
97
98
  disabled={isDisabled}
99
+ accessibilityRole={onPress ? 'button' : undefined}
98
100
  >
99
101
  {children ? (
100
102
  children
@@ -104,13 +106,16 @@ const ListItemRoot = ({
104
106
  <ListItemLeadingContent>{leadingContent}</ListItemLeadingContent>
105
107
  ) : null}
106
108
  <ListItemContent>
107
- <ListItemText>{text}</ListItemText>
109
+ {badgePosition === 'top' && badge ? <Badge {...badge} /> : null}
110
+ <ListItemText>{heading}</ListItemText>
108
111
  {helperText ? <ListItemHelperText>{helperText}</ListItemHelperText> : null}
112
+ {badgePosition === 'bottom' && badge ? <Badge {...badge} /> : null}
109
113
  </ListItemContent>
114
+ {!!numericValue && <DetailText size="lg">{numericValue}</DetailText>}
110
115
  {trailingContent ? (
111
116
  <ListItemTrailingContent>{trailingContent}</ListItemTrailingContent>
112
117
  ) : onPress ? (
113
- <ListItemTrailingContent>
118
+ <ListItemTrailingContent style={styles.centeredTrailingIcon}>
114
119
  <ListItemTrailingIcon as={ChevronRightSmallIcon} />
115
120
  </ListItemTrailingContent>
116
121
  ) : null}
@@ -129,13 +134,9 @@ const styles = StyleSheet.create(theme => ({
129
134
  paddingHorizontal: theme.components.list.item.functional.padding,
130
135
  flexDirection: 'row',
131
136
  gap: theme.components.list.item.gap,
137
+ borderTopWidth: theme.borderWidth['1'],
138
+ borderStyle: 'solid',
132
139
  variants: {
133
- divider: {
134
- true: {
135
- borderTopWidth: theme.borderWidth['1'],
136
- borderStyle: 'solid',
137
- },
138
- },
139
140
  isFirstChild: {
140
141
  true: {
141
142
  borderTopWidth: 0,
@@ -197,6 +198,9 @@ const styles = StyleSheet.create(theme => ({
197
198
  },
198
199
  ],
199
200
  },
201
+ centeredTrailingIcon: {
202
+ justifyContent: 'center',
203
+ },
200
204
  }));
201
205
 
202
206
  export default ListItemRoot;
@@ -1,9 +1,9 @@
1
1
  export { default as ListItem } from './ListItem';
2
- export { default as ListItemIcon } from './ListItemIcon';
3
- export { default as ListItemContent } from './ListItemContent';
4
2
  export type { default as ListItemProps } from './ListItem.props';
3
+ export { default as ListItemContent } from './ListItemContent';
4
+ export { default as ListItemHelperText } from './ListItemHelperText';
5
+ export { default as ListItemIcon } from './ListItemIcon';
5
6
  export { default as ListItemLeadingContent } from './ListItemLeadingContent';
7
+ export { default as ListItemText } from './ListItemText';
6
8
  export { default as ListItemTrailingContent } from './ListItemTrailingContent';
7
9
  export { default as ListItemTrailingIcon } from './ListItemTrailingIcon';
8
- export { default as ListItemHelperText } from './ListItemHelperText';
9
- export { default as ListItemText } from './ListItemText';
@@ -1,3 +1,4 @@
1
1
  export { default as List } from './List';
2
2
  export { useListContext } from './List.context';
3
+ export * from './ListAction';
3
4
  export * from './ListItem';
@@ -0,0 +1,134 @@
1
+ import React from 'react';
2
+ import { View } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { TickSmallIcon } from '@utilitywarehouse/hearth-react-native-icons';
5
+ import { Icon } from '../Icon';
6
+ import { BodyText } from '../BodyText';
7
+ import { ProgressStepProps } from './ProgressStepper.props';
8
+
9
+ interface ProgressStepInternalProps extends ProgressStepProps {
10
+ index?: number;
11
+ isLast?: boolean;
12
+ }
13
+
14
+ const ProgressStep = ({ status, index = 0, isLast = false, ...rest }: ProgressStepInternalProps) => {
15
+ styles.useVariants({ status, isLast });
16
+
17
+ const renderStepNumber = () => {
18
+ return (
19
+ <BodyText size="md" weight="semibold" style={styles.text}>
20
+ {index + 1}
21
+ </BodyText>
22
+ );
23
+ };
24
+
25
+ return (
26
+ <View
27
+ style={styles.container}
28
+ accessible
29
+ aria-label={`Step ${index + 1}, ${rest.id}, ${status}`}
30
+ {...rest}
31
+ >
32
+ <View style={styles.step}>
33
+ {status === 'complete' ? (
34
+ <Icon as={TickSmallIcon} width={20} height={20} style={styles.text} />
35
+ ) : status === 'active' ? (
36
+ <View style={styles.inner}>{renderStepNumber()}</View>
37
+ ) : (
38
+ renderStepNumber()
39
+ )}
40
+ </View>
41
+ {!isLast && <View style={styles.connector} />}
42
+ </View>
43
+ );
44
+ };
45
+
46
+ ProgressStep.displayName = 'ProgressStep';
47
+
48
+ const styles = StyleSheet.create(theme => ({
49
+ container: {
50
+ flexDirection: 'row',
51
+ alignItems: 'center',
52
+ variants: {
53
+ isLast: {
54
+ true: {
55
+ flex: 0,
56
+ _web: {
57
+ flex: 'none',
58
+ },
59
+ },
60
+ false: {
61
+ flex: 1,
62
+ },
63
+ },
64
+ },
65
+ },
66
+ step: {
67
+ width: theme.components.progressStepper.indicator.width,
68
+ height: theme.components.progressStepper.indicator.height,
69
+ borderRadius: theme.borderRadius.full,
70
+ alignItems: 'center',
71
+ justifyContent: 'center',
72
+ variants: {
73
+ status: {
74
+ complete: {
75
+ backgroundColor: theme.color.surface.brand.default,
76
+ },
77
+ active: {
78
+ backgroundColor: theme.color.surface.brand.default,
79
+ padding: theme.borderWidth[2],
80
+ },
81
+ incomplete: {
82
+ borderWidth: theme.components.progressStepper.indicator.future.borderWidth,
83
+ borderColor: theme.color.border.subtle,
84
+ },
85
+ },
86
+ },
87
+ },
88
+ inner: {
89
+ width: theme.components.progressStepper.indicator.width - theme.borderWidth[2] * 2,
90
+ height: theme.components.progressStepper.indicator.height - theme.borderWidth[2] * 2,
91
+ borderRadius: theme.borderRadius.full,
92
+ backgroundColor: theme.color.surface.brand.default,
93
+ borderWidth: theme.borderWidth[2],
94
+ borderColor: theme.color.surface.neutral.subtle,
95
+ alignItems: 'center',
96
+ justifyContent: 'center',
97
+ },
98
+ text: {
99
+ variants: {
100
+ status: {
101
+ complete: {
102
+ color: theme.color.text.inverted,
103
+ },
104
+ active: {
105
+ color: theme.color.text.inverted,
106
+ // NOTE: Adjust lineHeight to vertically center the text within the smaller inner circle
107
+ lineHeight: theme.lineHeight[500] - theme.borderWidth[2] * 2,
108
+ },
109
+ incomplete: {
110
+ color: theme.color.text.primary,
111
+ },
112
+ },
113
+ },
114
+ },
115
+ connector: {
116
+ flex: 1,
117
+ height: theme.components.progressStepper.bar.height,
118
+ variants: {
119
+ status: {
120
+ complete: {
121
+ backgroundColor: theme.components.progressStepper.bar.complete.backgroundColor,
122
+ },
123
+ active: {
124
+ backgroundColor: theme.color.border.subtle,
125
+ },
126
+ incomplete: {
127
+ backgroundColor: theme.color.border.subtle,
128
+ },
129
+ },
130
+ },
131
+ },
132
+ }));
133
+
134
+ export default ProgressStep;
@@ -0,0 +1,87 @@
1
+ import { Canvas, Meta } from '@storybook/addon-docs/blocks';
2
+ import { BackToTopButton, ViewFigmaButton } from '../../../docs/components';
3
+ import * as ProgressStepperStories from './ProgressStepper.stories';
4
+
5
+ <Meta title="Components / Progress Stepper" />
6
+
7
+ <ViewFigmaButton url="https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=6056-1987" />
8
+
9
+ <BackToTopButton />
10
+
11
+ # Progress Stepper
12
+
13
+ A form helper component that displays a series of dots connected by lines, showing the progress through a multi-step process. Each step can be marked as completed, active, or uncompleted.
14
+
15
+ - [Usage](#usage)
16
+ - [Examples](#examples)
17
+ - [API](#api)
18
+ - [Step Statuses](#step-statuses-1)
19
+ - [Accessibility](#accessibility)
20
+ - [Best Practices](#best-practices)
21
+
22
+ ## Usage
23
+
24
+ The `ProgressStepper` component uses a compound component pattern, making it easy to define steps declaratively. It provides visual feedback about complete steps, the current active step, and remaining incomplete steps.
25
+
26
+ ### Basic Usage
27
+
28
+ ```tsx
29
+ import { ProgressStepper, ProgressStep } from '@hearth/react-native';
30
+
31
+ <ProgressStepper>
32
+ <ProgressStep id={"1"} status="complete" />
33
+ <ProgressStep id={"2"} status="complete" />
34
+ <ProgressStep id={"3"} status="active" />
35
+ <ProgressStep id={"4"} status="incomplete" />
36
+ <ProgressStep id={"5"} status="incomplete" />
37
+ </ProgressStepper>
38
+ ```
39
+
40
+ ## Examples
41
+
42
+ ### Default Layout
43
+
44
+ <Canvas of={ProgressStepperStories.Playground} />
45
+
46
+ ### Step Statuses
47
+
48
+ See how different step statuses are visualized:
49
+
50
+ <Canvas of={ProgressStepperStories.StepStatuses} />
51
+
52
+ ### Basic
53
+
54
+ <Canvas of={ProgressStepperStories.BasicExample} />
55
+
56
+ ## API
57
+
58
+ ### Progress Stepper Props
59
+
60
+ | Prop | Type | Default | Description |
61
+ |------|------|---------|-------------|
62
+ | `children` | `ReactNode` | Required | `ProgressStep` components |
63
+
64
+ ### Progress Step Props
65
+
66
+ | Prop | Type | Default | Description |
67
+ |------|------|---------|-------------|
68
+ | `id` | `string` | Required | Unique identifier for the step |
69
+ | `status` | `'complete' \| 'active' \| 'incomplete'` | Required | Current status of the step |
70
+
71
+ ## Step Statuses
72
+
73
+ - **complete**: Step has been finished, shows a checkmark icon
74
+ - **active**: Current step, shows a number with highlighted background
75
+ - **incomplete**: Future step, shows a number with muted styling
76
+
77
+ ## Accessibility
78
+
79
+ - Each step is properly labeled for screen readers
80
+ - The component follows WCAG guidelines for color contrast
81
+ - Progress indication is communicated through both visual and textual means
82
+
83
+ ## Best Practices
84
+
85
+ 1. **Keep it simple** - The stepper focuses on essential functionality without complexity
86
+ 1. **Indicate progress clearly** - Make sure users understand how many steps remain
87
+ 1. **Use meaningful IDs** - Choose IDs that make sense for your specific use case (numbers, step names, etc.)
@@ -0,0 +1,27 @@
1
+ import { ViewProps } from 'react-native';
2
+
3
+ export type StepStatus = 'complete' | 'active' | 'incomplete';
4
+
5
+ export interface ProgressStepperProps extends ViewProps {
6
+ /**
7
+ * Child ProgressStep components
8
+ */
9
+ children: React.ReactNode;
10
+ }
11
+
12
+ export interface ProgressStepProps extends ViewProps {
13
+ /**
14
+ * Unique identifier for the step
15
+ */
16
+ id: string;
17
+ /**
18
+ * Current status of the step
19
+ */
20
+ status: StepStatus;
21
+ }
22
+
23
+ export interface ProgressStepperRootProps extends ViewProps {
24
+ children: React.ReactNode;
25
+ }
26
+
27
+ export default ProgressStepperProps;
@@ -0,0 +1,108 @@
1
+ import React from 'react';
2
+ import { Meta, StoryObj } from '@storybook/react-vite';
3
+ import { ProgressStepper, ProgressStep } from '.';
4
+ import { VariantTitle } from '../../../docs/components';
5
+ import { BodyText } from '../BodyText';
6
+ import { Flex } from '../Flex';
7
+ import { Heading } from '../Heading';
8
+
9
+ const meta = {
10
+ title: 'Stories / ProgressStepper',
11
+ component: ProgressStepper,
12
+ parameters: {
13
+ layout: 'centered',
14
+ },
15
+ } satisfies Meta<typeof ProgressStepper>;
16
+
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+
20
+ export const Playground: Story = {
21
+ args: {
22
+ children: (
23
+ <Flex space="xl" direction="column" align="center" style={{ flex: 1, minWidth: 200 }}>
24
+ <ProgressStepper>
25
+ <ProgressStep id={'1'} status="complete" />
26
+ <ProgressStep id={'2'} status="complete" />
27
+ <ProgressStep id={'3'} status="active" />
28
+ <ProgressStep id={'4'} status="incomplete" />
29
+ </ProgressStepper>
30
+ <Flex direction="row" space="lg" style={{ width: '100%' }}>
31
+ <ProgressStep id={'1'} status="complete" />
32
+ </Flex>
33
+ <Flex direction="row" space="lg" style={{ width: '100%' }}>
34
+ <ProgressStep id={'3'} status="active" />
35
+ </Flex>
36
+ <Flex direction="row" space="lg" style={{ width: '100%' }}>
37
+ <ProgressStep id={'5'} status="incomplete" />
38
+ </Flex>
39
+ </Flex>
40
+ ),
41
+ },
42
+ };
43
+
44
+ export const StepStatuses: Story = {
45
+ args: {
46
+ children: <></>,
47
+ },
48
+ parameters: {
49
+ controls: { exclude: ['space'] },
50
+ },
51
+ render: props => {
52
+ return (
53
+ <Flex space="xl" direction="column" align="center">
54
+ <VariantTitle title="All Uncompleted Steps">
55
+ <ProgressStepper {...props}>
56
+ <ProgressStep id={'1'} status="incomplete" />
57
+ <ProgressStep id={'2'} status="incomplete" />
58
+ <ProgressStep id={'3'} status="incomplete" />
59
+ </ProgressStepper>
60
+ </VariantTitle>
61
+ <VariantTitle title="One Active Step">
62
+ <ProgressStepper {...props}>
63
+ <ProgressStep id={'1'} status="active" />
64
+ <ProgressStep id={'2'} status="incomplete" />
65
+ <ProgressStep id={'3'} status="incomplete" />
66
+ </ProgressStepper>
67
+ </VariantTitle>
68
+ <VariantTitle title="Mixed Statuses">
69
+ <ProgressStepper {...props}>
70
+ <ProgressStep id={'1'} status="complete" />
71
+ <ProgressStep id={'2'} status="complete" />
72
+ <ProgressStep id={'3'} status="active" />
73
+ <ProgressStep id={'4'} status="incomplete" />
74
+ </ProgressStepper>
75
+ </VariantTitle>
76
+ <VariantTitle title="All Completed">
77
+ <ProgressStepper {...props}>
78
+ <ProgressStep id={'1'} status="complete" />
79
+ <ProgressStep id={'2'} status="complete" />
80
+ <ProgressStep id={'3'} status="complete" />
81
+ </ProgressStepper>
82
+ </VariantTitle>
83
+ </Flex>
84
+ );
85
+ },
86
+ };
87
+
88
+ export const BasicExample: Story = {
89
+ args: {
90
+ children: <></>,
91
+ },
92
+ render: props => {
93
+ return (
94
+ <Flex space="lg" direction="column" align="center">
95
+ <Heading size="md">Progress Stepper</Heading>
96
+ <BodyText>Shows progress through multi-step processes</BodyText>
97
+ <ProgressStepper {...props}>
98
+ <ProgressStep id="services-data" status="complete" />
99
+ <ProgressStep id="customer-data" status="complete" />
100
+ <ProgressStep id="shipping-data" status="active" />
101
+ <ProgressStep id="payment-data" status="incomplete" />
102
+ <ProgressStep id="summary" status="incomplete" />
103
+ </ProgressStepper>
104
+ <BodyText>Step 3 of 5</BodyText>
105
+ </Flex>
106
+ );
107
+ },
108
+ };
@@ -0,0 +1,26 @@
1
+ import React, { useMemo, Children, cloneElement, isValidElement } from 'react';
2
+ import ProgressStepperProps from './ProgressStepper.props';
3
+ import ProgressStepperRoot from './ProgressStepperRoot';
4
+
5
+ const ProgressStepper = ({ children, ...rest }: ProgressStepperProps) => {
6
+ // Process children to add index and isLast props
7
+ const processedChildren = useMemo(() => {
8
+ const childrenArray = Children.toArray(children);
9
+ return childrenArray.map((child, index) => {
10
+ if (isValidElement(child)) {
11
+ return cloneElement(child, {
12
+ ...(child.props as any),
13
+ index,
14
+ isLast: index === childrenArray.length - 1,
15
+ });
16
+ }
17
+ return child;
18
+ });
19
+ }, [children]);
20
+
21
+ return <ProgressStepperRoot {...rest}>{processedChildren}</ProgressStepperRoot>;
22
+ };
23
+
24
+ ProgressStepper.displayName = 'ProgressStepper';
25
+
26
+ export default ProgressStepper;
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { View, ViewStyle } from 'react-native';
3
+ import { StyleSheet } from 'react-native-unistyles';
4
+ import { ProgressStepperRootProps } from './ProgressStepper.props';
5
+
6
+ const ProgressStepperRoot = ({
7
+ children,
8
+ style,
9
+ ...rest
10
+ }: ProgressStepperRootProps) => {
11
+ return (
12
+ <View
13
+ style={[styles.root, style as ViewStyle]}
14
+ {...rest}
15
+ >
16
+ {children}
17
+ </View>
18
+ );
19
+ };
20
+
21
+ ProgressStepperRoot.displayName = 'ProgressStepperRoot';
22
+
23
+ const styles = StyleSheet.create(() => ({
24
+ root: {
25
+ flexDirection: 'row',
26
+ alignItems: 'center',
27
+ justifyContent: 'space-between',
28
+ width: '100%',
29
+ },
30
+ }));
31
+
32
+ export default ProgressStepperRoot;
@@ -0,0 +1,3 @@
1
+ export { default as ProgressStepper } from './ProgressStepper';
2
+ export { default as ProgressStep } from './ProgressStep';
3
+ export type { ProgressStepperProps, ProgressStepProps, StepStatus } from './ProgressStepper.props';
@@ -1,5 +1,6 @@
1
- import { ComponentType, Ref } from 'react';
2
- import type { ViewProps, View } from 'react-native';
1
+ import { Ref } from 'react';
2
+ import type { View, ViewProps } from 'react-native';
3
+ import BadgeProps from '../Badge/Badge.props';
3
4
 
4
5
  interface SectionHeaderBaseProps extends Omit<ViewProps, 'children'> {
5
6
  ref?: Ref<View>;
@@ -9,26 +10,18 @@ export interface SectionHeaderWithChildren extends SectionHeaderBaseProps {
9
10
  children: ViewProps['children'];
10
11
  heading?: never;
11
12
  helperText?: never;
12
- linkText?: never;
13
- linkHref?: never;
14
- linkOnPress?: never;
15
- linkTarget?: never;
16
- linkIcon?: never;
17
- linkIconPosition?: never;
18
- linkShowIcon?: never;
13
+ trailingContent?: never;
14
+ badge?: never;
15
+ invalidText?: never;
19
16
  }
20
17
 
21
18
  export interface SectionHeaderWithoutChildren extends SectionHeaderBaseProps {
22
19
  children?: never;
23
20
  heading: string;
24
21
  helperText?: string;
25
- linkText?: string;
26
- linkHref?: string;
27
- linkOnPress?: () => void;
28
- linkTarget?: '_blank' | '_self' | '_parent' | '_top';
29
- linkIcon?: ComponentType;
30
- linkIconPosition?: 'left' | 'right';
31
- linkShowIcon?: boolean;
22
+ trailingContent?: React.ReactNode;
23
+ badge?: BadgeProps;
24
+ invalidText?: string;
32
25
  }
33
26
 
34
27
  type SectionHeaderProps = SectionHeaderWithChildren | SectionHeaderWithoutChildren;