jfs-components 0.0.1 → 0.0.2

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 (96) hide show
  1. package/lib/commonjs/components/ActionFooter/ActionFooter.js +124 -0
  2. package/lib/commonjs/components/ActionFooter/ActionFooter.js.map +1 -0
  3. package/lib/commonjs/components/ActionFooter/ActionFooter.mdx +101 -0
  4. package/lib/commonjs/components/Button/Button.js +1 -1
  5. package/lib/commonjs/components/Button/Button.js.map +1 -1
  6. package/lib/commonjs/components/Button/Button.mdx +2 -2
  7. package/lib/commonjs/components/CardFeedback/CardFeedback.js +47 -11
  8. package/lib/commonjs/components/CardFeedback/CardFeedback.js.map +1 -1
  9. package/lib/commonjs/components/CardFeedback/CardFeedback.mdx +1 -0
  10. package/lib/commonjs/components/Divider/Divider.js +63 -0
  11. package/lib/commonjs/components/Divider/Divider.js.map +1 -0
  12. package/lib/commonjs/components/Divider/Divider.mdx +91 -0
  13. package/lib/commonjs/components/ListItem/ListItem.js +24 -13
  14. package/lib/commonjs/components/ListItem/ListItem.js.map +1 -1
  15. package/lib/commonjs/components/ListItem/ListItem.mdx +46 -5
  16. package/lib/commonjs/components/MerchantProfile/MerchantProfile.js +133 -0
  17. package/lib/commonjs/components/MerchantProfile/MerchantProfile.js.map +1 -0
  18. package/lib/commonjs/components/MerchantProfile/MerchantProfile.mdx +139 -0
  19. package/lib/commonjs/components/MoneyValue/MoneyValue.mdx +4 -0
  20. package/lib/commonjs/components/NavArrow/NavArrow.js +90 -0
  21. package/lib/commonjs/components/NavArrow/NavArrow.js.map +1 -0
  22. package/lib/commonjs/components/NavArrow/NavArrow.mdx +123 -0
  23. package/lib/commonjs/components/Section/Section.mdx +4 -4
  24. package/lib/commonjs/components/Stepper/Step.mdx +1 -1
  25. package/lib/commonjs/components/index.js +28 -0
  26. package/lib/commonjs/components/index.js.map +1 -1
  27. package/lib/commonjs/design-tokens/JFS Variables-variables-full.json +18633 -1
  28. package/lib/commonjs/design-tokens/figma-variables-resolver.js.map +1 -2
  29. package/lib/commonjs/icons/registry.js +1 -1
  30. package/lib/module/components/ActionFooter/ActionFooter.js +119 -0
  31. package/lib/module/components/ActionFooter/ActionFooter.js.map +1 -0
  32. package/lib/module/components/ActionFooter/ActionFooter.mdx +101 -0
  33. package/lib/module/components/Button/Button.js +1 -1
  34. package/lib/module/components/Button/Button.js.map +1 -1
  35. package/lib/module/components/Button/Button.mdx +2 -2
  36. package/lib/module/components/CardFeedback/CardFeedback.js +46 -11
  37. package/lib/module/components/CardFeedback/CardFeedback.js.map +1 -1
  38. package/lib/module/components/CardFeedback/CardFeedback.mdx +1 -0
  39. package/lib/module/components/Divider/Divider.js +58 -0
  40. package/lib/module/components/Divider/Divider.js.map +1 -0
  41. package/lib/module/components/Divider/Divider.mdx +91 -0
  42. package/lib/module/components/ListItem/ListItem.js +24 -13
  43. package/lib/module/components/ListItem/ListItem.js.map +1 -1
  44. package/lib/module/components/ListItem/ListItem.mdx +46 -5
  45. package/lib/module/components/MerchantProfile/MerchantProfile.js +128 -0
  46. package/lib/module/components/MerchantProfile/MerchantProfile.js.map +1 -0
  47. package/lib/module/components/MerchantProfile/MerchantProfile.mdx +139 -0
  48. package/lib/module/components/MoneyValue/MoneyValue.mdx +4 -0
  49. package/lib/module/components/NavArrow/NavArrow.js +84 -0
  50. package/lib/module/components/NavArrow/NavArrow.js.map +1 -0
  51. package/lib/module/components/NavArrow/NavArrow.mdx +123 -0
  52. package/lib/module/components/Section/Section.mdx +4 -4
  53. package/lib/module/components/Stepper/Step.mdx +1 -1
  54. package/lib/module/components/index.js +4 -0
  55. package/lib/module/components/index.js.map +1 -1
  56. package/lib/module/design-tokens/JFS Variables-variables-full.json +18633 -1
  57. package/lib/module/design-tokens/figma-variables-resolver.js +2 -2
  58. package/lib/module/design-tokens/figma-variables-resolver.js.map +2 -1
  59. package/lib/module/icons/registry.js +1 -1
  60. package/lib/typescript/components/ActionFooter/ActionFooter.d.ts +58 -0
  61. package/lib/typescript/components/ActionFooter/ActionFooter.d.ts.map +1 -0
  62. package/lib/typescript/components/CardFeedback/CardFeedback.d.ts +4 -3
  63. package/lib/typescript/components/CardFeedback/CardFeedback.d.ts.map +1 -1
  64. package/lib/typescript/components/Divider/Divider.d.ts +50 -0
  65. package/lib/typescript/components/Divider/Divider.d.ts.map +1 -0
  66. package/lib/typescript/components/ListItem/ListItem.d.ts +4 -3
  67. package/lib/typescript/components/ListItem/ListItem.d.ts.map +1 -1
  68. package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts +68 -0
  69. package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts.map +1 -0
  70. package/lib/typescript/components/NavArrow/NavArrow.d.ts +35 -0
  71. package/lib/typescript/components/NavArrow/NavArrow.d.ts.map +1 -0
  72. package/lib/typescript/components/index.d.ts +4 -0
  73. package/lib/typescript/components/index.d.ts.map +1 -1
  74. package/lib/typescript/icons/registry.d.ts +1 -1
  75. package/package.json +2 -2
  76. package/src/components/.token-metadata.json +99 -11
  77. package/src/components/ActionFooter/ActionFooter.mdx +101 -0
  78. package/src/components/ActionFooter/ActionFooter.tsx +142 -0
  79. package/src/components/Button/Button.mdx +2 -2
  80. package/src/components/Button/Button.tsx +1 -1
  81. package/src/components/CardFeedback/CardFeedback.mdx +1 -0
  82. package/src/components/CardFeedback/CardFeedback.tsx +37 -12
  83. package/src/components/Divider/Divider.mdx +91 -0
  84. package/src/components/Divider/Divider.tsx +91 -0
  85. package/src/components/ListItem/ListItem.mdx +46 -5
  86. package/src/components/ListItem/ListItem.tsx +22 -11
  87. package/src/components/MerchantProfile/MerchantProfile.mdx +139 -0
  88. package/src/components/MerchantProfile/MerchantProfile.tsx +174 -0
  89. package/src/components/MoneyValue/MoneyValue.mdx +4 -0
  90. package/src/components/NavArrow/NavArrow.mdx +123 -0
  91. package/src/components/NavArrow/NavArrow.tsx +108 -0
  92. package/src/components/Section/Section.mdx +4 -4
  93. package/src/components/Stepper/Step.mdx +1 -1
  94. package/src/components/index.ts +4 -0
  95. package/src/design-tokens/JFS Variables-variables-full.json +18633 -1
  96. package/src/icons/registry.ts +1 -1
