@ledgerhq/lumen-ui-rnative 0.1.24 → 0.1.26
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/dist/module/index.js +1 -0
- package/dist/module/index.js.map +1 -1
- package/dist/module/lib/Components/Avatar/Avatar.js +22 -23
- package/dist/module/lib/Components/Avatar/Avatar.js.map +1 -1
- package/dist/module/lib/Components/Avatar/Avatar.test.js +17 -23
- package/dist/module/lib/Components/Avatar/Avatar.test.js.map +1 -1
- package/dist/module/lib/Components/DotCount/DotCount.js +128 -0
- package/dist/module/lib/Components/DotCount/DotCount.js.map +1 -0
- package/dist/module/lib/Components/DotCount/DotCount.mdx +86 -0
- package/dist/module/lib/Components/DotCount/DotCount.stories.js +172 -0
- package/dist/module/lib/Components/DotCount/DotCount.stories.js.map +1 -0
- package/dist/module/lib/Components/DotCount/DotCount.test.js +174 -0
- package/dist/module/lib/Components/DotCount/DotCount.test.js.map +1 -0
- package/dist/module/lib/Components/DotCount/index.js +5 -0
- package/dist/module/lib/Components/DotCount/index.js.map +1 -0
- package/dist/module/lib/Components/DotCount/types.js +4 -0
- package/dist/module/lib/Components/DotCount/types.js.map +1 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.js +134 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.js.map +1 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.mdx +56 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.stories.js +217 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.stories.js.map +1 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.test.js +238 -0
- package/dist/module/lib/Components/DotIcon/DotIcon.test.js.map +1 -0
- package/dist/module/lib/Components/DotIcon/index.js +5 -0
- package/dist/module/lib/Components/DotIcon/index.js.map +1 -0
- package/dist/module/lib/Components/DotIcon/types.js +4 -0
- package/dist/module/lib/Components/DotIcon/types.js.map +1 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.js +89 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.js.map +1 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.mdx +82 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.stories.js +84 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.stories.js.map +1 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.test.js +136 -0
- package/dist/module/lib/Components/DotIndicator/DotIndicator.test.js.map +1 -0
- package/dist/module/lib/Components/DotIndicator/index.js +5 -0
- package/dist/module/lib/Components/DotIndicator/index.js.map +1 -0
- package/dist/module/lib/Components/DotIndicator/types.js +4 -0
- package/dist/module/lib/Components/DotIndicator/types.js.map +1 -0
- package/dist/module/lib/Components/DotSymbol/DotSymbol.js +29 -22
- package/dist/module/lib/Components/DotSymbol/DotSymbol.js.map +1 -1
- package/dist/module/lib/Components/DotSymbol/DotSymbol.stories.js +31 -9
- package/dist/module/lib/Components/DotSymbol/DotSymbol.stories.js.map +1 -1
- package/dist/module/lib/Components/MediaImage/MediaImage.js +2 -1
- package/dist/module/lib/Components/MediaImage/MediaImage.js.map +1 -1
- package/dist/module/lib/Components/MediaImage/MediaImage.stories.js +4 -0
- package/dist/module/lib/Components/MediaImage/MediaImage.stories.js.map +1 -1
- package/dist/module/lib/Components/Spinner/Spinner.js +6 -1
- package/dist/module/lib/Components/Spinner/Spinner.js.map +1 -1
- package/dist/module/lib/Components/TabBar/TabBar.js +16 -13
- package/dist/module/lib/Components/TabBar/TabBar.js.map +1 -1
- package/dist/module/lib/Components/Tag/Tag.js +2 -0
- package/dist/module/lib/Components/Tag/Tag.js.map +1 -1
- package/dist/module/lib/Components/Tag/Tag.stories.js +8 -1
- package/dist/module/lib/Components/Tag/Tag.stories.js.map +1 -1
- package/dist/module/lib/Components/index.js +3 -0
- package/dist/module/lib/Components/index.js.map +1 -1
- package/dist/typescript/src/index.d.ts +1 -0
- package/dist/typescript/src/index.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Avatar/Avatar.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Avatar/Avatar.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/DotCount/DotCount.d.ts +3 -0
- package/dist/typescript/src/lib/Components/DotCount/DotCount.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotCount/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/DotCount/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotCount/types.d.ts +40 -0
- package/dist/typescript/src/lib/Components/DotCount/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotIcon/DotIcon.d.ts +30 -0
- package/dist/typescript/src/lib/Components/DotIcon/DotIcon.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotIcon/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/DotIcon/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotIcon/types.d.ts +40 -0
- package/dist/typescript/src/lib/Components/DotIcon/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotIndicator/DotIndicator.d.ts +3 -0
- package/dist/typescript/src/lib/Components/DotIndicator/DotIndicator.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotIndicator/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/DotIndicator/index.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotIndicator/types.d.ts +25 -0
- package/dist/typescript/src/lib/Components/DotIndicator/types.d.ts.map +1 -0
- package/dist/typescript/src/lib/Components/DotSymbol/DotSymbol.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/MediaImage/MediaImage.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/MediaImage/types.d.ts +1 -1
- package/dist/typescript/src/lib/Components/MediaImage/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Spinner/Spinner.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Spinner/Spinner.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/TabBar/TabBar.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Tag/Tag.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/Tag/types.d.ts +1 -1
- package/dist/typescript/src/lib/Components/Tag/types.d.ts.map +1 -1
- package/dist/typescript/src/lib/Components/index.d.ts +3 -0
- package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/lib/Components/Avatar/Avatar.test.tsx +17 -27
- package/src/lib/Components/Avatar/Avatar.tsx +24 -21
- package/src/lib/Components/DotCount/DotCount.mdx +86 -0
- package/src/lib/Components/DotCount/DotCount.stories.tsx +124 -0
- package/src/lib/Components/DotCount/DotCount.test.tsx +150 -0
- package/src/lib/Components/DotCount/DotCount.tsx +130 -0
- package/src/lib/Components/DotCount/index.ts +2 -0
- package/src/lib/Components/DotCount/types.ts +40 -0
- package/src/lib/Components/DotIcon/DotIcon.mdx +56 -0
- package/src/lib/Components/DotIcon/DotIcon.stories.tsx +154 -0
- package/src/lib/Components/DotIcon/DotIcon.test.tsx +224 -0
- package/src/lib/Components/DotIcon/DotIcon.tsx +146 -0
- package/src/lib/Components/DotIcon/index.ts +6 -0
- package/src/lib/Components/DotIcon/types.ts +44 -0
- package/src/lib/Components/DotIndicator/DotIndicator.mdx +82 -0
- package/src/lib/Components/DotIndicator/DotIndicator.stories.tsx +67 -0
- package/src/lib/Components/DotIndicator/DotIndicator.test.tsx +132 -0
- package/src/lib/Components/DotIndicator/DotIndicator.tsx +97 -0
- package/src/lib/Components/DotIndicator/index.ts +2 -0
- package/src/lib/Components/DotIndicator/types.ts +25 -0
- package/src/lib/Components/DotSymbol/DotSymbol.stories.tsx +26 -7
- package/src/lib/Components/DotSymbol/DotSymbol.tsx +22 -23
- package/src/lib/Components/MediaImage/MediaImage.stories.tsx +1 -0
- package/src/lib/Components/MediaImage/MediaImage.tsx +3 -1
- package/src/lib/Components/MediaImage/types.ts +1 -1
- package/src/lib/Components/Spinner/Spinner.tsx +6 -2
- package/src/lib/Components/TabBar/TabBar.tsx +17 -16
- package/src/lib/Components/Tag/Tag.stories.tsx +11 -1
- package/src/lib/Components/Tag/Tag.tsx +2 -0
- package/src/lib/Components/Tag/types.ts +8 -1
- package/src/lib/Components/index.ts +3 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { useDisabledContext } from '@ledgerhq/lumen-utils-shared';
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
|
+
import { useStyleSheet } from '../../../styles';
|
|
4
|
+
import { Box, Text } from '../Utility';
|
|
5
|
+
import type { DotCountProps } from './types';
|
|
6
|
+
|
|
7
|
+
export function DotCount({
|
|
8
|
+
value,
|
|
9
|
+
size = 'md',
|
|
10
|
+
max = 99,
|
|
11
|
+
appearance = 'base',
|
|
12
|
+
disabled: disabledProp = false,
|
|
13
|
+
lx = {},
|
|
14
|
+
style,
|
|
15
|
+
children,
|
|
16
|
+
accessibilityLabel,
|
|
17
|
+
ref,
|
|
18
|
+
...props
|
|
19
|
+
}: DotCountProps) {
|
|
20
|
+
const disabled = useDisabledContext({
|
|
21
|
+
consumerName: 'DotCount',
|
|
22
|
+
mergeWith: { disabled: disabledProp },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const styles = useStyles({
|
|
26
|
+
size,
|
|
27
|
+
appearance,
|
|
28
|
+
disabled,
|
|
29
|
+
pinned: !!children,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const cappedMax = Math.max(1, Math.min(max, 99));
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Box
|
|
36
|
+
ref={ref}
|
|
37
|
+
lx={lx}
|
|
38
|
+
style={StyleSheet.flatten([
|
|
39
|
+
children ? { position: 'relative' } : undefined,
|
|
40
|
+
style,
|
|
41
|
+
])}
|
|
42
|
+
{...props}
|
|
43
|
+
>
|
|
44
|
+
<Box
|
|
45
|
+
style={styles.container}
|
|
46
|
+
accessibilityRole='image'
|
|
47
|
+
accessibilityLabel={accessibilityLabel}
|
|
48
|
+
accessible={!!accessibilityLabel}
|
|
49
|
+
pointerEvents='none'
|
|
50
|
+
>
|
|
51
|
+
{value > 0 && (
|
|
52
|
+
<Text style={styles.text} allowFontScaling={false}>
|
|
53
|
+
{value <= cappedMax ? value : `${cappedMax}+`}
|
|
54
|
+
</Text>
|
|
55
|
+
)}
|
|
56
|
+
</Box>
|
|
57
|
+
{children}
|
|
58
|
+
</Box>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const useStyles = ({
|
|
63
|
+
size,
|
|
64
|
+
appearance,
|
|
65
|
+
disabled,
|
|
66
|
+
pinned,
|
|
67
|
+
}: {
|
|
68
|
+
size: NonNullable<DotCountProps['size']>;
|
|
69
|
+
appearance: NonNullable<DotCountProps['appearance']>;
|
|
70
|
+
disabled: boolean;
|
|
71
|
+
pinned: boolean;
|
|
72
|
+
}) => {
|
|
73
|
+
return useStyleSheet(
|
|
74
|
+
(t) => {
|
|
75
|
+
const sizeMap = {
|
|
76
|
+
lg: {
|
|
77
|
+
minWidth: t.sizes.s24,
|
|
78
|
+
minHeight: t.sizes.s24,
|
|
79
|
+
paddingHorizontal: t.spacings.s8,
|
|
80
|
+
paddingVertical: t.spacings.s2,
|
|
81
|
+
},
|
|
82
|
+
md: {
|
|
83
|
+
minHeight: t.sizes.s16,
|
|
84
|
+
minWidth: t.sizes.s16,
|
|
85
|
+
paddingHorizontal: t.spacings.s4,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const bgColorMap = {
|
|
90
|
+
base: { backgroundColor: t.colors.bg.interactive },
|
|
91
|
+
red: { backgroundColor: t.colors.bg.errorStrong },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const textMap = {
|
|
95
|
+
lg: { ...t.typographies.body2SemiBold },
|
|
96
|
+
md: { ...t.typographies.body4SemiBold },
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const textColorMap = {
|
|
100
|
+
base: { color: t.colors.text.onInteractive },
|
|
101
|
+
red: { color: t.colors.text.onErrorStrong },
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
container: {
|
|
106
|
+
alignItems: 'center',
|
|
107
|
+
justifyContent: 'center',
|
|
108
|
+
borderRadius: t.borderRadius.full,
|
|
109
|
+
...(pinned && {
|
|
110
|
+
position: 'absolute',
|
|
111
|
+
top: t.spacings.s0,
|
|
112
|
+
right: t.spacings.s0,
|
|
113
|
+
zIndex: 1,
|
|
114
|
+
}),
|
|
115
|
+
...sizeMap[size],
|
|
116
|
+
...(disabled
|
|
117
|
+
? { backgroundColor: t.colors.bg.disabled }
|
|
118
|
+
: { ...bgColorMap[appearance] }),
|
|
119
|
+
},
|
|
120
|
+
text: {
|
|
121
|
+
...textMap[size],
|
|
122
|
+
...(disabled
|
|
123
|
+
? { color: t.colors.text.disabled }
|
|
124
|
+
: { ...textColorMap[appearance] }),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
[size, appearance, disabled, pinned],
|
|
129
|
+
);
|
|
130
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { StyledViewProps } from '../../../styles';
|
|
3
|
+
|
|
4
|
+
export type DotCountProps = {
|
|
5
|
+
/**
|
|
6
|
+
* The size of the dot count.
|
|
7
|
+
* @default md
|
|
8
|
+
*/
|
|
9
|
+
size?: 'lg' | 'md';
|
|
10
|
+
/**
|
|
11
|
+
* The amount to be displayed.
|
|
12
|
+
*
|
|
13
|
+
* If higher than `max`, the displayed value will be "[max]+".
|
|
14
|
+
*/
|
|
15
|
+
value: number;
|
|
16
|
+
/**
|
|
17
|
+
* The max value shown.
|
|
18
|
+
*
|
|
19
|
+
* If `value` is higher than `max`, the displayed value will be "[max]+".
|
|
20
|
+
*
|
|
21
|
+
* By design, it will ignore values higher than 99.
|
|
22
|
+
* @default 99
|
|
23
|
+
*/
|
|
24
|
+
max?: number;
|
|
25
|
+
/**
|
|
26
|
+
* The appearance of the dot count.
|
|
27
|
+
* @default base
|
|
28
|
+
*/
|
|
29
|
+
appearance?: 'base' | 'red';
|
|
30
|
+
/**
|
|
31
|
+
* Whether the dot count should show a disabled appearance.
|
|
32
|
+
* @default false
|
|
33
|
+
*/
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Can be used as a wrapper to any component in case you wish to overlay a dot count on top of it.
|
|
37
|
+
* If provided, it'll pin the dot count to the top-right of the child component passed.
|
|
38
|
+
*/
|
|
39
|
+
children?: ReactNode;
|
|
40
|
+
} & Omit<StyledViewProps, 'children'>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as DotIconStories from './DotIcon.stories';
|
|
3
|
+
|
|
4
|
+
<Meta title='Communication/DotIcon' of={DotIconStories} />
|
|
5
|
+
|
|
6
|
+
# DotIcon
|
|
7
|
+
|
|
8
|
+
## Introduction
|
|
9
|
+
|
|
10
|
+
DotIcon positions a small icon indicator at a configurable corner of a child element such as MediaImage or Spot. The dot background uses a semantic color (`success`, `muted`, or `error`) to convey meaning at a glance.
|
|
11
|
+
> View in [Figma](https://www.figma.com/design/zSkvGGiqcnhywp2l3HTHxA/1.-Symbol-Library?node-id=6159-1866).
|
|
12
|
+
|
|
13
|
+
## Anatomy
|
|
14
|
+
|
|
15
|
+
<Canvas of={DotIconStories.Base} />
|
|
16
|
+
|
|
17
|
+
- **Wrapper**: Relative container that preserves the child's layout
|
|
18
|
+
- **Child**: The primary element (e.g. MediaImage, Spot)
|
|
19
|
+
- **Dot**: Absolutely-positioned indicator with a semantic background and an icon inside
|
|
20
|
+
|
|
21
|
+
## Properties
|
|
22
|
+
|
|
23
|
+
### Overview
|
|
24
|
+
|
|
25
|
+
<Canvas of={DotIconStories.Base} />
|
|
26
|
+
<Controls of={DotIconStories.Base} />
|
|
27
|
+
|
|
28
|
+
### Pin
|
|
29
|
+
|
|
30
|
+
Four corner placements are available: `bottom-end` (default), `top-end`, `bottom-start`, and `top-start`.
|
|
31
|
+
|
|
32
|
+
<Canvas of={DotIconStories.PinShowcase} />
|
|
33
|
+
|
|
34
|
+
### Shapes
|
|
35
|
+
|
|
36
|
+
<Canvas of={DotIconStories.ShapeShowcase} />
|
|
37
|
+
|
|
38
|
+
- **circle** (default): Fully rounded dot
|
|
39
|
+
- **square**: Rounded corners that scale with size
|
|
40
|
+
|
|
41
|
+
### Appearances
|
|
42
|
+
|
|
43
|
+
The `appearance` prop controls the semantic background color of the dot: `success`, `muted`, or `error`.
|
|
44
|
+
|
|
45
|
+
<Canvas of={DotIconStories.AppearanceShowcase} />
|
|
46
|
+
|
|
47
|
+
### Sizes
|
|
48
|
+
|
|
49
|
+
The dot size is driven by the parent MediaImage or Spot size via the mapping helpers `mediaImageDotIconSizeMap` and `spotDotIconSizeMap`. Allowed sizes are `16`, `20`, and `24`. The icon size is handled internally by the component.
|
|
50
|
+
|
|
51
|
+
<Canvas of={DotIconStories.SizeShowcase} />
|
|
52
|
+
|
|
53
|
+
## Accessibility
|
|
54
|
+
|
|
55
|
+
- The icon is purely decorative; the child element should carry its own accessibility label.
|
|
56
|
+
- Pair semantic appearances with meaningful icons so the state can be understood without relying on color alone.
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
|
|
2
|
+
import { ArrowDown, ArrowUp, Close, Link, Star } from '../../Symbols';
|
|
3
|
+
import { MediaImage } from '../MediaImage';
|
|
4
|
+
import { Spinner } from '../Spinner';
|
|
5
|
+
import { Box } from '../Utility';
|
|
6
|
+
import { DotIcon, mediaImageDotIconSizeMap } from './DotIcon';
|
|
7
|
+
|
|
8
|
+
const meta = {
|
|
9
|
+
component: DotIcon,
|
|
10
|
+
title: 'Communication/DotIcon',
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
source: {
|
|
14
|
+
language: 'tsx',
|
|
15
|
+
format: true,
|
|
16
|
+
type: 'code',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
} satisfies Meta<typeof DotIcon>;
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
type Story = StoryObj<typeof meta>;
|
|
24
|
+
|
|
25
|
+
const parentSrc = 'https://crypto-icons.ledger.com/ADA.png';
|
|
26
|
+
|
|
27
|
+
export const Base: Story = {
|
|
28
|
+
args: {
|
|
29
|
+
appearance: 'success',
|
|
30
|
+
icon: ArrowDown,
|
|
31
|
+
pin: 'bottom-end',
|
|
32
|
+
size: mediaImageDotIconSizeMap[48],
|
|
33
|
+
shape: 'circle',
|
|
34
|
+
children: (
|
|
35
|
+
<MediaImage src={parentSrc} alt='Cardano' size={48} shape='circle' />
|
|
36
|
+
),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const PinShowcase: Story = {
|
|
41
|
+
args: { appearance: 'success', icon: ArrowDown },
|
|
42
|
+
render: () => (
|
|
43
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's32' }}>
|
|
44
|
+
<DotIcon appearance='success' icon={ArrowDown} pin='bottom-end'>
|
|
45
|
+
<MediaImage src={parentSrc} shape='circle' />
|
|
46
|
+
</DotIcon>
|
|
47
|
+
<DotIcon appearance='success' icon={ArrowDown} pin='top-end'>
|
|
48
|
+
<MediaImage src={parentSrc} shape='circle' />
|
|
49
|
+
</DotIcon>
|
|
50
|
+
<DotIcon appearance='success' icon={ArrowDown} pin='bottom-start'>
|
|
51
|
+
<MediaImage src={parentSrc} shape='circle' />
|
|
52
|
+
</DotIcon>
|
|
53
|
+
<DotIcon appearance='success' icon={ArrowDown} pin='top-start'>
|
|
54
|
+
<MediaImage src={parentSrc} shape='circle' />
|
|
55
|
+
</DotIcon>
|
|
56
|
+
</Box>
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const ShapeShowcase: Story = {
|
|
61
|
+
args: { appearance: 'muted', icon: ArrowDown },
|
|
62
|
+
render: () => (
|
|
63
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's48' }}>
|
|
64
|
+
<DotIcon
|
|
65
|
+
appearance='muted'
|
|
66
|
+
icon={ArrowDown}
|
|
67
|
+
shape='circle'
|
|
68
|
+
pin='bottom-end'
|
|
69
|
+
>
|
|
70
|
+
<MediaImage src={parentSrc} size={48} shape='circle' />
|
|
71
|
+
</DotIcon>
|
|
72
|
+
<DotIcon
|
|
73
|
+
appearance='muted'
|
|
74
|
+
icon={ArrowDown}
|
|
75
|
+
shape='square'
|
|
76
|
+
pin='bottom-end'
|
|
77
|
+
>
|
|
78
|
+
<MediaImage src={parentSrc} size={48} shape='square' />
|
|
79
|
+
</DotIcon>
|
|
80
|
+
</Box>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const AppearanceShowcase: Story = {
|
|
85
|
+
args: { appearance: 'success', icon: ArrowDown },
|
|
86
|
+
render: () => (
|
|
87
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's32' }}>
|
|
88
|
+
<DotIcon
|
|
89
|
+
appearance='success'
|
|
90
|
+
icon={ArrowDown}
|
|
91
|
+
size={mediaImageDotIconSizeMap[48]}
|
|
92
|
+
pin='bottom-end'
|
|
93
|
+
>
|
|
94
|
+
<MediaImage src={parentSrc} size={48} shape='circle' />
|
|
95
|
+
</DotIcon>
|
|
96
|
+
<DotIcon
|
|
97
|
+
appearance='muted'
|
|
98
|
+
icon={ArrowUp}
|
|
99
|
+
size={mediaImageDotIconSizeMap[48]}
|
|
100
|
+
pin='bottom-end'
|
|
101
|
+
>
|
|
102
|
+
<MediaImage src={parentSrc} size={48} shape='circle' />
|
|
103
|
+
</DotIcon>
|
|
104
|
+
<DotIcon
|
|
105
|
+
appearance='error'
|
|
106
|
+
icon={Close}
|
|
107
|
+
size={mediaImageDotIconSizeMap[48]}
|
|
108
|
+
pin='bottom-end'
|
|
109
|
+
>
|
|
110
|
+
<MediaImage src={parentSrc} size={48} shape='circle' />
|
|
111
|
+
</DotIcon>
|
|
112
|
+
</Box>
|
|
113
|
+
),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const SizeShowcase: Story = {
|
|
117
|
+
args: { appearance: 'muted', icon: Link },
|
|
118
|
+
render: () => (
|
|
119
|
+
<Box lx={{ flexDirection: 'row', alignItems: 'flex-end', gap: 's24' }}>
|
|
120
|
+
<DotIcon
|
|
121
|
+
appearance='muted'
|
|
122
|
+
icon={Link}
|
|
123
|
+
size={mediaImageDotIconSizeMap[40]}
|
|
124
|
+
pin='bottom-end'
|
|
125
|
+
>
|
|
126
|
+
<MediaImage src={parentSrc} size={40} shape='circle' />
|
|
127
|
+
</DotIcon>
|
|
128
|
+
<DotIcon
|
|
129
|
+
appearance='success'
|
|
130
|
+
icon={Star}
|
|
131
|
+
size={mediaImageDotIconSizeMap[48]}
|
|
132
|
+
pin='bottom-end'
|
|
133
|
+
>
|
|
134
|
+
<MediaImage src={parentSrc} size={48} shape='circle' />
|
|
135
|
+
</DotIcon>
|
|
136
|
+
<DotIcon
|
|
137
|
+
appearance='success'
|
|
138
|
+
icon={ArrowDown}
|
|
139
|
+
size={mediaImageDotIconSizeMap[56]}
|
|
140
|
+
pin='bottom-end'
|
|
141
|
+
>
|
|
142
|
+
<MediaImage src={parentSrc} size={56} shape='circle' />
|
|
143
|
+
</DotIcon>
|
|
144
|
+
<DotIcon
|
|
145
|
+
appearance='muted'
|
|
146
|
+
icon={Spinner}
|
|
147
|
+
size={mediaImageDotIconSizeMap[64]}
|
|
148
|
+
pin='bottom-end'
|
|
149
|
+
>
|
|
150
|
+
<MediaImage src={parentSrc} size={64} shape='circle' />
|
|
151
|
+
</DotIcon>
|
|
152
|
+
</Box>
|
|
153
|
+
),
|
|
154
|
+
};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { ledgerLiveThemes } from '@ledgerhq/lumen-design-core';
|
|
3
|
+
import { render } from '@testing-library/react-native';
|
|
4
|
+
import { createRef } from 'react';
|
|
5
|
+
import type { View } from 'react-native';
|
|
6
|
+
import { Text } from 'react-native';
|
|
7
|
+
import { ArrowDown } from '../../Symbols';
|
|
8
|
+
import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
|
|
9
|
+
import { DotIcon } from './DotIcon';
|
|
10
|
+
|
|
11
|
+
const { colors, sizes, borderRadius } = ledgerLiveThemes.dark;
|
|
12
|
+
|
|
13
|
+
const TestWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
14
|
+
<ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
|
|
15
|
+
{children}
|
|
16
|
+
</ThemeProvider>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
describe('DotIcon Component', () => {
|
|
20
|
+
describe('Rendering', () => {
|
|
21
|
+
it('should render children', () => {
|
|
22
|
+
const { getByText } = render(
|
|
23
|
+
<TestWrapper>
|
|
24
|
+
<DotIcon testID='dot-icon' appearance='success' icon={ArrowDown}>
|
|
25
|
+
<Text>Child</Text>
|
|
26
|
+
</DotIcon>
|
|
27
|
+
</TestWrapper>,
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
expect(getByText('Child')).toBeTruthy();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should render without children', () => {
|
|
34
|
+
const { getByTestId } = render(
|
|
35
|
+
<TestWrapper>
|
|
36
|
+
<DotIcon testID='dot-icon' appearance='success' icon={ArrowDown} />
|
|
37
|
+
</TestWrapper>,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(getByTestId('dot-icon')).toBeTruthy();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should have correct displayName', () => {
|
|
44
|
+
expect(DotIcon.displayName).toBe('DotIcon');
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('Appearances', () => {
|
|
49
|
+
it.each([
|
|
50
|
+
{
|
|
51
|
+
appearance: 'success' as const,
|
|
52
|
+
expectedColor: colors.bg.successStrong,
|
|
53
|
+
},
|
|
54
|
+
{ appearance: 'muted' as const, expectedColor: colors.bg.mutedStrong },
|
|
55
|
+
{ appearance: 'error' as const, expectedColor: colors.bg.errorStrong },
|
|
56
|
+
])(
|
|
57
|
+
'should apply $appearance background color to the dot',
|
|
58
|
+
({ appearance, expectedColor }) => {
|
|
59
|
+
const { getByTestId } = render(
|
|
60
|
+
<TestWrapper>
|
|
61
|
+
<DotIcon
|
|
62
|
+
testID='dot-icon'
|
|
63
|
+
appearance={appearance}
|
|
64
|
+
icon={ArrowDown}
|
|
65
|
+
/>
|
|
66
|
+
</TestWrapper>,
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const dotView = getByTestId('dot-icon-dot');
|
|
70
|
+
expect(dotView.props.style.backgroundColor).toBe(expectedColor);
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('Sizes', () => {
|
|
76
|
+
it.each([
|
|
77
|
+
{ size: 16 as const, expectedSize: sizes.s16 },
|
|
78
|
+
{ size: 20 as const, expectedSize: sizes.s20 },
|
|
79
|
+
{ size: 24 as const, expectedSize: sizes.s24 },
|
|
80
|
+
])(
|
|
81
|
+
'should apply correct width and height for size $size',
|
|
82
|
+
({ size, expectedSize }) => {
|
|
83
|
+
const { getByTestId } = render(
|
|
84
|
+
<TestWrapper>
|
|
85
|
+
<DotIcon
|
|
86
|
+
testID='dot-icon'
|
|
87
|
+
appearance='success'
|
|
88
|
+
icon={ArrowDown}
|
|
89
|
+
size={size}
|
|
90
|
+
/>
|
|
91
|
+
</TestWrapper>,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const dotView = getByTestId('dot-icon-dot');
|
|
95
|
+
expect(dotView.props.style.width).toBe(expectedSize);
|
|
96
|
+
expect(dotView.props.style.height).toBe(expectedSize);
|
|
97
|
+
},
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('Pins', () => {
|
|
102
|
+
it.each([
|
|
103
|
+
{
|
|
104
|
+
pin: 'bottom-end' as const,
|
|
105
|
+
verticalKey: 'bottom',
|
|
106
|
+
horizontalKey: 'right',
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
pin: 'bottom-start' as const,
|
|
110
|
+
verticalKey: 'bottom',
|
|
111
|
+
horizontalKey: 'left',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
pin: 'top-end' as const,
|
|
115
|
+
verticalKey: 'top',
|
|
116
|
+
horizontalKey: 'right',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
pin: 'top-start' as const,
|
|
120
|
+
verticalKey: 'top',
|
|
121
|
+
horizontalKey: 'left',
|
|
122
|
+
},
|
|
123
|
+
])('should position dot at $pin', ({ pin, verticalKey, horizontalKey }) => {
|
|
124
|
+
const { getByTestId } = render(
|
|
125
|
+
<TestWrapper>
|
|
126
|
+
<DotIcon
|
|
127
|
+
testID='dot-icon'
|
|
128
|
+
appearance='success'
|
|
129
|
+
icon={ArrowDown}
|
|
130
|
+
pin={pin}
|
|
131
|
+
/>
|
|
132
|
+
</TestWrapper>,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const dotView = getByTestId('dot-icon-dot');
|
|
136
|
+
expect(dotView.props.style[verticalKey]).toBe(-3);
|
|
137
|
+
expect(dotView.props.style[horizontalKey]).toBe(-3);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('Shapes', () => {
|
|
142
|
+
it('should apply full border radius for circle shape', () => {
|
|
143
|
+
const { getByTestId } = render(
|
|
144
|
+
<TestWrapper>
|
|
145
|
+
<DotIcon
|
|
146
|
+
testID='dot-icon'
|
|
147
|
+
appearance='success'
|
|
148
|
+
icon={ArrowDown}
|
|
149
|
+
shape='circle'
|
|
150
|
+
/>
|
|
151
|
+
</TestWrapper>,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const dotView = getByTestId('dot-icon-dot');
|
|
155
|
+
expect(dotView.props.style.borderRadius).toBe(borderRadius.full);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it.each([
|
|
159
|
+
{ size: 16 as const, expectedRadius: 5 },
|
|
160
|
+
{ size: 20 as const, expectedRadius: 6 },
|
|
161
|
+
{ size: 24 as const, expectedRadius: 8 },
|
|
162
|
+
])(
|
|
163
|
+
'should apply correct border radius for square shape at size $size',
|
|
164
|
+
({ size, expectedRadius }) => {
|
|
165
|
+
const { getByTestId } = render(
|
|
166
|
+
<TestWrapper>
|
|
167
|
+
<DotIcon
|
|
168
|
+
testID='dot-icon'
|
|
169
|
+
appearance='success'
|
|
170
|
+
icon={ArrowDown}
|
|
171
|
+
shape='square'
|
|
172
|
+
size={size}
|
|
173
|
+
/>
|
|
174
|
+
</TestWrapper>,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const dotView = getByTestId('dot-icon-dot');
|
|
178
|
+
expect(dotView.props.style.borderRadius).toBe(expectedRadius);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('Ref forwarding', () => {
|
|
184
|
+
it('should forward ref to the root element', () => {
|
|
185
|
+
const ref = createRef<View>();
|
|
186
|
+
|
|
187
|
+
render(
|
|
188
|
+
<TestWrapper>
|
|
189
|
+
<DotIcon ref={ref} appearance='success' icon={ArrowDown} />
|
|
190
|
+
</TestWrapper>,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
expect(ref.current).not.toBeNull();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('Styling', () => {
|
|
198
|
+
it('should apply custom styles', () => {
|
|
199
|
+
const { getByTestId } = render(
|
|
200
|
+
<TestWrapper>
|
|
201
|
+
<DotIcon
|
|
202
|
+
testID='dot-icon'
|
|
203
|
+
appearance='success'
|
|
204
|
+
icon={ArrowDown}
|
|
205
|
+
style={{ marginTop: 10 }}
|
|
206
|
+
/>
|
|
207
|
+
</TestWrapper>,
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const root = getByTestId('dot-icon');
|
|
211
|
+
expect(root.props.style.marginTop).toBe(10);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should pass additional props', () => {
|
|
215
|
+
const { getByTestId } = render(
|
|
216
|
+
<TestWrapper>
|
|
217
|
+
<DotIcon testID='custom-dot' appearance='success' icon={ArrowDown} />
|
|
218
|
+
</TestWrapper>,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
expect(getByTestId('custom-dot')).toBeTruthy();
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
});
|