jfs-components 0.0.1 → 0.0.3

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 (101) 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.js +36 -4
  20. package/lib/commonjs/components/MoneyValue/MoneyValue.js.map +1 -1
  21. package/lib/commonjs/components/MoneyValue/MoneyValue.mdx +4 -0
  22. package/lib/commonjs/components/NavArrow/NavArrow.js +90 -0
  23. package/lib/commonjs/components/NavArrow/NavArrow.js.map +1 -0
  24. package/lib/commonjs/components/NavArrow/NavArrow.mdx +123 -0
  25. package/lib/commonjs/components/Section/Section.mdx +4 -4
  26. package/lib/commonjs/components/Stepper/Step.mdx +1 -1
  27. package/lib/commonjs/components/index.js +28 -0
  28. package/lib/commonjs/components/index.js.map +1 -1
  29. package/lib/commonjs/design-tokens/JFS Variables-variables-full.json +18633 -1
  30. package/lib/commonjs/design-tokens/figma-variables-resolver.js.map +1 -2
  31. package/lib/commonjs/icons/registry.js +1 -1
  32. package/lib/module/components/ActionFooter/ActionFooter.js +119 -0
  33. package/lib/module/components/ActionFooter/ActionFooter.js.map +1 -0
  34. package/lib/module/components/ActionFooter/ActionFooter.mdx +101 -0
  35. package/lib/module/components/Button/Button.js +1 -1
  36. package/lib/module/components/Button/Button.js.map +1 -1
  37. package/lib/module/components/Button/Button.mdx +2 -2
  38. package/lib/module/components/CardFeedback/CardFeedback.js +46 -11
  39. package/lib/module/components/CardFeedback/CardFeedback.js.map +1 -1
  40. package/lib/module/components/CardFeedback/CardFeedback.mdx +1 -0
  41. package/lib/module/components/Divider/Divider.js +58 -0
  42. package/lib/module/components/Divider/Divider.js.map +1 -0
  43. package/lib/module/components/Divider/Divider.mdx +91 -0
  44. package/lib/module/components/ListItem/ListItem.js +24 -13
  45. package/lib/module/components/ListItem/ListItem.js.map +1 -1
  46. package/lib/module/components/ListItem/ListItem.mdx +46 -5
  47. package/lib/module/components/MerchantProfile/MerchantProfile.js +128 -0
  48. package/lib/module/components/MerchantProfile/MerchantProfile.js.map +1 -0
  49. package/lib/module/components/MerchantProfile/MerchantProfile.mdx +139 -0
  50. package/lib/module/components/MoneyValue/MoneyValue.js +36 -4
  51. package/lib/module/components/MoneyValue/MoneyValue.js.map +1 -1
  52. package/lib/module/components/MoneyValue/MoneyValue.mdx +4 -0
  53. package/lib/module/components/NavArrow/NavArrow.js +84 -0
  54. package/lib/module/components/NavArrow/NavArrow.js.map +1 -0
  55. package/lib/module/components/NavArrow/NavArrow.mdx +123 -0
  56. package/lib/module/components/Section/Section.mdx +4 -4
  57. package/lib/module/components/Stepper/Step.mdx +1 -1
  58. package/lib/module/components/index.js +4 -0
  59. package/lib/module/components/index.js.map +1 -1
  60. package/lib/module/design-tokens/JFS Variables-variables-full.json +18633 -1
  61. package/lib/module/icons/registry.js +1 -1
  62. package/lib/typescript/components/ActionFooter/ActionFooter.d.ts +58 -0
  63. package/lib/typescript/components/ActionFooter/ActionFooter.d.ts.map +1 -0
  64. package/lib/typescript/components/CardFeedback/CardFeedback.d.ts +4 -3
  65. package/lib/typescript/components/CardFeedback/CardFeedback.d.ts.map +1 -1
  66. package/lib/typescript/components/Divider/Divider.d.ts +50 -0
  67. package/lib/typescript/components/Divider/Divider.d.ts.map +1 -0
  68. package/lib/typescript/components/ListItem/ListItem.d.ts +4 -3
  69. package/lib/typescript/components/ListItem/ListItem.d.ts.map +1 -1
  70. package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts +68 -0
  71. package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts.map +1 -0
  72. package/lib/typescript/components/MoneyValue/MoneyValue.d.ts +9 -2
  73. package/lib/typescript/components/MoneyValue/MoneyValue.d.ts.map +1 -1
  74. package/lib/typescript/components/NavArrow/NavArrow.d.ts +35 -0
  75. package/lib/typescript/components/NavArrow/NavArrow.d.ts.map +1 -0
  76. package/lib/typescript/components/index.d.ts +4 -0
  77. package/lib/typescript/components/index.d.ts.map +1 -1
  78. package/lib/typescript/icons/registry.d.ts +1 -1
  79. package/package.json +2 -2
  80. package/src/components/.token-metadata.json +99 -11
  81. package/src/components/ActionFooter/ActionFooter.mdx +101 -0
  82. package/src/components/ActionFooter/ActionFooter.tsx +142 -0
  83. package/src/components/Button/Button.mdx +2 -2
  84. package/src/components/Button/Button.tsx +1 -1
  85. package/src/components/CardFeedback/CardFeedback.mdx +1 -0
  86. package/src/components/CardFeedback/CardFeedback.tsx +37 -12
  87. package/src/components/Divider/Divider.mdx +91 -0
  88. package/src/components/Divider/Divider.tsx +91 -0
  89. package/src/components/ListItem/ListItem.mdx +46 -5
  90. package/src/components/ListItem/ListItem.tsx +22 -11
  91. package/src/components/MerchantProfile/MerchantProfile.mdx +139 -0
  92. package/src/components/MerchantProfile/MerchantProfile.tsx +174 -0
  93. package/src/components/MoneyValue/MoneyValue.mdx +4 -0
  94. package/src/components/MoneyValue/MoneyValue.tsx +39 -3
  95. package/src/components/NavArrow/NavArrow.mdx +123 -0
  96. package/src/components/NavArrow/NavArrow.tsx +108 -0
  97. package/src/components/Section/Section.mdx +4 -4
  98. package/src/components/Stepper/Step.mdx +1 -1
  99. package/src/components/index.ts +4 -0
  100. package/src/design-tokens/JFS Variables-variables-full.json +18633 -1
  101. package/src/icons/registry.ts +1 -1