@@ -0,0 +1,142 @@
1
+ import React from 'react'
2
+ import {
3
+ View,
4
+ type ViewStyle,
5
+ type StyleProp,
6
+ Platform,
7
+ } from 'react-native'
8
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
9
+
10
+ export type ActionFooterProps = {
11
+ /**
12
+ * Content to render inside the action footer slot.
13
+ * Typically includes IconButton and Button components.
14
+ */
15
+ children?: React.ReactNode
16
+ /**
17
+ * Mode configuration passed to the token resolver.
18
+ * Pass the same modes to children components for consistent theming.
19
+ */
20
+ modes?: Record<string, any>
21
+ /**
22
+ * Optional style overrides for the container
23
+ */
24
+ style?: StyleProp<ViewStyle>
25
+ /**
26
+ * Accessibility label for the footer region
27
+ */
28
+ accessibilityLabel?: string
29
+ }
30
+
31
+ /**
32
+ * ActionFooter component that provides a fixed footer container for action buttons.
33
+ *
34
+ * This component is designed to hold action items like IconButton and Button components
35
+ * at the bottom of a screen. It includes a shadow for visual separation from content above.
36
+ *
37
+ * The `modes` prop is automatically passed to all slot children. If a child has its own
38
+ * `modes` prop, it will be merged with the parent's modes (child modes take precedence).
39
+ *
40
+ * @component
41
+ * @param {Object} props - Component props
42
+ * @param {React.ReactNode} [props.children] - Action elements to display (e.g., IconButton, Button)
43
+ * @param {Object} [props.modes={}] - Mode configuration for design tokens (automatically passed to children)
44
+ * @param {Object} [props.style] - Optional style overrides
45
+ * @param {string} [props.accessibilityLabel] - Accessibility label for the footer region
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * // Basic usage - modes are automatically passed to all children
50
+ * <ActionFooter modes={modes}>
51
+ * <IconButton iconName="ic_split" />
52
+ * <Button label="Request" style={{ flex: 1 }} />
53
+ * <Button label="Pay" style={{ flex: 1 }} />
54
+ * </ActionFooter>
55
+ *
56
+ * // Children can override with their own modes (merged with parent)
57
+ * <ActionFooter modes={modes}>
58
+ * <IconButton iconName="ic_split" />
59
+ * <Button label="Request" modes={{ Appearance: 'secondary' }} style={{ flex: 1 }} />
60
+ * <Button label="Pay" modes={{ Appearance: 'primary' }} style={{ flex: 1 }} />
61
+ * </ActionFooter>
62
+ * ```
63
+ */
64
+ function ActionFooter({
65
+ children,
66
+ modes = {},
67
+ style,
68
+ accessibilityLabel = 'Action footer',
69
+ }: ActionFooterProps) {
70
+ // Resolve design tokens
71
+ const backgroundColor = getVariableByName('actionFooter/background', modes) ?? '#ffffff'
72
+ const gap = getVariableByName('actionFooter/gap', modes) ?? 8
73
+ const paddingHorizontal = getVariableByName('actionFooter/padding/horizontal', modes) ?? 16
74
+ const paddingTop = getVariableByName('actionFooter/padding/top', modes) ?? 10
75
+ const paddingBottom = getVariableByName('actionFooter/padding/bottom', modes) ?? 41
76
+
77
+ // Shadow styles - cross-platform
78
+ const shadowStyle: ViewStyle = Platform.select({
79
+ ios: {
80
+ shadowColor: 'rgba(12, 13, 16, 1)',
81
+ shadowOffset: { width: 0, height: -12 },
82
+ shadowOpacity: 0.16,
83
+ shadowRadius: 24,
84
+ },
85
+ android: {
86
+ elevation: 16,
87
+ },
88
+ default: {
89
+ // Web shadow using boxShadow (RNW supports this)
90
+ },
91
+ }) as ViewStyle
92
+
93
+ const containerStyle: ViewStyle = {
94
+ backgroundColor,
95
+ paddingLeft: paddingHorizontal,
96
+ paddingRight: paddingHorizontal,
97
+ paddingTop,
98
+ paddingBottom,
99
+ ...shadowStyle,
100
+ }
101
+
102
+ // Slot container style for horizontal layout of action items
103
+ const slotStyle: ViewStyle = {
104
+ flexDirection: 'row',
105
+ alignItems: 'flex-start',
106
+ gap,
107
+ flex: 1,
108
+ }
109
+
110
+ // Web-specific box-shadow
111
+ const webShadow = Platform.OS === 'web'
112
+ ? { boxShadow: '0px -12px 24px 0px rgba(12, 13, 16, 0.12), 0px -16px 48px 0px rgba(12, 13, 16, 0.16)' } as any
113
+ : {}
114
+
115
+ // Pass modes prop to all slot children
116
+ const childrenWithModes = React.Children.map(children, (child) => {
117
+ if (React.isValidElement(child)) {
118
+ // Merge modes: child's modes take precedence over parent's modes
119
+ const childModes = (child.props as any).modes
120
+ const mergedModes = childModes ? { ...modes, ...childModes } : modes
121
+ return React.cloneElement(child as React.ReactElement<any>, {
122
+ modes: mergedModes,
123
+ })
124
+ }
125
+ return child
126
+ })
127
+
128
+ return (
129
+ <View
130
+ style={[containerStyle, webShadow, style]}
131
+ accessibilityRole="toolbar"
132
+ accessibilityLabel={accessibilityLabel}
133
+ >
134
+ <View style={slotStyle}>
135
+ {childrenWithModes}
136
+ </View>
137
+ </View>
138
+ )
139
+ }
140
+
141
+ export default ActionFooter
142
+
@@ -34,7 +34,7 @@ This component uses the following design token collections. Each collection supp
34
34
  - **Default:** positive
