@utilitywarehouse/hearth-react-native 0.16.2 → 0.18.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 (75) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +14 -14
  3. package/CHANGELOG.md +174 -0
  4. package/build/components/BodyText/BodyText.js +2 -2
  5. package/build/components/Card/CardAction/CardActionRoot.js +12 -2
  6. package/build/components/Card/CardActions.context.d.ts +6 -0
  7. package/build/components/Card/CardActions.context.js +5 -0
  8. package/build/components/Card/CardActions.d.ts +7 -0
  9. package/build/components/Card/CardActions.js +29 -0
  10. package/build/components/Card/CardRoot.js +16 -104
  11. package/build/components/Card/helpers.d.ts +8 -0
  12. package/build/components/Card/helpers.js +146 -0
  13. package/build/components/Card/index.d.ts +2 -0
  14. package/build/components/Card/index.js +2 -0
  15. package/build/components/ExpandableCard/ExpandableCardGroup.d.ts +1 -1
  16. package/build/components/ExpandableCard/ExpandableCardGroup.js +2 -2
  17. package/build/components/ExpandableCard/ExpandableCardGroup.props.d.ts +4 -0
  18. package/build/components/IconButton/IconButton.props.d.ts +19 -0
  19. package/build/components/IconButton/IconButtonRoot.d.ts +1 -1
  20. package/build/components/IconButton/IconButtonRoot.js +43 -2
  21. package/build/components/Input/Input.js +4 -3
  22. package/build/components/Input/Input.props.d.ts +9 -0
  23. package/build/components/List/List.context.d.ts +4 -2
  24. package/build/components/List/List.context.js +0 -2
  25. package/build/components/List/List.d.ts +1 -1
  26. package/build/components/List/List.js +25 -38
  27. package/build/components/List/List.props.d.ts +1 -0
  28. package/build/components/List/ListAction/ListAction.js +24 -7
  29. package/build/components/List/ListAction/ListAction.props.d.ts +1 -0
  30. package/build/components/List/ListItem/ListItemHelperText.d.ts +1 -1
  31. package/build/components/List/ListItem/ListItemHelperText.js +2 -2
  32. package/build/components/List/ListItem/ListItemRoot.js +12 -4
  33. package/build/utils/isThemedImageProps.d.ts +1 -1
  34. package/package.json +2 -2
  35. package/src/components/BodyText/BodyText.tsx +2 -2
  36. package/src/components/Card/Card.docs.mdx +224 -66
  37. package/src/components/Card/Card.stories.tsx +29 -25
  38. package/src/components/Card/CardAction/CardAction.stories.tsx +239 -93
  39. package/src/components/Card/CardAction/CardActionRoot.tsx +15 -2
  40. package/src/components/Card/CardActions.context.ts +12 -0
  41. package/src/components/Card/CardActions.tsx +40 -0
  42. package/src/components/Card/CardRoot.tsx +27 -132
  43. package/src/components/Card/helpers.tsx +195 -0
  44. package/src/components/Card/index.ts +2 -0
  45. package/src/components/ExpandableCard/ExpandableCard.figma.tsx +33 -38
  46. package/src/components/ExpandableCard/ExpandableCardGroup.figma.tsx +34 -17
  47. package/src/components/ExpandableCard/ExpandableCardGroup.props.ts +5 -0
  48. package/src/components/ExpandableCard/ExpandableCardGroup.tsx +2 -0
  49. package/src/components/HighlightBanner/HighlightBanner.figma.tsx +46 -0
  50. package/src/components/IconButton/IconButton.docs.mdx +91 -9
  51. package/src/components/IconButton/IconButton.figma.tsx +20 -30
  52. package/src/components/IconButton/IconButton.props.ts +19 -0
  53. package/src/components/IconButton/IconButton.stories.tsx +56 -0
  54. package/src/components/IconButton/IconButtonRoot.tsx +54 -1
  55. package/src/components/IconContainer/IconContainer.figma.tsx +7 -13
  56. package/src/components/IndicatorIconButton/IndicatorIconButton.figma.tsx +16 -0
  57. package/src/components/Input/Input.docs.mdx +55 -15
  58. package/src/components/Input/Input.figma.tsx +106 -40
  59. package/src/components/Input/Input.props.ts +9 -0
  60. package/src/components/Input/Input.tsx +21 -0
  61. package/src/components/Link/Link.figma.tsx +31 -38
  62. package/src/components/List/List.context.ts +2 -4
  63. package/src/components/List/List.docs.mdx +10 -5
  64. package/src/components/List/List.figma.tsx +42 -28
  65. package/src/components/List/List.props.ts +1 -0
  66. package/src/components/List/List.stories.tsx +43 -0
  67. package/src/components/List/List.tsx +38 -51
  68. package/src/components/List/ListAction/ListAction.figma.tsx +5 -13
  69. package/src/components/List/ListAction/ListAction.props.ts +1 -0
  70. package/src/components/List/ListAction/ListAction.tsx +40 -10
  71. package/src/components/List/ListItem/ListItem.figma.tsx +43 -27
  72. package/src/components/List/ListItem/ListItemHelperText.tsx +2 -2
  73. package/src/components/List/ListItem/ListItemRoot.tsx +15 -4
  74. package/src/utils/isThemedImageProps.ts +1 -1
  75. package/src/components/InlineLink/InlineLink.figma.tsx +0 -33
