@utilitywarehouse/hearth-react-native 0.4.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/CHANGELOG.md +54 -0
  4. package/build/components/Alert/AlertTitle.js +6 -6
  5. package/build/components/Badge/Badge.js +3 -3
  6. package/build/components/Badge/Badge.props.d.ts +1 -0
  7. package/build/components/Button/ButtonRoot.js +4 -0
  8. package/build/components/Button/ButtonText.js +2 -2
  9. package/build/components/Card/CardRoot.js +1 -1
  10. package/build/components/Carousel/Carousel.context.d.ts +4 -0
  11. package/build/components/Carousel/Carousel.context.js +4 -0
  12. package/build/components/Carousel/Carousel.d.ts +6 -0
  13. package/build/components/Carousel/Carousel.js +278 -0
  14. package/build/components/Carousel/Carousel.props.d.ts +65 -0
  15. package/build/components/Carousel/Carousel.props.js +1 -0
  16. package/build/components/Carousel/CarouselControlItem.d.ts +24 -0
  17. package/build/components/Carousel/CarouselControlItem.js +64 -0
  18. package/build/components/Carousel/CarouselControls.d.ts +4 -0
  19. package/build/components/Carousel/CarouselControls.js +74 -0
  20. package/build/components/Carousel/CarouselItem.d.ts +6 -0
  21. package/build/components/Carousel/CarouselItem.js +38 -0
  22. package/build/components/Carousel/index.d.ts +5 -0
  23. package/build/components/Carousel/index.js +5 -0
  24. package/build/components/Checkbox/CheckboxTextContent.d.ts +1 -1
  25. package/build/components/Checkbox/CheckboxTextContent.js +9 -2
  26. package/build/components/CurrencyInput/CurrencyInput.d.ts +1 -1
  27. package/build/components/CurrencyInput/CurrencyInput.js +3 -3
  28. package/build/components/CurrencyInput/CurrencyInput.props.d.ts +2 -2
  29. package/build/components/DescriptionList/DescriptionList.d.ts +1 -1
  30. package/build/components/DescriptionList/DescriptionList.js +2 -2
  31. package/build/components/DescriptionList/DescriptionList.props.d.ts +1 -8
  32. package/build/components/DescriptionList/DescriptionListItem.d.ts +1 -1
  33. package/build/components/DescriptionList/DescriptionListItem.js +4 -3
  34. package/build/components/DescriptionList/DescriptionListItem.props.d.ts +3 -8
  35. package/build/components/IndicatorIconButton/IndicatorIconButton.d.ts +6 -0
  36. package/build/components/IndicatorIconButton/IndicatorIconButton.js +26 -0
  37. package/build/components/IndicatorIconButton/IndicatorIconButton.props.d.ts +8 -0
  38. package/build/components/IndicatorIconButton/IndicatorIconButton.props.js +1 -0
  39. package/build/components/IndicatorIconButton/index.d.ts +2 -0
  40. package/build/components/IndicatorIconButton/index.js +1 -0
  41. package/build/components/Link/LinkText.js +3 -3
  42. package/build/components/List/List.context.d.ts +0 -2
  43. package/build/components/List/List.d.ts +1 -1
  44. package/build/components/List/List.js +5 -5
  45. package/build/components/List/List.props.d.ts +1 -9
  46. package/build/components/List/ListAction/ListAction.d.ts +18 -0
  47. package/build/components/List/ListAction/ListAction.js +103 -0
  48. package/build/components/List/ListAction/ListAction.props.d.ts +8 -0
  49. package/build/components/List/ListAction/ListAction.props.js +1 -0
  50. package/build/components/List/ListAction/ListActionContent.d.ts +6 -0
  51. package/build/components/List/ListAction/ListActionContent.js +14 -0
  52. package/build/components/List/ListAction/ListActionText.d.ts +6 -0
  53. package/build/components/List/ListAction/ListActionText.js +7 -0
  54. package/build/components/List/ListAction/ListActionTrailingContent.d.ts +6 -0
  55. package/build/components/List/ListAction/ListActionTrailingContent.js +5 -0
  56. package/build/components/List/ListAction/ListActionTrailingIcon.d.ts +9 -0
  57. package/build/components/List/ListAction/ListActionTrailingIcon.js +18 -0
  58. package/build/components/List/ListAction/index.d.ts +6 -0
  59. package/build/components/List/ListAction/index.js +5 -0
  60. package/build/components/List/ListItem/ListItem.context.d.ts +1 -1
  61. package/build/components/List/ListItem/ListItem.props.d.ts +9 -5
  62. package/build/components/List/ListItem/ListItemRoot.d.ts +1 -1
  63. package/build/components/List/ListItem/ListItemRoot.js +10 -12
  64. package/build/components/List/ListItem/index.d.ts +4 -4
  65. package/build/components/List/ListItem/index.js +3 -3
  66. package/build/components/List/index.d.ts +1 -0
  67. package/build/components/List/index.js +1 -0
  68. package/build/components/ProgressStepper/ProgressStep.d.ts +10 -0
  69. package/build/components/ProgressStepper/ProgressStep.js +100 -0
  70. package/build/components/ProgressStepper/ProgressStepper.d.ts +6 -0
  71. package/build/components/ProgressStepper/ProgressStepper.js +22 -0
  72. package/build/components/ProgressStepper/ProgressStepper.props.d.ts +22 -0
  73. package/build/components/ProgressStepper/ProgressStepper.props.js +1 -0
  74. package/build/components/ProgressStepper/ProgressStepperRoot.d.ts +6 -0
  75. package/build/components/ProgressStepper/ProgressStepperRoot.js +16 -0
  76. package/build/components/ProgressStepper/index.d.ts +3 -0
  77. package/build/components/ProgressStepper/index.js +2 -0
  78. package/build/components/Radio/RadioTextContent.d.ts +1 -1
  79. package/build/components/Radio/RadioTextContent.js +9 -2
  80. package/build/components/SectionHeader/SectionHeader.d.ts +1 -1
  81. package/build/components/SectionHeader/SectionHeader.js +6 -3
  82. package/build/components/SectionHeader/SectionHeader.props.d.ts +9 -16
  83. package/build/components/SectionHeader/SectionHeaderTrailingContent.d.ts +6 -0
  84. package/build/components/SectionHeader/SectionHeaderTrailingContent.js +13 -0
  85. package/build/components/SectionHeader/index.d.ts +1 -0
  86. package/build/components/SectionHeader/index.js +1 -0
  87. package/build/components/Tabs/Tab.js +2 -2
  88. package/build/components/ToggleButton/ToggleButtonText.js +2 -2
  89. package/build/components/UnstyledIconButton/UnstyledIconButton.props.d.ts +4 -1
  90. package/build/components/index.d.ts +3 -0
  91. package/build/components/index.js +3 -0
  92. package/build/core/themes.d.ts +12 -24
  93. package/build/tokens/components/dark/button.d.ts +1 -1
  94. package/build/tokens/components/dark/button.js +1 -1
  95. package/build/tokens/components/dark/dialog.d.ts +1 -0
  96. package/build/tokens/components/dark/dialog.js +1 -0
  97. package/build/tokens/components/dark/illustrations.d.ts +1 -0
  98. package/build/tokens/components/dark/illustrations.js +1 -0
  99. package/build/tokens/components/dark/toast.d.ts +4 -1
  100. package/build/tokens/components/dark/toast.js +4 -1
  101. package/build/tokens/components/light/button.d.ts +1 -1
  102. package/build/tokens/components/light/button.js +1 -1
  103. package/build/tokens/components/light/dialog.d.ts +1 -0
  104. package/build/tokens/components/light/dialog.js +1 -0
  105. package/build/tokens/components/light/illustrations.d.ts +1 -0
  106. package/build/tokens/components/light/illustrations.js +1 -0
  107. package/build/tokens/components/light/toast.d.ts +4 -1
  108. package/build/tokens/components/light/toast.js +4 -1
  109. package/build/tokens/layout.d.ts +6 -12
  110. package/build/tokens/layout.js +3 -6
  111. package/build/utils/getFlattenedColorValue.js +2 -19
  112. package/build/utils/index.d.ts +1 -0
  113. package/build/utils/index.js +1 -0
  114. package/build/utils/styleUtils.d.ts +0 -4
  115. package/build/utils/styleUtils.js +0 -50
  116. package/build/utils/themeValueHelpers.d.ts +17 -0
  117. package/build/utils/themeValueHelpers.js +54 -0
  118. package/docs/components/AllComponents.web.tsx +86 -4
  119. package/docs/components/BadgeList.tsx +20 -56
  120. package/docs/components/SwitchList.tsx +4 -8
  121. package/docs/getting-started.mdx +37 -13
  122. package/docs/introduction.mdx +51 -6
  123. package/package.json +7 -7
  124. package/src/components/Alert/AlertTitle.tsx +7 -7
  125. package/src/components/Badge/Badge.props.ts +1 -0
  126. package/src/components/Badge/Badge.tsx +3 -2
  127. package/src/components/Button/ButtonRoot.tsx +4 -0
  128. package/src/components/Button/ButtonText.tsx +3 -3
  129. package/src/components/Card/CardRoot.tsx +2 -0
  130. package/src/components/Carousel/Carousel.context.tsx +8 -0
  131. package/src/components/Carousel/Carousel.docs.mdx +389 -0
  132. package/src/components/Carousel/Carousel.props.ts +89 -0
  133. package/src/components/Carousel/Carousel.stories.tsx +317 -0
  134. package/src/components/Carousel/Carousel.tsx +444 -0
  135. package/src/components/Carousel/CarouselControlItem.tsx +87 -0
  136. package/src/components/Carousel/CarouselControls.tsx +150 -0
  137. package/src/components/Carousel/CarouselItem.tsx +68 -0
  138. package/src/components/Carousel/index.ts +6 -0
  139. package/src/components/Checkbox/CheckboxTextContent.tsx +11 -3
  140. package/src/components/CurrencyInput/CurrencyInput.docs.mdx +4 -4
  141. package/src/components/CurrencyInput/CurrencyInput.props.ts +2 -2
  142. package/src/components/CurrencyInput/CurrencyInput.stories.tsx +17 -15
  143. package/src/components/CurrencyInput/CurrencyInput.tsx +3 -3
  144. package/src/components/DescriptionList/DescriptionList.docs.mdx +24 -27
  145. package/src/components/DescriptionList/DescriptionList.props.ts +1 -8
  146. package/src/components/DescriptionList/DescriptionList.stories.tsx +13 -19
  147. package/src/components/DescriptionList/DescriptionList.tsx +2 -14
  148. package/src/components/DescriptionList/DescriptionListItem.props.ts +3 -8
  149. package/src/components/DescriptionList/DescriptionListItem.tsx +13 -21
  150. package/src/components/IndicatorIconButton/IndicatorIconButton.docs.mdx +85 -0
  151. package/src/components/IndicatorIconButton/IndicatorIconButton.props.ts +12 -0
  152. package/src/components/IndicatorIconButton/IndicatorIconButton.stories.tsx +142 -0
  153. package/src/components/IndicatorIconButton/IndicatorIconButton.tsx +36 -0
  154. package/src/components/IndicatorIconButton/index.tsx +2 -0
  155. package/src/components/Link/LinkText.tsx +4 -4
  156. package/src/components/List/List.context.ts +0 -1
  157. package/src/components/List/List.docs.mdx +376 -179
  158. package/src/components/List/List.props.ts +1 -9
  159. package/src/components/List/List.stories.tsx +289 -38
  160. package/src/components/List/List.tsx +5 -26
  161. package/src/components/List/ListAction/ListAction.props.ts +10 -0
  162. package/src/components/List/ListAction/ListAction.tsx +133 -0
  163. package/src/components/List/ListAction/ListActionContent.tsx +21 -0
  164. package/src/components/List/ListAction/ListActionText.tsx +14 -0
  165. package/src/components/List/ListAction/ListActionTrailingContent.tsx +9 -0
  166. package/src/components/List/ListAction/ListActionTrailingIcon.tsx +32 -0
  167. package/src/components/List/ListAction/index.ts +6 -0
  168. package/src/components/List/ListItem/ListItem.context.ts +1 -1
  169. package/src/components/List/ListItem/ListItem.props.ts +9 -5
  170. package/src/components/List/ListItem/ListItemRoot.tsx +18 -14
  171. package/src/components/List/ListItem/index.ts +4 -4
  172. package/src/components/List/index.ts +1 -0
  173. package/src/components/ProgressStepper/ProgressStep.tsx +134 -0
  174. package/src/components/ProgressStepper/ProgressStepper.docs.mdx +87 -0
  175. package/src/components/ProgressStepper/ProgressStepper.props.ts +27 -0
  176. package/src/components/ProgressStepper/ProgressStepper.stories.tsx +108 -0
  177. package/src/components/ProgressStepper/ProgressStepper.tsx +26 -0
  178. package/src/components/ProgressStepper/ProgressStepperRoot.tsx +32 -0
  179. package/src/components/ProgressStepper/index.ts +3 -0
  180. package/src/components/Radio/RadioTextContent.tsx +11 -3
  181. package/src/components/SectionHeader/SectionHeader.props.ts +9 -16
  182. package/src/components/SectionHeader/SectionHeader.stories.tsx +28 -18
  183. package/src/components/SectionHeader/SectionHeader.tsx +18 -19
  184. package/src/components/SectionHeader/SectionHeaderTrailingContent.tsx +20 -0
  185. package/src/components/SectionHeader/Sectionheader.docs.mdx +9 -24
  186. package/src/components/SectionHeader/index.ts +1 -0
  187. package/src/components/Switch/Switch.docs.mdx +0 -4
  188. package/src/components/Tabs/Tab.tsx +4 -2
  189. package/src/components/ToggleButton/ToggleButtonText.tsx +3 -3
  190. package/src/components/UnstyledIconButton/UnstyledIconButton.props.ts +2 -1
  191. package/src/components/index.ts +3 -0
  192. package/src/tokens/components/dark/button.ts +1 -1
  193. package/src/tokens/components/dark/dialog.ts +1 -0
  194. package/src/tokens/components/dark/illustrations.ts +1 -0
  195. package/src/tokens/components/dark/toast.ts +4 -1
  196. package/src/tokens/components/light/button.ts +1 -1
  197. package/src/tokens/components/light/dialog.ts +1 -0
  198. package/src/tokens/components/light/illustrations.ts +1 -0
  199. package/src/tokens/components/light/toast.ts +4 -1
  200. package/src/tokens/layout.ts +3 -6
  201. package/src/utils/getFlattenedColorValue.ts +2 -21
  202. package/src/utils/getStyleValue.ts +0 -3
  203. package/src/utils/index.ts +1 -0
  204. package/src/utils/styleUtils.ts +0 -57
  205. package/src/utils/themeValueHelpers.ts +60 -0