35
35
 
36
36
  ### Context
37
- - **Modes:** Default | Nudge&Alert
37
+ - **Modes:** Default | Nudge&Alert | CTACard | ListItem
38
38
  - **Default:** Default
39
39
 
40
40
  ### Button / Size
@@ -90,7 +90,7 @@ This component uses the following design tokens, resolved through `getVariableBy
90
90
  - **`button/fontFamily`**
91
91
  - **`button/fontSize`**
92
92
  - **`button/fontWeight`**
93
- - **`button/forground`**
93
+ - **`button/foreground`**
94
94
  - **`button/gap`**
95
95
  - **`button/lineHeight`**
96
96
  - **`button/padding/horizontal`**
@@ -89,7 +89,7 @@ function Button({
89
89
  const fontWeight = typeof fontWeightValue === 'number' ? fontWeightValue.toString() : fontWeightValue
90
90
  const lineHeight = getVariableByName('button/lineHeight', modes) || 19
91
91
  const fontSize = getVariableByName('button/fontSize', modes) || 16
92
- const textColor = getVariableByName('button/forground', modes) || '#0f0d0a'
92
+ const textColor = getVariableByName('button/foreground', modes) || '#0f0d0a'
93
93
 
94
94
  const baseLabelTextStyle: TextStyle = {
95
95
  color: textColor,
@@ -62,6 +62,7 @@ This component uses the following design token collections. Each collection supp
62
62
 
63
63
  This component uses the following design tokens, resolved through `getVariableByName`:
64
64
 
65
+ - **`appearanceFeedback/cardFeedback/icon/color`**
65
66
  - **`cardFeedback/actionSlot/gap`**
66
67
  - **`cardFeedback/background`**
67
68
  - **`cardFeedback/body/color`**
@@ -1,6 +1,7 @@
1
1
  import React, { createContext, useContext, isValidElement, cloneElement } from 'react';
2
2
  import { View, Text, StyleSheet, type ViewStyle, type TextStyle, type StyleProp, Image } from 'react-native';
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
4
+ import IconComponent from '../../icons/Icon';
4
5
 
5
6
  /**
6
7
  * Context to share 'modes' with child components.
@@ -29,9 +30,15 @@ export interface CardFeedbackProps {
29
30
  */
30
31
  export function CardFeedback({
31
32
  children,
32
- modes = {},
33
+ modes: propModes,
33
34
  style,
34
35
  }: CardFeedbackProps) {
36
+ const modes = {
37
+ 'Appearance.System': 'positive',
38
+ 'Context': 'Nudge&Alert',
39
+ 'Semantic Intent': 'System',
40
+ ...propModes,
41
+ };
35
42
  // Container Tokens
36
43
  const backgroundColor = getVariableByName('cardFeedback/background', modes) || '#dbf0d9';
37
44
  const radius = parseFloat(getVariableByName('cardFeedback/radius', modes) || '12');
@@ -66,16 +73,16 @@ export function CardFeedback({
66
73
 
67
74
  /**
68
75
  * Icon Wrapper
69
- * Tokens: cardFeedback/icon/size, cardFeedback/icon/color
76
+ * Tokens: cardFeedback/icon/size, appearanceFeedback/cardFeedback/icon/color
70
77
  */
71
- export function Icon({ children, style, modes: propModes }: { children?: React.ReactNode; style?: StyleProp<ViewStyle>; modes?: Record<string, any> }) {
78
+ export function Icon({ children, style, modes: propModes, icon }: { children?: React.ReactNode; style?: StyleProp<ViewStyle>; modes?: Record<string, any>; icon?: string }) {
72
79
  const context = useContext(CardFeedbackContext);
73
80
  const modes = propModes || context.modes || {};
74
81
 
75
82
  const size = parseFloat(getVariableByName('cardFeedback/icon/size', modes) || '20');
76
- // Color is usually applied to the SVG/Icon itself, but we can access it here if needed
77
- // const color = getVariableByName('cardFeedback/icon/color', modes);
78
-
83
+ // Color for the icon itself
84
+ const color = getVariableByName('appearanceFeedback/cardFeedback/icon/color', modes);
85
+ console.log(color);
79
86
  const containerStyle: ViewStyle = {
80
87
  width: size,
81
88
  height: size,
@@ -84,11 +91,21 @@ export function Icon({ children, style, modes: propModes }: { children?: React.R
84
91
  overflow: 'hidden',
85
92
  };
86
93
 
87
- // If children is an SVG or Icon component, we might need to clone it to pass color?
88
- // For now, just render children in the sized box.
94
+ // Pass modes to children (e.g., icon components that accept modes)
95
+ const childrenWithModes = React.Children.map(children, child => {
96
+ if (isValidElement(child)) {
97
+ return cloneElement(child as React.ReactElement<{ modes?: Record<string, any> }>, { modes: { ...(child.props as any).modes, ...modes } });
98
+ }
99
+ return child;
100
+ });
101
+
89
102
  return (
90
103
  <View style={[containerStyle, style]}>
91
- {children}
104
+ {icon ? (
105
+ <IconComponent name={icon} size={size} color={color} />
106
+ ) : (
107
+ childrenWithModes
108
+ )}
92
109
  </View>
93
110
  );
94
111
  }
@@ -104,9 +121,17 @@ export function Content({ children, style, modes: propModes }: { children?: Reac
104
121
 
105
122
  const gap = parseFloat(getVariableByName('cardFeedback/text/gap', modes) || '8');
106
123
 
124
+ // Pass modes to children (Title, Body, etc.)
125
+ const childrenWithModes = React.Children.map(children, child => {
126
+ if (isValidElement(child)) {
127
+ return cloneElement(child as React.ReactElement<{ modes?: Record<string, any> }>, { modes: { ...(child.props as any).modes, ...modes } });
128
+ }
129
+ return child;
130
+ });
131
+
107
132
  return (
108
133
  <View style={[{ gap, width: '100%' }, style]}>
109
- {children}
134
+ {childrenWithModes}
110
135
  </View>
111
136
  );
112
137
  }
@@ -169,12 +194,12 @@ export function Action({ children, style, modes: propModes }: { children?: React
169
194
  const context = useContext(CardFeedbackContext);
170
195
  const modes = propModes || context.modes || {};
171
196
 
172
- const gap = parseFloat(getVariableByName('cardFeedback/actionSlot/gap', modes) || '4');
197
+ const gap = parseFloat(getVariableByName('cardFeedback/actionSlot/gap', modes) || '0');
173
198
 
174
199
  // "Maximise existing component usage" -> Pass modes to children (Buttons)
175
200
  const childrenWithModes = React.Children.map(children, child => {
176
201
  if (isValidElement(child)) {
177
- return cloneElement(child as any, { modes: { ...(child.props as any).modes, ...modes } });
202
+ return cloneElement(child as React.ReactElement<{ modes?: Record<string, any> }>, { modes: { ...(child.props as any).modes, ...modes } });
178
203
  }
179
204
  return child;
180
205
  });
@@ -0,0 +1,91 @@
1
+ import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
2
+ import * as DividerStories from './Divider.stories';
3
+ import Divider from './Divider';
4
+
5
+ <Meta of={DividerStories} />
6
+
7
+ # Divider
8
+
9
+ A simple divider component that renders a horizontal or vertical separator line. Uses design tokens for consistent sizing and color.
10
+
11
+ ## Props
12
+
13
+ <ArgsTable of={Divider} />
14
+
15
+ ## Available Collections and Modes
16
+
17
+ This component uses the following design token collections. Each collection supports multiple modes that can be configured via the `modes` prop.
18
+
19
+ ### Color Mode
20
+ - **Modes:** Light | Dark
21
+ - **Default:** Light
22
+ ## Usage
23
+
24
+ ### Horizontal Divider
25
+
26
+ The default horizontal divider stretches to fill its container width:
27
+
28
+ <Canvas>
29
+ <Story of={DividerStories.Horizontal} />
30
+ </Canvas>
31
+
32
+ ### Vertical Divider
33
+
34
+ Vertical dividers stretch to fill their container height:
35
+
36
+ <Canvas>
37
+ <Story of={DividerStories.Vertical} />
38
+ </Canvas>
39
+
40
+ ### In Content Layout
41
+
42
+ Use horizontal dividers to separate content sections:
43
+
44
+ <Canvas>
45
+ <Story of={DividerStories.InContentLayout} />
46
+ </Canvas>
47
+
48
+ ### In Row Layout
49
+
50
+ Use vertical dividers to separate items in a horizontal row:
51
+
52
+ <Canvas>
53
+ <Story of={DividerStories.InRowLayout} />
54
+ </Canvas>
55
+
56
+ ## Component Usage
57
+
58
+ ```tsx
59
+ import { Divider } from 'jsf-components';
60
+
61
+ // Horizontal divider (default)
62
+ <Divider />
63
+
64
+ // Vertical divider
65
+ <Divider direction="vertical" />
66
+
67
+ // In a vertical layout
68
+ <View>
69
+ <Text>Section 1</Text>
70
+ <Divider />
71
+ <Text>Section 2</Text>
72
+ </View>
73
+
74
+ // In a horizontal layout
75
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
76
+ <Text>Item 1</Text>
77
+ <View style={{ marginHorizontal: 8 }}>
78
+ <Divider direction="vertical" />
79
+ </View>
80
+ <Text>Item 2</Text>
81
+ </View>
82
+ ```
83
+
84
+ ## Design Tokens
85
+
86
+ This component uses the following design tokens, resolved through `getVariableByName`:
87
+
88
+ - **`divider/color`**
89
+ - **`divider/size`**
90
+
91
+ All tokens support mode-based theming through the `modes` prop.
@@ -0,0 +1,91 @@
1
+ import React from 'react'
2
+ import {
3
+ View,
4
+ type ViewStyle,
5
+ type StyleProp,
6
+ } from 'react-native'
7
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
8
+
9
+ export type DividerDirection = 'horizontal' | 'vertical'
10
+
11
+ export type DividerProps = {
12
+ /**
13
+ * Direction of the divider line.
14
+ * - 'horizontal': renders a horizontal line (default)
15
+ * - 'vertical': renders a vertical line
16
+ */
17
+ direction?: DividerDirection
18
+ /**
19
+ * Mode configuration passed to the token resolver.
20
+ */
21
+ modes?: Record<string, any>
22
+ /**
23
+ * Optional style overrides for the divider
24
+ */
25
+ style?: StyleProp<ViewStyle>
26
+ /**
27
+ * Accessibility label for the divider
28
+ */
29
+ accessibilityLabel?: string
30
+ }
31
+
32
+ /**
33
+ * Divider component that renders a horizontal or vertical separator line.
34
+ *
35
+ * This component is used to visually separate content sections. It supports
36
+ * both horizontal and vertical orientations based on the `direction` prop.
37
+ *
38
+ * @component
39
+ * @param {Object} props - Component props
40
+ * @param {'horizontal' | 'vertical'} [props.direction='horizontal'] - Direction of the divider
41
+ * @param {Object} [props.modes={}] - Mode configuration for design tokens
42
+ * @param {Object} [props.style] - Optional style overrides
43
+ * @param {string} [props.accessibilityLabel] - Accessibility label for the divider
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * // Horizontal divider (default)
48
+ * <Divider />
49
+ *
50
+ * // Vertical divider
51
+ * <Divider direction="vertical" />
52
+ *
53
+ * // With custom modes
54
+ * <Divider modes={{ Appearance: 'dark' }} />
55
+ * ```
56
+ */
57
+ function Divider({
58
+ direction = 'horizontal',
59
+ modes = {},
60
+ style,
61
+ accessibilityLabel = 'Divider',
62
+ }: DividerProps) {
63
+ // Resolve design tokens
64
+ const size = getVariableByName('divider/size', modes) ?? 1
65
+ const color = getVariableByName('divider/color', modes) ?? '#1a1c1f'
66
+
67
+ const isVertical = direction === 'vertical'
68
+
69
+ const dividerStyle: ViewStyle = isVertical
70
+ ? {
71
+ width: size,
72
+ backgroundColor: color,
73
+ alignSelf: 'stretch',
74
+ }
75
+ : {
76
+ height: size,
77
+ backgroundColor: color,
78
+ alignSelf: 'stretch',
79
+ }
80
+
81
+ return (
82
+ <View
83
+ style={[dividerStyle, style]}
84
+ accessibilityRole="none"
85
+ accessibilityLabel={accessibilityLabel}
86
+ />
87
+ )
88
+ }
89
+
90
+ export default Divider
91
+
@@ -7,7 +7,9 @@ import { AccessibilitySection, AnatomySection, UsageConstraintsSection } from '.
7
7
 