@@ -1,104 +1,18 @@
1
- import React, { ReactNode, useMemo } from 'react';
1
+ import { ReactNode, useMemo } from 'react';
2
2
  import { GestureResponderEvent, Pressable, ViewStyle } from 'react-native';
3
3
  import { StyleSheet } from 'react-native-unistyles';
4
4
  import { useStyleProps } from '../../hooks';
5
5
  import { CardContext } from './Card.context';
6
6
  import CardProps from './Card.props';
7
+ import CardActions from './CardActions';
7
8
  import CardContent from './CardContent';
8
-
9
- // Helper to check if children contain specific component types
10
- const checkForComponentType = (children: React.ReactNode, displayName: string): boolean => {
11
- return React.Children.toArray(children).some(child => {
12
- if (React.isValidElement(child)) {
13
- // @ts-expect-error - type
14
- if (child.type.displayName === displayName) {
15
- return true;
16
- }
17
- const childProps = child.props as any;
18
- if (childProps.children) {
19
- return checkForComponentType(childProps.children, displayName);
20
- }
21
- }
22
- return false;
23
- });
24
- };
25
-
26
- // Helper to filter out specific component types from children
27
- const filterChildren = (children: React.ReactNode, excludeDisplayName: string): React.ReactNode => {
28
- return React.Children.map(children, child => {
29
- if (React.isValidElement(child)) {
30
- // @ts-expect-error - type
31
- if (child.type.displayName === excludeDisplayName) {
32
- return null;
33
- }
34
- const childProps = child.props as any;
35
- if (childProps.children) {
36
- return React.cloneElement(child, {
37
- ...childProps,
38
- children: filterChildren(childProps.children, excludeDisplayName),
39
- });
40
- }
41
- }
42
- return child;
43
- });
44
- };
45
-
46
- // Helper to extract specific component types from children
47
- const extractChildren = (
48
- children: React.ReactNode,
49
- includeDisplayName: string,
50
- markFirst = false
51
- ): React.ReactNode => {
52
- let isFirstFound = false;
53
- return React.Children.map(children, child => {
54
- if (React.isValidElement(child)) {
55
- // @ts-expect-error - type
56
- if (child.type.displayName === includeDisplayName) {
57
- const isFirst = markFirst && !isFirstFound;
58
- if (isFirst) {
59
- isFirstFound = true;
60
- }
61
- return markFirst
62
- ? React.cloneElement(child, { ...(child.props || {}), isFirst } as any)
63
- : child;
64
- }
65
- const childProps = child.props as any;
66
- if (childProps.children) {
67
- return extractChildren(childProps.children, includeDisplayName, markFirst);
68
- }
69
- }
70
- return null;
71
- });
72
- };
73
-
74
- // Helper that recursively collects onPress or other defined handlers from descendants
75
- const collectChildActionHandlers = (
76
- children: React.ReactNode
77
- ): Array<(e: GestureResponderEvent) => void> =>
78
- React.Children.toArray(children).reduce(
79
- (handlers, child) => {
80
- if (React.isValidElement(child)) {
81
- const childProps = child.props as any;
82
- // @ts-expect-error - type
83
- if (child.type.displayName === 'CardPressHandler') {
84
- const actionChildren = React.Children.toArray(childProps.children);
85
- const handlerToInherit = childProps['handlerToInherit'] || 'onPress';
86
- const firstChild = actionChildren[0];
87
- if (
88
- React.isValidElement(firstChild) &&
89
- typeof (firstChild.props as any)[handlerToInherit] === 'function'
90
- ) {
91
- handlers.push((firstChild.props as any)[handlerToInherit]);
92
- }
93
- }
94
- if (childProps.children) {
95
- handlers.push(...collectChildActionHandlers(childProps.children));
96
- }
97
- }
98
- return handlers;
99
- },
100
- [] as Array<(e: GestureResponderEvent) => void>
101
- );
9
+ import {
10
+ checkForComponentType,
11
+ collectChildActionHandlers,
12
+ extractCardActions,
13
+ filterChildren,
14
+ hasOnlyPotentialActions,
15
+ } from './helpers';
102
16
 
