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.
- package/lib/commonjs/components/ActionFooter/ActionFooter.js +124 -0
- package/lib/commonjs/components/ActionFooter/ActionFooter.js.map +1 -0
- package/lib/commonjs/components/ActionFooter/ActionFooter.mdx +101 -0
- package/lib/commonjs/components/Button/Button.js +1 -1
- package/lib/commonjs/components/Button/Button.js.map +1 -1
- package/lib/commonjs/components/Button/Button.mdx +2 -2
- package/lib/commonjs/components/CardFeedback/CardFeedback.js +47 -11
- package/lib/commonjs/components/CardFeedback/CardFeedback.js.map +1 -1
- package/lib/commonjs/components/CardFeedback/CardFeedback.mdx +1 -0
- package/lib/commonjs/components/Divider/Divider.js +63 -0
- package/lib/commonjs/components/Divider/Divider.js.map +1 -0
- package/lib/commonjs/components/Divider/Divider.mdx +91 -0
- package/lib/commonjs/components/ListItem/ListItem.js +24 -13
- package/lib/commonjs/components/ListItem/ListItem.js.map +1 -1
- package/lib/commonjs/components/ListItem/ListItem.mdx +46 -5
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.js +133 -0
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.js.map +1 -0
- package/lib/commonjs/components/MerchantProfile/MerchantProfile.mdx +139 -0
- package/lib/commonjs/components/MoneyValue/MoneyValue.js +36 -4
- package/lib/commonjs/components/MoneyValue/MoneyValue.js.map +1 -1
- package/lib/commonjs/components/MoneyValue/MoneyValue.mdx +4 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js +90 -0
- package/lib/commonjs/components/NavArrow/NavArrow.js.map +1 -0
- package/lib/commonjs/components/NavArrow/NavArrow.mdx +123 -0
- package/lib/commonjs/components/Section/Section.mdx +4 -4
- package/lib/commonjs/components/Stepper/Step.mdx +1 -1
- package/lib/commonjs/components/index.js +28 -0
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/design-tokens/JFS Variables-variables-full.json +18633 -1
- package/lib/commonjs/design-tokens/figma-variables-resolver.js.map +1 -2
- package/lib/commonjs/icons/registry.js +1 -1
- package/lib/module/components/ActionFooter/ActionFooter.js +119 -0
- package/lib/module/components/ActionFooter/ActionFooter.js.map +1 -0
- package/lib/module/components/ActionFooter/ActionFooter.mdx +101 -0
- package/lib/module/components/Button/Button.js +1 -1
- package/lib/module/components/Button/Button.js.map +1 -1
- package/lib/module/components/Button/Button.mdx +2 -2
- package/lib/module/components/CardFeedback/CardFeedback.js +46 -11
- package/lib/module/components/CardFeedback/CardFeedback.js.map +1 -1
- package/lib/module/components/CardFeedback/CardFeedback.mdx +1 -0
- package/lib/module/components/Divider/Divider.js +58 -0
- package/lib/module/components/Divider/Divider.js.map +1 -0
- package/lib/module/components/Divider/Divider.mdx +91 -0
- package/lib/module/components/ListItem/ListItem.js +24 -13
- package/lib/module/components/ListItem/ListItem.js.map +1 -1
- package/lib/module/components/ListItem/ListItem.mdx +46 -5
- package/lib/module/components/MerchantProfile/MerchantProfile.js +128 -0
- package/lib/module/components/MerchantProfile/MerchantProfile.js.map +1 -0
- package/lib/module/components/MerchantProfile/MerchantProfile.mdx +139 -0
- package/lib/module/components/MoneyValue/MoneyValue.js +36 -4
- package/lib/module/components/MoneyValue/MoneyValue.js.map +1 -1
- package/lib/module/components/MoneyValue/MoneyValue.mdx +4 -0
- package/lib/module/components/NavArrow/NavArrow.js +84 -0
- package/lib/module/components/NavArrow/NavArrow.js.map +1 -0
- package/lib/module/components/NavArrow/NavArrow.mdx +123 -0
- package/lib/module/components/Section/Section.mdx +4 -4
- package/lib/module/components/Stepper/Step.mdx +1 -1
- package/lib/module/components/index.js +4 -0
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/design-tokens/JFS Variables-variables-full.json +18633 -1
- package/lib/module/icons/registry.js +1 -1
- package/lib/typescript/components/ActionFooter/ActionFooter.d.ts +58 -0
- package/lib/typescript/components/ActionFooter/ActionFooter.d.ts.map +1 -0
- package/lib/typescript/components/CardFeedback/CardFeedback.d.ts +4 -3
- package/lib/typescript/components/CardFeedback/CardFeedback.d.ts.map +1 -1
- package/lib/typescript/components/Divider/Divider.d.ts +50 -0
- package/lib/typescript/components/Divider/Divider.d.ts.map +1 -0
- package/lib/typescript/components/ListItem/ListItem.d.ts +4 -3
- package/lib/typescript/components/ListItem/ListItem.d.ts.map +1 -1
- package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts +68 -0
- package/lib/typescript/components/MerchantProfile/MerchantProfile.d.ts.map +1 -0
- package/lib/typescript/components/MoneyValue/MoneyValue.d.ts +9 -2
- package/lib/typescript/components/MoneyValue/MoneyValue.d.ts.map +1 -1
- package/lib/typescript/components/NavArrow/NavArrow.d.ts +35 -0
- package/lib/typescript/components/NavArrow/NavArrow.d.ts.map +1 -0
- package/lib/typescript/components/index.d.ts +4 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/icons/registry.d.ts +1 -1
- package/package.json +2 -2
- package/src/components/.token-metadata.json +99 -11
- package/src/components/ActionFooter/ActionFooter.mdx +101 -0
- package/src/components/ActionFooter/ActionFooter.tsx +142 -0
- package/src/components/Button/Button.mdx +2 -2
- package/src/components/Button/Button.tsx +1 -1
- package/src/components/CardFeedback/CardFeedback.mdx +1 -0
- package/src/components/CardFeedback/CardFeedback.tsx +37 -12
- package/src/components/Divider/Divider.mdx +91 -0
- package/src/components/Divider/Divider.tsx +91 -0
- package/src/components/ListItem/ListItem.mdx +46 -5
- package/src/components/ListItem/ListItem.tsx +22 -11
- package/src/components/MerchantProfile/MerchantProfile.mdx +139 -0
- package/src/components/MerchantProfile/MerchantProfile.tsx +174 -0
- package/src/components/MoneyValue/MoneyValue.mdx +4 -0
- package/src/components/MoneyValue/MoneyValue.tsx +39 -3
- package/src/components/NavArrow/NavArrow.mdx +123 -0
- package/src/components/NavArrow/NavArrow.tsx +108 -0
- package/src/components/Section/Section.mdx +4 -4
- package/src/components/Stepper/Step.mdx +1 -1
- package/src/components/index.ts +4 -0
- package/src/design-tokens/JFS Variables-variables-full.json +18633 -1
- 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
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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} ${
|
|
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
|
-
{
|
|
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.
|