8
8
  # ListItem
9
9
 
10
- Helper function to recursively clone children and pass modes prop to components that accept it.
10
+ A token-driven list item component supporting vertical and horizontal layouts, with optional navigation behaviour and slots for leading, support text and trailing content.
11
+
12
+ The horizontal layout includes a **NavArrow** on the far right by default, which can be hidden via the `navArrow` prop.
11
13
 
12
14
  ## Available Collections and Modes
13
15
 
@@ -26,9 +28,47 @@ This component uses the following design token collections. Each collection supp
26
28
  - **Default:** Light
27
29
  ## Usage
28
30
 
29
- <Canvas>
30
- <Story of={ListItemStories.Default} />
31
- </Canvas>
31
+ ### Horizontal Layout with NavArrow
32
+
33
+ <Canvas of={ListItemStories.HorizontalNavigation} />
34
+
35
+ ### Horizontal Layout (NavArrow only, no endSlot)
36
+
37
+ <Canvas of={ListItemStories.HorizontalWithNavArrowOnly} />
38
+
39
+ ### Vertical Layout
40
+
41
+ <Canvas of={ListItemStories.VerticalStatic} />
42
+
43
+ ### Usage Example
44
+
45
+ ```tsx
46
+ import ListItem from './ListItem';
47
+ import Button from '../Button/Button';
48
+
49
+ // Horizontal with NavArrow (default)
50
+ <ListItem
51
+ layout="Horizontal"
52
+ title="Navigate to Details"
53
+ supportText="Tap to view more"
54
+ navArrow={true}
55
+ />
56
+
57
+ // Horizontal without NavArrow
58
+ <ListItem
59
+ layout="Horizontal"
60
+ title="Balance"
61
+ supportText="₹500"
62
+ navArrow={false}
63
+ endSlot={<Button label="Add Money" />}
64
+ />
65
+
66
+ // Vertical layout (NavArrow not applicable)
67
+ <ListItem
68
+ layout="Vertical"
69
+ supportText="Payments"
70
+ />
71
+ ```
32
72
 