@@ -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
 
@@ -0,0 +1,139 @@
1
+ import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
2
+ import * as MerchantProfileStories from './MerchantProfile.stories';
3
+ import MerchantProfile from './MerchantProfile';
4
+ import { AccessibilitySection, AnatomySection, UsageConstraintsSection } from '../docs/DocSections';
5
+
6
+ <Meta of={MerchantProfileStories} />
7
+
8
+ # MerchantProfile
9
+
10
+ Displays a merchant's identity in a centered vertical layout with an avatar, title, and subtitle. Commonly used on payment confirmation screens, transaction receipts, and merchant identification flows.
11
+
12
+ ## Props
13
+
14
+ <ArgsTable of={MerchantProfile} />
15
+
16
+ ## Available Collections and Modes
17
+
18
+ This component does not use any design token collections with multiple modes.
19
+ ## Usage
20
+
21
+ The default configuration shows the typical merchant profile layout:
22
+
23
+ <Canvas>
24
+ <Story of={MerchantProfileStories.Default} />
25
+ </Canvas>
26
+
27
+ ## Examples
28
+
29
+ ### Short Name
30
+
31
+ For merchants with shorter names:
32
+
33
+ <Canvas>
34
+ <Story of={MerchantProfileStories.ShortName} />
35
+ </Canvas>
36
+
37
+ ### Long Name
38
+
39
+ Handles longer merchant names gracefully with text wrapping:
40
+
41
+ <Canvas>
42
+ <Story of={MerchantProfileStories.LongName} />
43
+ </Canvas>
44
+
45
+ ### Without Subtitle
46
+
47
+ When only the merchant name is needed:
48
+
49
+ <Canvas>
50
+ <Story of={MerchantProfileStories.WithoutSubtitle} />
51
+ </Canvas>
52
+
53
+ ### Multiple Profiles
54
+
55
+ Multiple profiles displayed in a list:
56
+
57
+ <Canvas>
58
+ <Story of={MerchantProfileStories.MultipleProfiles} />
59
+ </Canvas>
60
+
61
+ ## Component Usage
62
+
63
+ ```tsx
64
+ import { MerchantProfile } from 'jsf-components';
65
+
66
+ function PaymentConfirmation() {
67
+ return (
68
+ <View style={{ flex: 1 }}>
69
+ <MerchantProfile
70
+ title="Uber India Systems Private Limited"
71
+ subtitle="UPI ID: uberindia@ybl"
72
+ modes={{}}
73
+ />
74
+
75
+ {/* Payment amount and other details */}
76
+ </View>
77
+ );
78
+ }
79
+ ```
80
+
81
+ ### Custom Avatar
82
+
83
+ You can customize the avatar using the `avatarProps` prop:
84
+
85
+ ```tsx
86
+ <MerchantProfile
87
+ title="Amazon Pay"
88
+ subtitle="UPI ID: amazonpay@apl"
89
+ avatarProps={{
90
+ imageSource: 'https://example.com/merchant-logo.png',
91
+ modes: { 'Avatar Size': 'XL' },
92
+ }}
93
+ />
94
+ ```
95
+
96
+ <AccessibilitySection
97
+ items={[
98
+ 'The component uses `accessibilityRole="header"` to identify it as a header region.',
99
+ 'A default accessibility label is generated from the title and subtitle.',
100
+ 'Provide a custom `accessibilityLabel` for more context if needed.',
101
+ 'The Avatar child component handles its own accessibility attributes.',
102
+ ]}
103
+ />
104
+
105
+ <AnatomySection>
106
+ <ul>
107
+ <li><strong>Avatar</strong> — circular merchant logo/image at the top.</li>
108
+ <li><strong>Title</strong> — bold merchant name centered below the avatar.</li>
109
+ <li><strong>Subtitle</strong> — lighter text, typically the UPI ID or additional info.</li>
110
+ </ul>
111
+ </AnatomySection>
112
+
113
+ <UsageConstraintsSection
114
+ items={[
115
+ 'Keep titles concise; very long names will wrap but may affect layout balance.',
116
+ 'Use consistent modes across the MerchantProfile and its siblings for visual harmony.',
117
+ 'The component is designed for centered layouts; avoid placing in left-aligned contexts.',
118
+ 'For interactive merchant profiles (e.g., tappable), wrap in a Pressable component.',
119
+ ]}
120
+ />
121
+
122
+ ## Design Tokens
123
+
124
+ This component uses the following design tokens, resolved through `getVariableByName`:
125
+
126
+ - **`merchantProfile/gap`**
127
+ - **`merchantProfile/padding/horizontal`**
128
+ - **`merchantProfile/subtitle/color`**
129
+ - **`merchantProfile/subtitle/fontFamily`**
130
+ - **`merchantProfile/subtitle/fontSize`**
131
+ - **`merchantProfile/subtitle/fontWeight`**
132
+ - **`merchantProfile/subtitle/lineHeight`**
133
+ - **`merchantProfile/title/color`**
134
+ - **`merchantProfile/title/fontFamily`**
135
+ - **`merchantProfile/title/fontSize`**
136
+ - **`merchantProfile/title/fontWeight`**
137
+ - **`merchantProfile/title/lineHeight`**
138
+
139
+ All tokens support mode-based theming through the `modes` prop.
@@ -0,0 +1,174 @@
1
+ import React from 'react'
2
+ import {
3
+ View,
4
+ Text,
5
+ type ViewStyle,
6
+ type TextStyle,
7
+ type StyleProp,
8
+ } from 'react-native'
9
+ import { getVariableByName } from '../../design-tokens/figma-variables-resolver'
10
+ import Avatar, { type AvatarProps } from '../Avatar/Avatar'
11
+
12
+ export type MerchantProfileProps = {
13
+ /**
14
+ * The merchant's display name or title
15
+ */
16
+ title?: string
17
+ /**
18
+ * Subtitle text, typically the UPI ID or additional merchant info
19
+ */
20
+ subtitle?: string
21
+ /**
22
+ * Mode configuration passed to the token resolver.
23
+ * Also passed to the Avatar child component for consistent theming.
24
+ */
25
+ modes?: Record<string, any>
26
+ /**
27
+ * Optional style overrides for the container
28
+ */
29
+ style?: StyleProp<ViewStyle>
30
+ /**
31
+ * Props to pass to the Avatar component
32
+ */
33
+ avatarProps?: Partial<AvatarProps>
34
+ /**
35
+ * Accessibility label for the merchant profile region
36
+ */
37
+ accessibilityLabel?: string
38
+ }
39
+
40
+ /**
41
+ * MerchantProfile component that displays a merchant's identity.
42
+ *
43
+ * This component shows a centered profile layout with:
44
+ * - A circular avatar image
45
+ * - A title (merchant name)
46
+ * - A subtitle (typically UPI ID)
47
+ *
48
+ * All styling values are resolved from Figma design tokens using the provided modes.
49
+ *
50
+ * @component
51
+ * @param {Object} props - Component props
52
+ * @param {string} [props.title="Uber India Systems Private Limited"] - The merchant's display name
53
+ * @param {string} [props.subtitle="UPI ID: uberindia@ybl"] - Subtitle text, typically the UPI ID
54
+ * @param {Object} [props.modes={}] - Mode configuration for design tokens
55
+ * @param {Object} [props.style] - Optional style overrides for the container
56
+ * @param {Object} [props.avatarProps] - Props to pass to the Avatar component
57
+ * @param {string} [props.accessibilityLabel] - Accessibility label for the region
58
+ *
59
+ * @example
60
+ * ```tsx
61
+ * // Basic usage
62
+ * <MerchantProfile
63
+ * title="Uber India Systems Private Limited"
64
+ * subtitle="UPI ID: uberindia@ybl"
65
+ * modes={{}}
66
+ * />
67
+ *
68
+ * // With custom avatar
69
+ * <MerchantProfile
70
+ * title="Amazon Pay"
71
+ * subtitle="UPI ID: amazonpay@apl"
72
+ * avatarProps={{ imageSource: 'https://example.com/logo.png' }}
73
+ * />
74
+ * ```
75
+ */
76
+ function MerchantProfile({
77
+ title = 'Uber India Systems Private Limited',
78
+ subtitle = 'UPI ID: uberindia@ybl',
79
+ modes = {},
80
+ style,
81
+ avatarProps,
82
+ accessibilityLabel,
83
+ }: MerchantProfileProps) {
84
+ // Resolve design tokens with fallback values matching Figma design
85
+ const gap = getVariableByName('merchantProfile/gap', modes) ?? 12
86
+ const paddingHorizontal = getVariableByName('merchantProfile/padding/horizontal', modes) ?? 16
87
+
88
+ // Title typography tokens
89
+ const titleColor = getVariableByName('merchantProfile/title/color', modes) ?? '#0c0d10'
90
+ const titleFontSize = getVariableByName('merchantProfile/title/fontSize', modes) ?? 16
91
+ const titleLineHeight = getVariableByName('merchantProfile/title/lineHeight', modes) ?? 18
92
+ const titleFontFamily = getVariableByName('merchantProfile/title/fontFamily', modes) ?? 'System'
93
+ const titleFontWeightRaw = getVariableByName('merchantProfile/title/fontWeight', modes) ?? 700
94
+ const titleFontWeight = typeof titleFontWeightRaw === 'number'
95
+ ? titleFontWeightRaw.toString()
96
+ : titleFontWeightRaw
97
+
98
+ // Subtitle typography tokens
99
+ const subtitleColor = getVariableByName('merchantProfile/subtitle/color', modes) ?? '#191b1e'
100
+ const subtitleFontSize = getVariableByName('merchantProfile/subtitle/fontSize', modes) ?? 12
101
+ const subtitleLineHeight = getVariableByName('merchantProfile/subtitle/lineHeight', modes) ?? 16
102
+ const subtitleFontFamily = getVariableByName('merchantProfile/subtitle/fontFamily', modes) ?? 'System'
103
+ const subtitleFontWeightRaw = getVariableByName('merchantProfile/subtitle/fontWeight', modes) ?? 500
104
+ const subtitleFontWeight = typeof subtitleFontWeightRaw === 'number'
105
+ ? subtitleFontWeightRaw.toString()
106
+ : subtitleFontWeightRaw
107
+
108
+ // Container style
109
+ const containerStyle: ViewStyle = {
110
+ alignItems: 'center',
111
+ paddingHorizontal,
112
+ gap,
113
+ }
114
+
115
+ // Title text style
116
+ const titleTextStyle: TextStyle = {
117
+ color: titleColor,
118
+ fontSize: titleFontSize,
119
+ lineHeight: titleLineHeight,
120
+ fontFamily: titleFontFamily,
121
+ fontWeight: titleFontWeight as TextStyle['fontWeight'],
122
+ textAlign: 'center',
123
+ }
124
+
125
+ // Subtitle text style
126
+ const subtitleTextStyle: TextStyle = {
127
+ color: subtitleColor,
128
+ fontSize: subtitleFontSize,
129
+ lineHeight: subtitleLineHeight,
130
+ fontFamily: subtitleFontFamily,
131
+ fontWeight: subtitleFontWeight as TextStyle['fontWeight'],
132
+ textAlign: 'center',
133
+ }
134
+
135
+ // Default accessibility label
136
+ const defaultAccessibilityLabel = accessibilityLabel ||
137
+ `Merchant profile for ${title}${subtitle ? `, ${subtitle}` : ''}`
138
+
139
+ // Avatar modes - use provided modes to ensure consistent theming
140
+ // Override to use a larger avatar size for the merchant profile
141
+ const avatarModes = {
142
+ ...modes,
143
+ ...(avatarProps?.modes || {}),
144
+ }
145
+
146
+ return (
147
+ <View
148
+ style={[containerStyle, style]}
149
+ accessibilityRole="header"
150
+ accessibilityLabel={defaultAccessibilityLabel}
151
+ >
152
+ <Avatar
153
+ style="Image"
154
+ modes={avatarModes}
155
+ {...avatarProps}
156
+ />
157
+ <Text
158
+ style={titleTextStyle}
159
+ accessibilityRole="text"
160
+ >
161
+ {title}
162
+ </Text>
163
+ <Text
164
+ style={subtitleTextStyle}
165
+ accessibilityRole="text"
166
+ >
167
+ {subtitle}
168
+ </Text>
169
+ </View>
170
+ )
171
+ }
172
+
173
+ export default MerchantProfile
174
+
@@ -16,6 +16,10 @@ This component uses the following design token collections. Each collection supp
16
16
  ### Color Mode