103
17
  const Card = ({
104
18
  children,
@@ -116,8 +30,8 @@ const Card = ({
116
30
  }: CardProps & { states?: { active?: boolean; disabled?: boolean } }) => {
117
31
  const { active } = states || { active: false };
118
32
  const childActionHandlers = collectChildActionHandlers(children as ReactNode);
119
- const hasActions = checkForComponentType(children as ReactNode, 'CardAction');
120
- const hasContent = checkForComponentType(children as ReactNode, 'CardContent');
33
+ const hasActions = checkForComponentType(children as ReactNode, CardActions);
34
+ const hasContent = checkForComponentType(children as ReactNode, CardContent);
121
35
  // Extract style props using our custom hook
122
36
  const { computedStyles, remainingProps } = useStyleProps(rest);
123
37
 
@@ -132,19 +46,9 @@ const Card = ({
132
46
  const inheritChildAction = childActionHandlers.length > 0;
133
47
  const showPressed = inheritChildAction || !!onPress;
134
48
 
135
- const filteredChildren =
136
- !hasContent && hasActions ? filterChildren(children as ReactNode, 'CardAction') : null;
137
-
138
- // Check if there's any content besides CardActions
139
- const hasOnlyActions =
140
- hasActions &&
141
- !hasContent &&
142
- React.Children.toArray(filteredChildren).filter(child => child != null).length === 0;
143
-
144
- const filteredCardActions =
145
- !hasContent && hasActions
146
- ? extractChildren(children as ReactNode, 'CardAction', hasOnlyActions)
147
- : null;
49
+ // Check if all children are action groups (CardActions)
50
+ const potentiallyOnlyActions = hasOnlyPotentialActions(children as ReactNode, CardActions);
51
+ const hasOnlyActions = potentiallyOnlyActions && !hasContent;
148
52
 
149
53
  const context = useMemo(
150
54
  () => ({
@@ -162,38 +66,30 @@ const Card = ({
162
66
  styles.useVariants({
163
67
  variant,
164
68
  colorScheme,
165
- noPadding: noPadding || hasActions || hasContent,
69
+ noPadding: noPadding || hasActions || hasContent || hasOnlyActions,
166
70
  active,
167
71
  showPressed,
168
72
  disabled,
169
- space: hasActions || hasContent ? 'none' : space,
73
+ space: hasActions || hasContent || hasOnlyActions ? 'none' : space,
170
74
  shadowColor,
171
75
  });
172
76
 
173
77
  const renderChildren = () => {
174
- // Default: render children as-is
175
- if (hasContent || !hasActions) {
78
+ // Explicit CardContent used - render as-is or Card has only actions (or potential action wrappers) - render children directly
79
+ if (hasContent || hasOnlyActions || !hasActions) {
176
80
  return children as ReactNode;
177
81
  }
178
82
 
179
- // Card has actions but no explicit CardContent
180
- if (hasOnlyActions) {
181
- // Only CardActions, no other content - render actions directly
182
- return filteredCardActions as ReactNode;
183
- }
83
+ // Has both actions and other content - wrap non-action content and render actions separately
84
+ const filteredNonActionChildren = filterChildren(children as ReactNode, CardActions);
85
+ const cardActions = extractCardActions(children as ReactNode, CardActions);
184
86
 
185
- if (filteredChildren) {
186
- // Has both actions and other content - wrap content and render actions below
187
- return (
188
- <>
189
- <CardContent>{filteredChildren as ReactNode}</CardContent>
190
- {filteredCardActions as ReactNode}
191
- </>
192
- );
193
- }
194
-
195
- // Fallback
196
- return children as ReactNode;
87
+ return (
88
+ <>
89
+ <CardContent>{filteredNonActionChildren as ReactNode}</CardContent>
90
+ {cardActions}
91
+ </>
92
+ );
197
93
  };
198
94
 
199
95
  return (
@@ -298,7 +194,6 @@ const styles = StyleSheet.create(theme => ({
298
194
  },
299
195
  },
300
196
  },
301
-
302
197
  active: {
303
198
  true: {},
304
199
  },
@@ -0,0 +1,195 @@
1
+ import React from 'react';
2
+ import { GestureResponderEvent } from 'react-native';
3
+
4
+ const getInheritableHandler = (
5
+ child: React.ReactNode,
6
+ handlerToInherit: string
7
+ ): ((e: GestureResponderEvent) => void) | null => {
8
+ if (!React.isValidElement(child)) {
9
+ return null;
10
+ }
11
+
12
+ const childProps = child.props as any;
13
+ const isDisabled = !!childProps.disabled || !!childProps.loading;
14
+ if (isDisabled || typeof childProps[handlerToInherit] !== 'function') {
15
+ return null;
16
+ }
17
+
18
+ return childProps[handlerToInherit];
19
+ };
20
+
21
+ // Helper to check if children contain specific component types
22
+ export const checkForComponentType = (
23
+ children: React.ReactNode,
24
+ componentType: React.ComponentType<any>
25
+ ): boolean => {
26
+ return React.Children.toArray(children).some(child => {
27
+ if (React.isValidElement(child)) {
28
+ if (child.type === componentType) {
29
+ return true;
30
+ }
31
+ const childProps = child.props as any;
32
+ if (childProps.children) {
33
+ return checkForComponentType(childProps.children, componentType);
34
+ }
35
+ }
36
+ return false;
37
+ });
38
+ };
39
+
40
+ // Check if a component has "content" in its children (not just actions)
41
+ export const hasContentInChildren = (
42
+ child: React.ReactNode,
43
+ actionType: React.ComponentType<any>
44
+ ): boolean => {
45
+ if (!React.isValidElement(child)) {
46
+ return false;
47
+ }
48
+
49
+ const childProps = child.props as any;
50
+ if (!childProps?.children) {
51
+ return false;
52
+ }
53
+
54
+ // Check if children contain anything other than the action type
55
+ const childrenArray = React.Children.toArray(childProps.children);
56
+ return childrenArray.some(c => {
57
+ if (!React.isValidElement(c)) {
58
+ // Text, numbers, etc - this is content
59
+ return c != null;
60
+ }
61
+ return c.type !== actionType;
62
+ });
63
+ };
64
+
65
+ // Check if all children are CardActions groups
66
+ export const hasOnlyPotentialActions = (
67
+ children: React.ReactNode,
68
+ actionType: React.ComponentType<any>
69
+ ): boolean => {
70
+ const childArray = React.Children.toArray(children);
71
+ if (childArray.length === 0) {
72
+ return false;
73
+ }
74
+
75
+ let hasActionCandidate = false;
76
+
77
+ for (const child of childArray) {
78
+ if (!React.isValidElement(child)) {
79
+ if (child != null) {
80
+ return false;
81
+ }
82
+ continue;
83
+ }
84
+
85
+ if (child.type === actionType) {
86
+ hasActionCandidate = true;
87
+ continue;
88
+ }
89
+
90
+ if (checkForComponentType(child, actionType)) {
91
+ hasActionCandidate = true;
92
+ continue;
93
+ }
94
+
95
+ if (typeof child.type === 'string') {
96
+ return false;
97
+ }
98
+
99
+ if (hasContentInChildren(child, actionType)) {
100
+ return false;
101
+ }
102
+
103
+ return false;
104
+ }
105
+
106
+ return hasActionCandidate || checkForComponentType(children, actionType);
107
+ };
108
+
109
+ // Helper to filter out specific component types from children
110
+ export const filterChildren = (
111
+ children: React.ReactNode,
112
+ excludeComponentType: React.ComponentType<any>
113
+ ): React.ReactNode => {
114
+ return React.Children.map(children, child => {
115
+ if (React.isValidElement(child)) {
116
+ if (child.type === excludeComponentType) {
117
+ return null;
118
+ }
119
+
120
+ // Check if this child contains the excludeComponentType
121
+ if (checkForComponentType(child, excludeComponentType)) {
122
+ return null; // This child or its descendants contain the action
123
+ }
124
+
125
+ const childProps = child.props as any;
126
+ if (childProps.children) {
127
+ const filteredChildren = filterChildren(childProps.children, excludeComponentType);
128
+ // Only preserve wrapper if it has non-null children
129
+ const hasContent = React.Children.toArray(filteredChildren).some(c => c != null);
130
+ if (!hasContent) {
131
+ return null;
132
+ }
133
+ return React.cloneElement(child, {
134
+ ...childProps,
135
+ children: filteredChildren,
136
+ });
137
+ }
138
+ }
139
+ return child;
140
+ });
141
+ };
142
+
143
+ // Helper to extract only CardActions (preserving wrapper components)
144
+ export const extractCardActions = (
145
+ children: React.ReactNode,
146
+ actionType: React.ComponentType<any>
147
+ ): React.ReactNode => {
148
+ const recursiveExtract = (children: React.ReactNode): React.ReactNode => {
149
+ return React.Children.map(children, child => {
150
+ if (!React.isValidElement(child)) return null;
151
+
152
+ // Direct action
153
+ if (child.type === actionType) {
154
+ return child;
155
+ }
156
+
157
+ // If this child contains a CardActions wrapper in its tree, keep it
158
+ if (checkForComponentType(child, actionType) && !hasContentInChildren(child, actionType)) {
159
+ return child;
160
+ }
161
+
162
+ return null;
163
+ });
164
+ };
165
+
166
+ return recursiveExtract(children);
167
+ };
168
+
169
+ // Helper that recursively collects onPress or other defined handlers from descendants
170
+ export const collectChildActionHandlers = (
171
+ children: React.ReactNode
172
+ ): Array<(e: GestureResponderEvent) => void> =>
173
+ React.Children.toArray(children).reduce(
174
+ (handlers, child) => {
175
+ if (React.isValidElement(child)) {
176
+ const childProps = child.props as any;
177
+ // Check using displayName as CardPressHandler might not be directly importable
178
+ // @ts-expect-error - type
179
+ if (child.type?.displayName === 'CardPressHandler') {
180
+ const actionChildren = React.Children.toArray(childProps.children);
181
+ const handlerToInherit = childProps['handlerToInherit'] || 'onPress';
182
+ const firstChild = actionChildren[0];
183
+ const handler = getInheritableHandler(firstChild, handlerToInherit);
184
+ if (handler) {
185
+ handlers.push(handler);
186
+ }
187
+ }
188
+ if (childProps.children) {
189
+ handlers.push(...collectChildActionHandlers(childProps.children));
190
+ }
191
+ }
192
+ return handlers;
193
+ },
194
+ [] as Array<(e: GestureResponderEvent) => void>
195
+ );
@@ -1,5 +1,7 @@
1
1
  export { default as Card } from './Card';
2
2
  export { useCardContext } from './Card.context';
3
3
  export * from './CardAction';
4
+ export { default as CardActions } from './CardActions';
5
+ export { default as CardContent } from './CardContent';
4
6
  export { default as CardPressHandler } from './CardPressHandler';
5
7
  export { useCardPressHandlerContext } from './CardPressHandler.context';
@@ -1,54 +1,49 @@
1
- import React from "react"
2
- import ExpandableCard from "./ExpandableCard"
3
- import figma from "@figma/code-connect"
4
-
5
- /**
6
- * -- This file was auto-generated by Code Connect --
7
- * `props` includes a mapping from your code props to Figma properties.
8
- * You should check this is correct, and update the `example` function
9
- * to return the code example you'd like to see in Figma
10
- */
1
+ import figma from '@figma/code-connect';
2
+ import { ExpandableCard } from '../';
3
+ import { IconContainer } from '../IconContainer';
11
4
 
12
5
  figma.connect(
13
6
  ExpandableCard,
14
- "https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=7222%3A5935",
7
+ 'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=7222%3A5935',
15
8
  {
16
9
  props: {
17
- // These props were automatically mapped based on your linked code:
18
- expanded: figma.boolean("Expand?"),
19
- heading: figma.string("Heading"),
20
- helperText: figma.string("Helper text"),
21
- expandedContent: figma.enum("Expand?", {
22
- False: false,
23
- True: true,
10
+ expanded: figma.boolean('Expand?'),
11
+ heading: figma.string('Heading'),
12
+ helperText: figma.string('Helper text'),
13
+ leadingContent: figma.boolean('Leading content?', {
14
+ true: figma.nestedProps('Leading content', {
15
+ variant: figma.enum('Variant', {
16
+ Icon: figma.instance('Icon-24'),
17
+ 'Icon Container': <IconContainer icon={figma.instance('Icon-24')} />,
18
+ }),
19
+ }),
24
20
  }),
25
- disabled: figma.enum("State", {
21
+ disabled: figma.enum('State', {
26
22
  Active: true,
27
23
  }),
28
- collapsable: figma.boolean("Expand?"),
29
- focusable: figma.enum("State", {
24
+ collapsable: figma.boolean('Expand?'),
25
+ focusable: figma.enum('State', {
30
26
  Focus: true,
31
27
  }),
32
- // No matching props could be found for these Figma properties:
33
- // "customContent": figma.instance('Custom content'),
34
- // "heading": figma.string('Heading'),
35
- // "helperText": figma.string('Helper text'),
36
- // "helperText": figma.boolean('Helper text?'),
37
- // "numericalValue": figma.boolean('Numerical value?'),
38
- // "numericalValue": figma.string('Numerical value'),
39
- // "leadingContent": figma.boolean('Leading content?'),
40
- // "badge": figma.boolean('Badge?')
28
+ content: figma.instance('Custom content'),
29
+ numericalValue: figma.boolean('Numerical value?', {
30
+ true: figma.string('Numerical value'),
31
+ }),
32
+ badge: figma.boolean('Badge?', {
33
+ true: figma.instance('Badge'),
34
+ }),
41
35
  },
42
- example: (props) => (
36
+ example: props => (
43
37
  <ExpandableCard
44
38
  expanded={props.expanded}
45
39
  heading={props.heading}
46
40
  helperText={props.helperText}
47
- expandedContent={props.expandedContent}
48
- disabled={props.disabled}
49
- collapsable={props.collapsable}
50
- focusable={props.focusable}
51
- />
41
+ leadingContent={props.leadingContent?.variant}
42
+ numericValue={props.numericalValue}
43
+ badge={props.badge}
44
+ >
45
+ {props.content}
46
+ </ExpandableCard>
52
47
  ),
53
- },
54
- )
48
+ }
49
+ );
@@ -1,23 +1,40 @@
1
- import React from "react"
2
- import ExpandableCardGroup from "./ExpandableCardGroup"
3
- import figma from "@figma/code-connect"
4
-
5
- /**
6
- * -- This file was auto-generated by Code Connect --
7
- * None of your props could be automatically mapped to Figma properties.
8
- * You should update the `props` object to include a mapping from your
9
- * code props to Figma properties, and update the `example` function to
10
- * return the code example you'd like to see in Figma
11
- */
1
+ import figma from '@figma/code-connect';
2
+ import { ExpandableCardGroup } from '../';
12
3
 
13
4
  figma.connect(
14
5
  ExpandableCardGroup,
15
- "https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR?node-id=7222%3A7221",
6
+ 'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=7222-7221&m=dev',
16
7
  {
17
8
  props: {
18
- // No matching props could be found for these Figma properties:
19
- // "sectionHeader": figma.boolean('Section header?')
9
+ sectionHeader: figma.boolean('Section header?', {
10
+ true: figma.nestedProps('Section Header', {
11
+ heading: figma.string('Heading'),
12
+ helperText: figma.boolean('Helper text?', {
13
+ true: figma.string('Helper text'),
14
+ }),
15
+ trailingContent: figma.boolean('Trailing content?', {
16
+ true: figma.nestedProps('Trailing content', {
17
+ headerTrailingContent: figma.instance('Variant'),
18
+ }),
19
+ }),
20
+ invalidText: figma.enum('State', {
21
+ Invalid: figma.nestedProps('Validation Text', {
22
+ invalidText: figma.string('Text'),
23
+ }),
24
+ }),
25
+ }),
26
+ }),
27
+ cards: figma.children('Expandable Card'),
20
28
  },
21
- example: (props) => <ExpandableCardGroup />,
22
- },
23
- )
29
+ example: props => (
30
+ <ExpandableCardGroup
31
+ heading={props.sectionHeader?.heading}
32
+ helperText={props.sectionHeader?.helperText}
33
+ headerTrailingContent={props.sectionHeader?.trailingContent?.headerTrailingContent}
34
+ invalidText={props.sectionHeader?.invalidText?.invalidText}
35
+ >
36
+ {props.cards}
37
+ </ExpandableCardGroup>
38
+ ),
39
+ }
40
+ );
@@ -26,6 +26,11 @@ export interface ExpandableCardGroupProps extends ViewProps {
26
26
  * Test ID for testing
27
27
  */
28
28
  testID?: string;
29
+
30
+ /**
31
+ * Validation text displayed below the helper text when in an invalid state
32
+ */
33
+ invalidText?: string;
29
34
  }
30
35
 
31
36
  export default ExpandableCardGroupProps;
@@ -10,6 +10,7 @@ const ExpandableCardGroup = ({
10
10
  children,
11
11
  style,
12
12
  testID = 'expandable-card-group',
13
+ invalidText,
13
14
  ...props
14
15
  }: ExpandableCardGroupProps) => {
15
16
  return (
@@ -19,6 +20,7 @@ const ExpandableCardGroup = ({
19
20
  heading={heading}
20
21
  helperText={helperText}
21
22
  trailingContent={headerTrailingContent}
23
+ invalidText={invalidText}
22
24
  />
23
25
  ) : null}
24
26
  <View style={styles.cardsContainer}>{children}</View>
@@ -0,0 +1,46 @@
1
+ import figma from '@figma/code-connect';
2
+ import { HighlightBanner, Image } from '..';
3
+
4
+ figma.connect(
5
+ HighlightBanner,
6
+ 'https://www.figma.com/design/6NKZXZhFSExXrcbBgc6zTR/Hearth-Components---Tokens?node-id=8306-4665&t=3uUSBVdxldgG5uz3-4',
7
+ {
8
+ props: {
9
+ heading: figma.string('Heading'),
10
+ description: figma.string('Description'),
11
+ image: figma.boolean('Image?', {
12
+ true: <Image source={{ uri: '' }} />,
13
+ }),
14
+ headingColor: figma.enum('Heading color', {
15
+ Highlight: 'highlight',
16
+ Pig: 'pig',
17
+ Energy: 'energy',
18
+ Broadband: 'broadband',
19
+ Insurance: 'insurance',
20
+ Cashback: 'cashback',
21
+ Mobile: 'mobile',
22
+ }),
23
+ link: figma.boolean('Link?', {
24
+ true: figma.children('Link'),
25
+ }),
26
+ button: figma.boolean('Button?', {
27
+ true: figma.children('Button'),
28
+ }),
29
+ },
30
+ example: props => {
31
+ return (
32
+ // HighlightBanner doesn't need to be wrapped in a Card
33
+ // it's a standalone component in code please see the HighlightBanner docs
34
+ // https://hearth.prod.uw.systems/react-native/?path=/docs/components-highlightbanner--docs
35
+ <HighlightBanner
36
+ heading={props.heading}
37
+ description={props.description}
38
+ image={props.image}
39
+ headingColor={props.headingColor}
40
+ link={props.link}
41
+ button={props.button}
42
+ />
43
+ );
44
+ },
45
+ }
46
+ );