33
73
 
34
74
  <AccessibilitySection
@@ -43,7 +83,8 @@ This component uses the following design token collections. Each collection supp
43
83
  <ul>
44
84
  <li><strong>Leading slot</strong> — optional icon/avatar.</li>
45
85
  <li><strong>Title & support</strong> — primary and secondary text.</li>
46
- <li><strong>Trailing slot</strong> — optional meta info or action affordance.</li>
86
+ <li><strong>Trailing slot (endSlot)</strong> — optional meta info or action affordance. <strong>Note:</strong> This slot strictly enforces <code>Context: 'ListItem'</code> for its children, overriding any conflicting context passed via the <code>modes</code> prop.</li>
87
+ <li><strong>NavArrow</strong> — navigation chevron on the far right (Horizontal layout only). Shown by default, can be hidden via <code>navArrow=&#123;false&#125;</code>.</li>
47
88
  </ul>
48
89
  </AnatomySection>
49
90
 
@@ -2,16 +2,20 @@ import React, { useMemo } from 'react'
2
2
  import { View, Text, Pressable, type StyleProp, type ViewStyle, type TextStyle, type AccessibilityState } from 'react-native'
3
3
  import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
4
4
  import IconCapsule from '../IconCapsule/IconCapsule'
5
- import Button from '../Button/Button'
5
+ import NavArrow from '../NavArrow/NavArrow'
6
6
  import { usePressableWebSupport, type WebAccessibilityProps } from '../../utils/web-platform-utils'