@@ -0,0 +1,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 = ({ state, index = 0, isLast = false, ...rest }: ProgressStepInternalProps) => {
15
+ styles.useVariants({ state, 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}, ${state}`}
30
+ {...rest}
31
+ >
32
+ <View style={styles.step}>
33
+ {state === 'complete' ? (
34
+ <Icon as={TickSmallIcon} width={20} height={20} style={styles.text} />
35
+ ) : state === '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
+ state: {
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
+ state: {
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
+ state: {
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 States](#step-states-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"} state="complete" />
33
+ <ProgressStep id={"2"} state="complete" />
34
+ <ProgressStep id={"3"} state="active" />
35
+ <ProgressStep id={"4"} state="incomplete" />
36
+ <ProgressStep id={"5"} state="incomplete" />
37
+ </ProgressStepper>
38
+ ```
39
+
40
+ ## Examples
41
+
42
+ ### Default Layout
43
+
44
+ <Canvas of={ProgressStepperStories.Playground} />
45
+
46
+ ### Step States
47
+
48
+ See how different step states are visualized:
49
+
50
+ <Canvas of={ProgressStepperStories.StepStates} />
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
+ | `state` | `'complete' \| 'active' \| 'incomplete'` | Required | Current state of the step |
70
+
71
+ ## Step States
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 StepState = '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 state of the step
19
+ */
20
+ state: StepState;
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'} state="complete" />
26
+ <ProgressStep id={'2'} state="complete" />
27
+ <ProgressStep id={'3'} state="active" />
28
+ <ProgressStep id={'4'} state="incomplete" />
29
+ </ProgressStepper>
30
+ <Flex direction="row" space="lg" style={{ width: '100%' }}>
31
+ <ProgressStep id={'1'} state="complete" />
32
+ </Flex>
33
+ <Flex direction="row" space="lg" style={{ width: '100%' }}>
34
+ <ProgressStep id={'3'} state="active" />
35
+ </Flex>
36
+ <Flex direction="row" space="lg" style={{ width: '100%' }}>
37
+ <ProgressStep id={'5'} state="incomplete" />
38
+ </Flex>
39
+ </Flex>
40
+ ),
41
+ },
42
+ };
43
+
44
+ export const StepStates: 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'} state="incomplete" />
57
+ <ProgressStep id={'2'} state="incomplete" />
58
+ <ProgressStep id={'3'} state="incomplete" />
59
+ </ProgressStepper>
60
+ </VariantTitle>
61
+ <VariantTitle title="One Active Step">
62
+ <ProgressStepper {...props}>
63
+ <ProgressStep id={'1'} state="active" />
64
+ <ProgressStep id={'2'} state="incomplete" />
65
+ <ProgressStep id={'3'} state="incomplete" />
66
+ </ProgressStepper>
67
+ </VariantTitle>
68
+ <VariantTitle title="Mixed States">
69
+ <ProgressStepper {...props}>
70
+ <ProgressStep id={'1'} state="complete" />
71
+ <ProgressStep id={'2'} state="complete" />
72
+ <ProgressStep id={'3'} state="active" />
73
+ <ProgressStep id={'4'} state="incomplete" />
74
+ </ProgressStepper>
75
+ </VariantTitle>
76
+ <VariantTitle title="All Completed">
77
+ <ProgressStepper {...props}>
78
+ <ProgressStep id={'1'} state="complete" />
79
+ <ProgressStep id={'2'} state="complete" />
80
+ <ProgressStep id={'3'} state="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" state="complete" />
99
+ <ProgressStep id="customer-data" state="complete" />
100
+ <ProgressStep id="shipping-data" state="active" />
101
+ <ProgressStep id="payment-data" state="incomplete" />
102
+ <ProgressStep id="summary" state="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, StepState } from './ProgressStepper.props';
@@ -1,14 +1,22 @@
1
- import FlexProps from '../Flex/Flex.props';
1
+ import { StyleSheet } from 'react-native-unistyles';
2
2
  import { Flex } from '../Flex';
3
+ import FlexProps from '../Flex/Flex.props';
3
4
 
4
- const RadioTextContent = ({ children, ...props }: FlexProps) => {
5
+ const RadioTextContent = ({ children, style, ...props }: FlexProps) => {
5
6
  return (
6
- <Flex direction="column" space="none" {...props}>
7
+ <Flex direction="column" space="none" style={[styles.content, style]} {...props}>
7
8
  {children}
8
9
  </Flex>
9
10
  );
10
11
  };
11
12
 
13
+ const styles = StyleSheet.create({
14
+ content: {
15
+ flex: 1,
16
+ flexShrink: 1,
17
+ },
18
+ });
19
+
12
20
  RadioTextContent.displayName = 'RadioTextContent';
13
21
 
14
22
  export default RadioTextContent;
@@ -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;