17
17
  - **Modes:** Light | Dark
18
18
  - **Default:** Light
19
+
20
+ ### Context3
21
+ - **Modes:** Default | Transaction Bubble
22
+ - **Default:** Default
19
23
  ## Usage
20
24
 
21
25
  <Canvas>
@@ -40,10 +40,12 @@ const CURRENCY_SYMBOLS = {
40
40
  type MoneyValueProps = {
41
41
  value?: string | number;
42
42
  currency?: string;
43
+ negative?: boolean;
43
44
  modes?: Record<string, any>;
44
45
  style?: StyleProp<ViewStyle>;
45
46
  valueStyle?: StyleProp<TextStyle>;
46
47
  currencyStyle?: StyleProp<TextStyle>;
48
+ negativeSignStyle?: StyleProp<TextStyle>;
47
49
  accessibilityLabel?: string;
48
50
  accessibilityHint?: string;
49
51
  } & React.ComponentProps<typeof View>;
@@ -58,29 +60,54 @@ type MoneyValueProps = {
58
60
  * (e.g. "INR", "USD"). When an ISO code is provided, it will be mapped to the
59
61
  * appropriate symbol, with special care to support INR and other major currencies.
60
62
  *
63
+ * Negative values are auto-detected from the value prop (e.g., -500 or "-500").
64
+ * The `negative` prop can be used to explicitly override this behavior.
65
+ *
61
66
  * @component
62
67
  * @param {Object} props
63
- * @param {string|number} [props.value="500"] - Monetary value to display.
68
+ * @param {string|number} [props.value="500"] - Monetary value to display. Negative values are auto-detected.
64
69
  * @param {string} [props.currency="₹"] - Currency symbol or ISO code (e.g. "INR").
70
+ * @param {boolean} [props.negative] - Explicitly override negative display. If undefined, auto-detects from value.
65
71
  * @param {Object} [props.modes={}] - Modes object passed directly to `getVariableByName`.
66
72
  * Example: {"MoneyValue / Theme": "Default"}
67
73
  * @param {Object} [props.style] - Optional container style overrides.
68
74
  * @param {Object} [props.valueStyle] - Optional value text style overrides.
69
75
  * @param {Object} [props.currencyStyle] - Optional currency text style overrides.
76
+ * @param {Object} [props.negativeSignStyle] - Optional negative sign text style overrides.
70
77
  * @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, generates from value and currency
71
78
  * @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
72
79
  */
73
80
  function MoneyValue({
74
81
  value = '500',
75
82
  currency = '₹',
83
+ negative,
76
84
  modes = {},
77
85
  style,
78
86
  valueStyle,
79
87
  currencyStyle,
88
+ negativeSignStyle,
80
89
  accessibilityLabel,
81
90
  accessibilityHint,
82
91
  ...rest
83
92
  }: MoneyValueProps) {
93
+ // Auto-detect negative from value and compute display value
94
+ const { isNegative, displayValue } = React.useMemo(() => {
95
+ const stringValue = String(value)
96
+ const trimmed = stringValue.trim()
97
+ const valueIsNegative = trimmed.startsWith('-') || (typeof value === 'number' && value < 0)
98
+
99
+ // Strip leading minus sign for display (we show it separately)
100
+ const absoluteValue = trimmed.startsWith('-') ? trimmed.slice(1) : trimmed
101
+
102
+ // Use explicit negative prop if provided, otherwise use auto-detected
103
+ const showNegative = negative !== undefined ? negative : valueIsNegative
104
+
105
+ return {
106
+ isNegative: showNegative,
107
+ displayValue: absoluteValue,
108
+ }
109
+ }, [value, negative])
110
+
84
111
  // Resolve typography and layout tokens from Figma
85
112
  const textColor =
86
113
  getVariableByName('moneyValue/text/color', modes) || '#0f0d0a'
@@ -122,7 +149,7 @@ function MoneyValue({
122
149
 
123
150
  // Generate default accessibility label from value and currency
124
151
  const defaultAccessibilityLabel = accessibilityLabel ||
125
- `${resolvedCurrency} ${value}`
152
+ `${isNegative ? 'negative ' : ''}${resolvedCurrency} ${displayValue}`
126
153
 
127
154
  return (
128
155
  <View
@@ -132,6 +159,15 @@ function MoneyValue({
132
159
  accessibilityHint={accessibilityHint}
133
160
  {...rest}
134
161
  >
162
+ {isNegative && (
163
+ <Text
164
+ style={[baseTextStyle, negativeSignStyle]}
165
+ accessibilityElementsHidden={true}
166
+ importantForAccessibility="no"
167
+ >
168
+ -
169
+ </Text>
170
+ )}
135
171
  <Text
136
172
  style={[baseTextStyle, currencyStyle]}
137
173
  accessibilityElementsHidden={true}
@@ -144,7 +180,7 @@ function MoneyValue({
144
180
  accessibilityElementsHidden={true}
145
181
  importantForAccessibility="no"
146
182
  >
147
- {value}
183
+ {displayValue}
148
184
  </Text>
149
185
  </View>
150
186
  )
@@ -0,0 +1,123 @@
1
+ import { Meta, Story, Canvas, PureArgsTable as ArgsTable } from '@storybook/addon-docs/blocks';
2
+ import * as NavArrowStories from './NavArrow.stories';
3
+ import NavArrow from './NavArrow';
4
+
5
+ <Meta of={NavArrowStories} />
6
+
7
+ # NavArrow
8
+
9
+ A small navigation arrow component that displays a chevron for Back or Forward navigation. Commonly used in navigation headers, list items, or anywhere directional navigation cues are needed.
10
+
11
+ ## Available Collections and Modes
12
+
13
+ This component uses the following design token collections. Each collection supports multiple modes that can be configured via the `modes` prop.
14
+
15
+ ### Color Mode
16
+ - **Modes:** Light | Dark
17
+ - **Default:** Light
18
+
19
+ ### Context2
20
+ - **Modes:** Default | AppBar
21
+ - **Default:** Default
22
+ ## Usage
23
+
24
+ ### Basic Usage
25
+
26
+ <Canvas>
27
+ <Story of={NavArrowStories.Default} />
28
+ </Canvas>
29
+
30
+ ### Direction Variants
31
+
32
+ The NavArrow supports two directions:
33
+
34
+ - **Back** — Left-pointing chevron (‹) for backward navigation
35
+ - **Forward** — Right-pointing chevron (›) for forward navigation
36
+
37
+ <Canvas>
38
+ <Story of={NavArrowStories.AllDirections} />
39
+ </Canvas>
40
+
41
+ ### Usage Examples
42
+
43
+ ```jsx
44
+ import { NavArrow } from 'jfs-components';
45
+
46
+ // Basic back arrow
47
+ <NavArrow direction="Back" />
48
+
49
+ // Forward arrow
50
+ <NavArrow direction="Forward" />
51
+
52
+ // With custom modes (for theming)
53
+ <NavArrow direction="Back" modes={{ 'Color Mode': 'Dark' }} />
54
+
55
+ // With accessibility label
56
+ <NavArrow
57
+ direction="Back"
58
+ accessibilityLabel="Navigate to previous screen"
59
+ />
60
+ ```
61
+
62
+ ### Usage in a Header
63
+
64
+ ```jsx
65
+ import { View, Text, Pressable } from 'react-native';
66
+ import { NavArrow } from 'jfs-components';
67
+
68
+ function Header({ title, onBack }) {
69
+ return (
70
+ <View style={{ flexDirection: 'row', alignItems: 'center', padding: 16 }}>
71
+ <Pressable onPress={onBack} style={{ padding: 8 }}>
72
+ <NavArrow direction="Back" />
73
+ </Pressable>
74
+ <Text style={{ marginLeft: 8, fontSize: 18, fontWeight: '600' }}>
75
+ {title}
76
+ </Text>
77
+ </View>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ### Usage in a List Item
83
+
84
+ ```jsx
85
+ import { View, Text, Pressable } from 'react-native';
86
+ import { NavArrow } from 'jfs-components';
87
+
88
+ function NavigableListItem({ label, onPress }) {
89
+ return (
90
+ <Pressable
91
+ onPress={onPress}
92
+ style={{
93
+ flexDirection: 'row',
94
+ alignItems: 'center',
95
+ justifyContent: 'space-between',
96
+ padding: 16
97
+ }}
98
+ >
99
+ <Text>{label}</Text>
100
+ <NavArrow direction="Forward" />
101
+ </Pressable>
102
+ );
103
+ }
104
+ ```
105
+
106
+ ## Props
107
+
108
+ <ArgsTable of={NavArrow} />
109
+
110
+ ## Design Tokens
111
+
112
+ This component uses the following design tokens, resolved through `getVariableByName`:
113
+
114
+ - **`navArrow/background`**
115
+ - **`navArrow/height`**
116
+ - **`navArrow/icon/color`**
117
+ - **`navArrow/icon/height`**
118
+ - **`navArrow/icon/strokeWeight`**
119
+ - **`navArrow/icon/width`**
120
+ - **`navArrow/radius`**
121
+ - **`navArrow/width`**
122
+
123
+ All tokens support mode-based theming through the `modes` prop.