7
7
 
8
8
  /**
9
9
  * Helper function to recursively clone children and pass modes prop to components that accept it.
10
10
  * This ensures that all child components in slots receive the modes prop from the parent.
11
+ *
12
+ * @param forcedModes - Optional modes that will ALWAYS be applied last, overriding any other modes.
13
+ * Useful for slots that need to enforce specific context values.
11
14
  */
12
15
  function cloneChildrenWithModes(
13
16
  children: React.ReactNode,
14
- modes: Record<string, any>
17
+ modes: Record<string, any>,
18
+ forcedModes?: Record<string, any>
15
19
  ): React.ReactNode[] {
16
20
  const result = React.Children.map(children, (child) => {
17
21
  if (!React.isValidElement(child)) {
@@ -24,15 +28,21 @@ function cloneChildrenWithModes(
24
28
 
25
29
  // Clone the child with modes prop if it doesn't already have one
26
30
  // or merge with existing modes if it does
27
- // Merge order: parent modes first, then child's explicit modes override them
31
+ // Merge order: parent modes first, then child's explicit modes override them,
32
+ // then forcedModes (if provided) are applied last and can never be overridden
28
33
  const existingModes = (child.props as any)?.modes
29
- const mergedModes = existingModes ? { ...modes, ...existingModes } : modes
34
+ const mergedModes = forcedModes
35
+ ? { ...modes, ...(existingModes || {}), ...forcedModes }
36
+ : existingModes
37
+ ? { ...modes, ...existingModes }
38
+ : modes
30
39
 
31
40
  // Recursively process children if they exist
32
41
  const processedChildren: React.ReactNode | undefined = hasChildren
33
42
  ? cloneChildrenWithModes(
34
43
  React.Children.toArray(childChildren),
35
- modes
44
+ modes,
45
+ forcedModes
36
46
  )
37
47
  : undefined
38
48
 
@@ -57,7 +67,8 @@ type ListItemProps = {
57
67
  leading?: React.ReactNode;
58
68
  supportSlot?: React.ReactNode;
59
69
  endSlot?: React.ReactNode;
60
- showTrailing?: boolean;
70
+ /** Whether to show the NavArrow on the far right (Horizontal layout only). Defaults to true. */
71
+ navArrow?: boolean;
61
72
  modes?: Record<string, any>;
62
73
  onPress?: () => void;
63
74
  style?: StyleProp<ViewStyle>;
@@ -94,7 +105,7 @@ type ListItemProps = {
94
105
  * @param {React.ReactNode} [props.leading] - Optional leading element. Defaults to `IconCapsule`.
95
106
  * @param {React.ReactNode} [props.supportSlot] - Optional custom slot used instead of the default support text block.
96
107
  * @param {React.ReactNode} [props.endSlot] - Optional custom trailing slot (Figma Slot "end").
97
- * @param {boolean} [props.showTrailing=true] - When `true`, renders the default trailing Button in Horizontal layout if `endSlot` is not provided.
108
+ * @param {boolean} [props.navArrow=true] - Whether to show NavArrow on the far right (Horizontal layout only).
98
109
  * @param {Object} [props.modes={}] - Modes object passed to `getVariableByName` for all design tokens.
99
110
  * @param {Function} [props.onPress] - When provided, the entire item becomes pressable (navigation variant).
100
111
  * @param {Object} [props.style] - Optional container style overrides.
@@ -111,7 +122,7 @@ function ListItem({
111
122
  leading,
112
123
  supportSlot,
113
124
  endSlot,
114
- showTrailing = true,
125
+ navArrow = true,
115
126
  modes = {},
116
127
  onPress,
117
128
  style,
@@ -275,8 +286,9 @@ function ListItem({
275
286
 
276
287
  if (layout === 'Horizontal') {
277
288
  // Process endSlot to pass modes to children
289
+ // Force Context: 'ListItem' - this value can never be overridden by external modes
278
290
  const processedEndSlot = endSlot
279
- ? cloneChildrenWithModes(React.Children.toArray(endSlot), modes)
291
+ ? cloneChildrenWithModes(React.Children.toArray(endSlot), modes, { "Context": 'ListItem' })
280
292
  : []
281
293
  // Extract single element if array has one element, otherwise use array
282
294
  const trailingContent =
@@ -284,8 +296,6 @@ function ListItem({
284
296
  ? processedEndSlot.length === 1
285
297
  ? processedEndSlot[0]
286
298
  : processedEndSlot
287
- : showTrailing
288
- ? <Button label="Button" modes={modes} {...({} as any)} />
289
299
  : null
290
300
 
291
301
  const innerContent = (
@@ -310,6 +320,7 @@ function ListItem({
310
320
  {trailingContent ? (
311
321
  <View style={trailingWrapperStyle}>{trailingContent}</View>
312
322
  ) : null}
323
+ {navArrow && <NavArrow direction="Forward" modes={modes} />}
313
324
  </View>
314
325
  )
315
326