@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.
Files changed (124) hide show
  1. package/dist/module/index.js +1 -0
  2. package/dist/module/index.js.map +1 -1
  3. package/dist/module/lib/Components/Avatar/Avatar.js +22 -23
  4. package/dist/module/lib/Components/Avatar/Avatar.js.map +1 -1
  5. package/dist/module/lib/Components/Avatar/Avatar.test.js +17 -23
  6. package/dist/module/lib/Components/Avatar/Avatar.test.js.map +1 -1
  7. package/dist/module/lib/Components/DotCount/DotCount.js +128 -0
  8. package/dist/module/lib/Components/DotCount/DotCount.js.map +1 -0
  9. package/dist/module/lib/Components/DotCount/DotCount.mdx +86 -0
  10. package/dist/module/lib/Components/DotCount/DotCount.stories.js +172 -0
  11. package/dist/module/lib/Components/DotCount/DotCount.stories.js.map +1 -0
  12. package/dist/module/lib/Components/DotCount/DotCount.test.js +174 -0
  13. package/dist/module/lib/Components/DotCount/DotCount.test.js.map +1 -0
  14. package/dist/module/lib/Components/DotCount/index.js +5 -0
  15. package/dist/module/lib/Components/DotCount/index.js.map +1 -0
  16. package/dist/module/lib/Components/DotCount/types.js +4 -0
  17. package/dist/module/lib/Components/DotCount/types.js.map +1 -0
  18. package/dist/module/lib/Components/DotIcon/DotIcon.js +134 -0
  19. package/dist/module/lib/Components/DotIcon/DotIcon.js.map +1 -0
  20. package/dist/module/lib/Components/DotIcon/DotIcon.mdx +56 -0
  21. package/dist/module/lib/Components/DotIcon/DotIcon.stories.js +217 -0
  22. package/dist/module/lib/Components/DotIcon/DotIcon.stories.js.map +1 -0
  23. package/dist/module/lib/Components/DotIcon/DotIcon.test.js +238 -0
  24. package/dist/module/lib/Components/DotIcon/DotIcon.test.js.map +1 -0
  25. package/dist/module/lib/Components/DotIcon/index.js +5 -0
  26. package/dist/module/lib/Components/DotIcon/index.js.map +1 -0
  27. package/dist/module/lib/Components/DotIcon/types.js +4 -0
  28. package/dist/module/lib/Components/DotIcon/types.js.map +1 -0
  29. package/dist/module/lib/Components/DotIndicator/DotIndicator.js +89 -0
  30. package/dist/module/lib/Components/DotIndicator/DotIndicator.js.map +1 -0
  31. package/dist/module/lib/Components/DotIndicator/DotIndicator.mdx +82 -0
  32. package/dist/module/lib/Components/DotIndicator/DotIndicator.stories.js +84 -0
  33. package/dist/module/lib/Components/DotIndicator/DotIndicator.stories.js.map +1 -0
  34. package/dist/module/lib/Components/DotIndicator/DotIndicator.test.js +136 -0
  35. package/dist/module/lib/Components/DotIndicator/DotIndicator.test.js.map +1 -0
  36. package/dist/module/lib/Components/DotIndicator/index.js +5 -0
  37. package/dist/module/lib/Components/DotIndicator/index.js.map +1 -0
  38. package/dist/module/lib/Components/DotIndicator/types.js +4 -0
  39. package/dist/module/lib/Components/DotIndicator/types.js.map +1 -0
  40. package/dist/module/lib/Components/DotSymbol/DotSymbol.js +29 -22
  41. package/dist/module/lib/Components/DotSymbol/DotSymbol.js.map +1 -1
  42. package/dist/module/lib/Components/DotSymbol/DotSymbol.stories.js +31 -9
  43. package/dist/module/lib/Components/DotSymbol/DotSymbol.stories.js.map +1 -1
  44. package/dist/module/lib/Components/MediaImage/MediaImage.js +2 -1
  45. package/dist/module/lib/Components/MediaImage/MediaImage.js.map +1 -1
  46. package/dist/module/lib/Components/MediaImage/MediaImage.stories.js +4 -0
  47. package/dist/module/lib/Components/MediaImage/MediaImage.stories.js.map +1 -1
  48. package/dist/module/lib/Components/Spinner/Spinner.js +6 -1
  49. package/dist/module/lib/Components/Spinner/Spinner.js.map +1 -1
  50. package/dist/module/lib/Components/TabBar/TabBar.js +16 -13
  51. package/dist/module/lib/Components/TabBar/TabBar.js.map +1 -1
  52. package/dist/module/lib/Components/Tag/Tag.js +2 -0
  53. package/dist/module/lib/Components/Tag/Tag.js.map +1 -1
  54. package/dist/module/lib/Components/Tag/Tag.stories.js +8 -1
  55. package/dist/module/lib/Components/Tag/Tag.stories.js.map +1 -1
  56. package/dist/module/lib/Components/index.js +3 -0
  57. package/dist/module/lib/Components/index.js.map +1 -1
  58. package/dist/typescript/src/index.d.ts +1 -0
  59. package/dist/typescript/src/index.d.ts.map +1 -1
  60. package/dist/typescript/src/lib/Components/Avatar/Avatar.d.ts +1 -1
  61. package/dist/typescript/src/lib/Components/Avatar/Avatar.d.ts.map +1 -1
  62. package/dist/typescript/src/lib/Components/DotCount/DotCount.d.ts +3 -0
  63. package/dist/typescript/src/lib/Components/DotCount/DotCount.d.ts.map +1 -0
  64. package/dist/typescript/src/lib/Components/DotCount/index.d.ts +3 -0
  65. package/dist/typescript/src/lib/Components/DotCount/index.d.ts.map +1 -0
  66. package/dist/typescript/src/lib/Components/DotCount/types.d.ts +40 -0
  67. package/dist/typescript/src/lib/Components/DotCount/types.d.ts.map +1 -0
  68. package/dist/typescript/src/lib/Components/DotIcon/DotIcon.d.ts +30 -0
  69. package/dist/typescript/src/lib/Components/DotIcon/DotIcon.d.ts.map +1 -0
  70. package/dist/typescript/src/lib/Components/DotIcon/index.d.ts +3 -0
  71. package/dist/typescript/src/lib/Components/DotIcon/index.d.ts.map +1 -0
  72. package/dist/typescript/src/lib/Components/DotIcon/types.d.ts +40 -0
  73. package/dist/typescript/src/lib/Components/DotIcon/types.d.ts.map +1 -0
  74. package/dist/typescript/src/lib/Components/DotIndicator/DotIndicator.d.ts +3 -0
  75. package/dist/typescript/src/lib/Components/DotIndicator/DotIndicator.d.ts.map +1 -0
  76. package/dist/typescript/src/lib/Components/DotIndicator/index.d.ts +3 -0
  77. package/dist/typescript/src/lib/Components/DotIndicator/index.d.ts.map +1 -0
  78. package/dist/typescript/src/lib/Components/DotIndicator/types.d.ts +25 -0
  79. package/dist/typescript/src/lib/Components/DotIndicator/types.d.ts.map +1 -0
  80. package/dist/typescript/src/lib/Components/DotSymbol/DotSymbol.d.ts.map +1 -1
  81. package/dist/typescript/src/lib/Components/MediaImage/MediaImage.d.ts.map +1 -1
  82. package/dist/typescript/src/lib/Components/MediaImage/types.d.ts +1 -1
  83. package/dist/typescript/src/lib/Components/MediaImage/types.d.ts.map +1 -1
  84. package/dist/typescript/src/lib/Components/Spinner/Spinner.d.ts +1 -1
  85. package/dist/typescript/src/lib/Components/Spinner/Spinner.d.ts.map +1 -1
  86. package/dist/typescript/src/lib/Components/TabBar/TabBar.d.ts.map +1 -1
  87. package/dist/typescript/src/lib/Components/Tag/Tag.d.ts.map +1 -1
  88. package/dist/typescript/src/lib/Components/Tag/types.d.ts +1 -1
  89. package/dist/typescript/src/lib/Components/Tag/types.d.ts.map +1 -1
  90. package/dist/typescript/src/lib/Components/index.d.ts +3 -0
  91. package/dist/typescript/src/lib/Components/index.d.ts.map +1 -1
  92. package/package.json +1 -1
  93. package/src/index.ts +1 -0
  94. package/src/lib/Components/Avatar/Avatar.test.tsx +17 -27
  95. package/src/lib/Components/Avatar/Avatar.tsx +24 -21
  96. package/src/lib/Components/DotCount/DotCount.mdx +86 -0
  97. package/src/lib/Components/DotCount/DotCount.stories.tsx +124 -0
  98. package/src/lib/Components/DotCount/DotCount.test.tsx +150 -0
  99. package/src/lib/Components/DotCount/DotCount.tsx +130 -0
  100. package/src/lib/Components/DotCount/index.ts +2 -0
  101. package/src/lib/Components/DotCount/types.ts +40 -0
  102. package/src/lib/Components/DotIcon/DotIcon.mdx +56 -0
  103. package/src/lib/Components/DotIcon/DotIcon.stories.tsx +154 -0
  104. package/src/lib/Components/DotIcon/DotIcon.test.tsx +224 -0
  105. package/src/lib/Components/DotIcon/DotIcon.tsx +146 -0
  106. package/src/lib/Components/DotIcon/index.ts +6 -0
  107. package/src/lib/Components/DotIcon/types.ts +44 -0
  108. package/src/lib/Components/DotIndicator/DotIndicator.mdx +82 -0
  109. package/src/lib/Components/DotIndicator/DotIndicator.stories.tsx +67 -0
  110. package/src/lib/Components/DotIndicator/DotIndicator.test.tsx +132 -0
  111. package/src/lib/Components/DotIndicator/DotIndicator.tsx +97 -0
  112. package/src/lib/Components/DotIndicator/index.ts +2 -0
  113. package/src/lib/Components/DotIndicator/types.ts +25 -0
  114. package/src/lib/Components/DotSymbol/DotSymbol.stories.tsx +26 -7
  115. package/src/lib/Components/DotSymbol/DotSymbol.tsx +22 -23
  116. package/src/lib/Components/MediaImage/MediaImage.stories.tsx +1 -0
  117. package/src/lib/Components/MediaImage/MediaImage.tsx +3 -1
  118. package/src/lib/Components/MediaImage/types.ts +1 -1
  119. package/src/lib/Components/Spinner/Spinner.tsx +6 -2
  120. package/src/lib/Components/TabBar/TabBar.tsx +17 -16
  121. package/src/lib/Components/Tag/Tag.stories.tsx +11 -1
  122. package/src/lib/Components/Tag/Tag.tsx +2 -0
  123. package/src/lib/Components/Tag/types.ts +8 -1
  124. package/src/lib/Components/index.ts +3 -0
@@ -4,12 +4,15 @@ export * from './AmountInput';
4
4
  export * from './Avatar';
5
5
  export * from './Banner';
6
6
  export * from './BottomSheet';
7
+ export * from './DotCount';
8
+ export * from './DotIndicator';
7
9
  export * from './Button';
8
10
  export * from './Card';
9
11
  export * from './CardButton';
10
12
  export * from './ContentBanner';
11
13
  export * from './Checkbox';
12
14
  export * from './Divider';
15
+ export * from './DotIcon';
13
16
  export * from './DotSymbol';
14
17
  export * from './Icon';
15
18
  export * from './IconButton';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/Components/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,OAAO,CAAC;AACtB,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ledgerhq/lumen-ui-rnative",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "license": "Apache-2.0",
5
5
  "keywords": [
6
6
  "react-native",
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { type SupportedLocale, Languages } from './i18n';
2
+ export { useTheme, useStyleSheet } from './styles';
2
3
  export * from './lib/Components';
3
4
  export * from './lib/Animations';
4
5
  export * from './lib/Haptics';
@@ -4,7 +4,7 @@ import { render, waitFor } from '@testing-library/react-native';
4
4
  import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
5
5
  import { Avatar } from './Avatar';
6
6
 
7
- const { colors, sizes } = ledgerLiveThemes.dark;
7
+ const { sizes } = ledgerLiveThemes.dark;
8
8
 
9
9
  const TestWrapper = ({ children }: { children: React.ReactNode }) => (
10
10
  <ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
@@ -145,45 +145,39 @@ describe('Avatar Component', () => {
145
145
  });
146
146
 
147
147
  it('should show notification indicator when showNotification is true', () => {
148
- const { getByTestId } = render(
148
+ const { getByTestId, toJSON } = render(
149
149
  <TestWrapper>
150
150
  <Avatar testID='avatar-id' showNotification />
151
151
  </TestWrapper>,
152
152
  );
153
153
 
154
- const avatar = getByTestId('avatar-id');
155
- const notificationIndicator = avatar.props.children[0];
156
-
157
- expect(notificationIndicator).toBeTruthy();
158
- expect(notificationIndicator.props.style.backgroundColor).toBe(
159
- colors.bg.errorStrong,
160
- );
154
+ const tree = toJSON();
155
+ expect(tree.children).toHaveLength(2);
156
+ expect(tree.children[0].props.accessibilityRole).toBe('image');
157
+ expect(tree.children[0].props.pointerEvents).toBe('none');
158
+ expect(getByTestId('avatar-id')).toBeTruthy();
161
159
  });
162
160
 
163
161
  it('should not show notification indicator by default', () => {
164
- const { getByTestId } = render(
162
+ const { toJSON } = render(
165
163
  <TestWrapper>
166
164
  <Avatar testID='avatar-id' />
167
165
  </TestWrapper>,
168
166
  );
169
167
 
170
- const avatar = getByTestId('avatar-id');
171
- const notificationIndicator = avatar.props.children[0];
172
-
173
- expect(notificationIndicator).toBe(false);
168
+ const tree = toJSON();
169
+ expect(tree.props.testID).toBe('avatar-id');
174
170
  });
175
171
 
176
172
  it('should apply correct notification indicator size based on avatar size', () => {
177
- const { getByTestId, rerender } = render(
173
+ const { toJSON, rerender } = render(
178
174
  <TestWrapper>
179
175
  <Avatar testID='avatar-id' size='sm' showNotification />
180
176
  </TestWrapper>,
181
177
  );
182
178
 
183
- let avatar = getByTestId('avatar-id');
184
- let notificationIndicator = avatar.props.children[0];
185
- expect(notificationIndicator.props.style.width).toBe(sizes.s10);
186
- expect(notificationIndicator.props.style.height).toBe(sizes.s10);
179
+ let dot = toJSON().children[0];
180
+ expect(dot.props.style.height).toBe(sizes.s10);
187
181
 
188
182
  rerender(
189
183
  <TestWrapper>
@@ -191,10 +185,8 @@ describe('Avatar Component', () => {
191
185
  </TestWrapper>,
192
186
  );
193
187
 
194
- avatar = getByTestId('avatar-id');
195
- notificationIndicator = avatar.props.children[0];
196
- expect(notificationIndicator.props.style.width).toBe(sizes.s12);
197
- expect(notificationIndicator.props.style.height).toBe(sizes.s12);
188
+ dot = toJSON().children[0];
189
+ expect(dot.props.style.height).toBe(sizes.s12);
198
190
 
199
191
  rerender(
200
192
  <TestWrapper>
@@ -202,10 +194,8 @@ describe('Avatar Component', () => {
202
194
  </TestWrapper>,
203
195
  );
204
196
 
205
- avatar = getByTestId('avatar-id');
206
- notificationIndicator = avatar.props.children[0];
207
- expect(notificationIndicator.props.style.width).toBe(sizes.s16);
208
- expect(notificationIndicator.props.style.height).toBe(sizes.s16);
197
+ dot = toJSON().children[0];
198
+ expect(dot.props.style.height).toBe(sizes.s16);
209
199
  });
210
200
 
211
201
  it('should apply custom styles', () => {
@@ -1,8 +1,9 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { StyleSheet, Image, View } from 'react-native';
2
+ import { StyleSheet, Image } from 'react-native';
3
3
  import { useCommonTranslation } from '../../../i18n';
4
4
  import { useStyleSheet } from '../../../styles';
5
5
  import { User } from '../../Symbols';
6
+ import { DotIndicator } from '../DotIndicator';
6
7
  import { Box } from '../Utility';
7
8
  import type { AvatarProps } from './types';
8
9
 
@@ -14,6 +15,15 @@ const fallbackSizes = {
14
15
  lg: 32,
15
16
  } as const;
16
17
 
18
+ const dotSizeMap: Record<
19
+ Size,
20
+ NonNullable<React.ComponentProps<typeof DotIndicator>['size']>
21
+ > = {
22
+ sm: 'xs',
23
+ md: 'sm',
24
+ lg: 'md',
25
+ };
26
+
17
27
  const useStyles = ({ size }: { size: Size }) => {
18
28
  return useStyleSheet(
19
29
  (t) => {
@@ -23,12 +33,6 @@ const useStyles = ({ size }: { size: Size }) => {
23
33
  lg: { size: t.sizes.s72, padding: t.spacings.s4 },
24
34
  };
25
35
 
26
- const notificationsMap = {
27
- sm: t.sizes.s10,
28
- md: t.sizes.s12,
29
- lg: t.sizes.s16,
30
- };
31
-
32
36
  return {
33
37
  root: {
34
38
  position: 'relative',
@@ -40,16 +44,6 @@ const useStyles = ({ size }: { size: Size }) => {
40
44
  justifyContent: 'center',
41
45
  padding: sizeMap[size].padding,
42
46
  },
43
- notification: {
44
- position: 'absolute',
45
- top: 0,
46
- right: 0,
47
- width: notificationsMap[size],
48
- height: notificationsMap[size],
49
- borderRadius: 9999,
50
- backgroundColor: t.colors.bg.errorStrong,
51
- zIndex: 1,
52
- },
53
47
  image: {
54
48
  width: '100%',
55
49
  height: '100%',
@@ -85,6 +79,7 @@ export const Avatar = ({
85
79
  alt = 'avatar',
86
80
  size = 'md',
87
81
  showNotification = false,
82
+ testID,
88
83
  ref,
89
84
  ...props
90
85
  }: AvatarProps) => {
@@ -103,18 +98,16 @@ export const Avatar = ({
103
98
  setError(false);
104
99
  }, [src]);
105
100
 
106
- return (
101
+ const avatarContent = (
107
102
  <Box
108
103
  ref={ref}
109
104
  lx={lx}
110
105
  style={StyleSheet.flatten([styles.root, style])}
111
106
  accessibilityRole='image'
112
107
  accessibilityLabel={accessibilityLabel}
108
+ testID={showNotification ? undefined : testID}
113
109
  {...props}
114
110
  >
115
- {showNotification && (
116
- <View style={styles.notification} accessible={false} />
117
- )}
118
111
  {shouldFallback ? (
119
112
  <User
120
113
  size={fallbackSizes[size]}
@@ -132,4 +125,14 @@ export const Avatar = ({
132
125
  )}
133
126
  </Box>
134
127
  );
128
+
129
+ if (showNotification) {
130
+ return (
131
+ <DotIndicator size={dotSizeMap[size]} appearance='red' testID={testID}>
132
+ {avatarContent}
133
+ </DotIndicator>
134
+ );
135
+ }
136
+
137
+ return avatarContent;
135
138
  };
@@ -0,0 +1,86 @@
1
+ import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks';
2
+ import * as DotCountStories from './DotCount.stories';
3
+ import { CustomTabs, Tab } from '../../../../.storybook/components';
4
+
5
+ <Meta title='Communication/DotCount' of={DotCountStories} />
6
+
7
+ # DotCount
8
+
9
+ <CustomTabs>
10
+ <Tab label="Overview">
11
+
12
+ ## Introduction
13
+
14
+ DotCount displays a numeric count inside a colored pill. It supports two sizes (`lg` and `md`), two appearances (`base` and `red`), and can be pinned to the top-right of a child element.
15
+
16
+ > View in [Figma](https://www.figma.com/design/JxaLVMTWirCpU0rsbZ30k7/2.-Components-Library?node-id=38-998).
17
+
18
+ ## Properties
19
+
20
+ <Canvas of={DotCountStories.Base} />
21
+ <Controls of={DotCountStories.Base} />
22
+
23
+ ## Sizes
24
+
25
+ DotCount comes in two sizes:
26
+
27
+ - **lg** - large pill size for prominent counters.
28
+ - **md** (default) - standard pill size, suitable for most use cases.
29
+
30
+ <Canvas of={DotCountStories.SizeShowcase} />
31
+
32
+ ## Appearances
33
+
34
+ Two color schemes are available:
35
+
36
+ - **base** (default) - neutral tone for general-purpose counters.
37
+ - **red** - attention-grabbing, typically used for unread counts or alerts.
38
+
39
+ <Canvas of={DotCountStories.AppearanceShowcase} />
40
+
41
+ ## Overflow & Max
42
+
43
+ When `value` exceeds `max`, the display shows `[max]+`. Values of `0` or below hide the text. If you need a DotCount-like component but without the count, consider the DotIndicator instead.
44
+
45
+ <Canvas of={DotCountStories.OverflowShowcase} />
46
+
47
+ ## With Children
48
+
49
+ When wrapping a child element, the count pill pins itself to the top-right corner.
50
+
51
+ <Canvas of={DotCountStories.WithChildren} />
52
+
53
+ </Tab>
54
+ <Tab label="Implementation">
55
+
56
+ ## Setup
57
+
58
+ Install and set up the library with our [Setup Guide →](?path=/docs/getting-started-setup--docs).
59
+
60
+ ## Basic Usage
61
+
62
+ ```tsx
63
+ import { DotCount } from '@ledgerhq/lumen-ui-rnative';
64
+
65
+ function MyComponent() {
66
+ return <DotCount value={5} size="lg" />;
67
+ }
68
+ ```
69
+
70
+ ### Pinned to a child
71
+
72
+ ```tsx
73
+ <DotCount value={3} size="md">
74
+ <Avatar src="..." size="md" />
75
+ </DotCount>
76
+ ```
77
+
78
+ ### Custom max
79
+
80
+ ```tsx
81
+ <DotCount value={150} max={50} size="lg" />
82
+ {/* Renders "50+" */}
83
+ ```
84
+
85
+ </Tab>
86
+ </CustomTabs>
@@ -0,0 +1,124 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-native-web-vite';
2
+ import { useState } from 'react';
3
+ import { Avatar } from '../Avatar/Avatar';
4
+ import { MediaImage } from '../MediaImage/MediaImage';
5
+ import {
6
+ SegmentedControl,
7
+ SegmentedControlButton,
8
+ } from '../SegmentedControl/SegmentedControl';
9
+ import { Box } from '../Utility/Box';
10
+ import { DotCount } from './DotCount';
11
+
12
+ const meta = {
13
+ component: DotCount,
14
+ title: 'Communication/DotCount',
15
+ parameters: {
16
+ docs: {
17
+ source: {
18
+ language: 'tsx',
19
+ format: true,
20
+ type: 'code',
21
+ },
22
+ },
23
+ },
24
+ argTypes: {
25
+ size: {
26
+ control: 'radio',
27
+ options: ['lg', 'md'],
28
+ },
29
+ appearance: {
30
+ control: 'radio',
31
+ options: ['base', 'red'],
32
+ },
33
+ value: {
34
+ control: 'number',
35
+ },
36
+ max: {
37
+ control: 'number',
38
+ },
39
+ },
40
+ } satisfies Meta<typeof DotCount>;
41
+
42
+ export default meta;
43
+ type Story = StoryObj<typeof meta>;
44
+
45
+ export const Base: Story = {
46
+ args: {
47
+ value: 3,
48
+ size: 'lg',
49
+ appearance: 'base',
50
+ },
51
+ };
52
+
53
+ export const SizeShowcase: Story = {
54
+ args: { value: 5 },
55
+ render: () => (
56
+ <Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's12' }}>
57
+ <DotCount value={5} size='lg' />
58
+ <DotCount value={5} size='md' />
59
+ </Box>
60
+ ),
61
+ };
62
+
63
+ export const AppearanceShowcase: Story = {
64
+ args: { value: 3 },
65
+ render: () => (
66
+ <Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's12' }}>
67
+ <DotCount value={3} size='lg' appearance='base' />
68
+ <DotCount value={3} size='lg' appearance='red' />
69
+ <DotCount value={3} size='lg' disabled />
70
+ </Box>
71
+ ),
72
+ };
73
+
74
+ export const OverflowShowcase: Story = {
75
+ args: { value: 100 },
76
+ render: () => (
77
+ <Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's12' }}>
78
+ <DotCount value={100} size='lg' />
79
+ <DotCount value={100} max={50} size='lg' />
80
+ <DotCount value={0} size='lg' />
81
+ </Box>
82
+ ),
83
+ };
84
+
85
+ export const WithChildren: Story = {
86
+ args: { value: 5 },
87
+ render: () => {
88
+ const [fitState, setFitState] = useState('preview');
89
+
90
+ return (
91
+ <Box lx={{ gap: 's24' }}>
92
+ <Box lx={{ flexDirection: 'row', alignItems: 'center', gap: 's12' }}>
93
+ <DotCount value={5} size='md'>
94
+ <MediaImage
95
+ src='https://crypto-icons.ledger.com/BTC.png'
96
+ alt='Bitcoin'
97
+ size={40}
98
+ shape='circle'
99
+ />
100
+ </DotCount>
101
+ <DotCount value={100} size='md'>
102
+ <Avatar
103
+ src='https://plus.unsplash.com/premium_photo-1689551670902-19b441a6afde?q=80&w=774&auto=format&fit=crop'
104
+ size='md'
105
+ />
106
+ </DotCount>
107
+ </Box>
108
+ <SegmentedControl
109
+ selectedValue={fitState}
110
+ onSelectedChange={setFitState}
111
+ tabLayout='fit'
112
+ accessibilityLabel='Fit layout'
113
+ >
114
+ <SegmentedControlButton value='preview'>
115
+ Preview
116
+ <DotCount value={3} size='md' style={{ marginLeft: 6 }} />
117
+ </SegmentedControlButton>
118
+ <SegmentedControlButton value='raw'>Raw</SegmentedControlButton>
119
+ <SegmentedControlButton value='blame'>Blame</SegmentedControlButton>
120
+ </SegmentedControl>
121
+ </Box>
122
+ );
123
+ },
124
+ };
@@ -0,0 +1,150 @@
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 { Text, type View } from 'react-native';
6
+ import { ThemeProvider } from '../ThemeProvider/ThemeProvider';
7
+ import { DotCount } from './DotCount';
8
+
9
+ const TestWrapper = ({ children }: { children: React.ReactNode }) => (
10
+ <ThemeProvider themes={ledgerLiveThemes} colorScheme='dark' locale='en'>
11
+ {children}
12
+ </ThemeProvider>
13
+ );
14
+
15
+ describe('DotCount', () => {
16
+ it('should render the value', () => {
17
+ const { getByText } = render(
18
+ <TestWrapper>
19
+ <DotCount value={5} size='lg' />
20
+ </TestWrapper>,
21
+ );
22
+
23
+ expect(getByText('5')).toBeTruthy();
24
+ });
25
+
26
+ it('should hide text when value is 0', () => {
27
+ const { queryByText } = render(
28
+ <TestWrapper>
29
+ <DotCount value={0} size='lg' />
30
+ </TestWrapper>,
31
+ );
32
+
33
+ expect(queryByText('0')).toBeNull();
34
+ });
35
+
36
+ it('should cap value at max and show overflow indicator', () => {
37
+ const { getByText } = render(
38
+ <TestWrapper>
39
+ <DotCount value={100} max={50} size='lg' />
40
+ </TestWrapper>,
41
+ );
42
+
43
+ expect(getByText('50+')).toBeTruthy();
44
+ });
45
+
46
+ it('should default max to 99', () => {
47
+ const { getByText } = render(
48
+ <TestWrapper>
49
+ <DotCount value={150} size='lg' />
50
+ </TestWrapper>,
51
+ );
52
+
53
+ expect(getByText('99+')).toBeTruthy();
54
+ });
55
+
56
+ it('should clamp max to 1 when given zero or negative', () => {
57
+ const { getByText } = render(
58
+ <TestWrapper>
59
+ <DotCount value={5} max={0} size='lg' />
60
+ </TestWrapper>,
61
+ );
62
+
63
+ expect(getByText('1+')).toBeTruthy();
64
+ });
65
+
66
+ it('should render children alongside the count', () => {
67
+ const { getByText } = render(
68
+ <TestWrapper>
69
+ <DotCount value={3} size='md'>
70
+ <Text>Child</Text>
71
+ </DotCount>
72
+ </TestWrapper>,
73
+ );
74
+
75
+ expect(getByText('3')).toBeTruthy();
76
+ expect(getByText('Child')).toBeTruthy();
77
+ });
78
+
79
+ it('should forward testID to the outer wrapper', () => {
80
+ const { getByTestId } = render(
81
+ <TestWrapper>
82
+ <DotCount testID='dot-count' value={3} size='lg' />
83
+ </TestWrapper>,
84
+ );
85
+
86
+ expect(getByTestId('dot-count')).toBeTruthy();
87
+ });
88
+
89
+ it('should forward pointerEvents to the outer wrapper', () => {
90
+ const { getByTestId } = render(
91
+ <TestWrapper>
92
+ <DotCount testID='dot-count' value={3} size='lg' pointerEvents='none' />
93
+ </TestWrapper>,
94
+ );
95
+
96
+ expect(getByTestId('dot-count').props.pointerEvents).toBe('none');
97
+ });
98
+
99
+ it('should forward ref to the outer wrapper', () => {
100
+ const ref = createRef<View>();
101
+
102
+ render(
103
+ <TestWrapper>
104
+ <DotCount ref={ref} value={3} size='lg' />
105
+ </TestWrapper>,
106
+ );
107
+
108
+ expect(ref.current).toBeTruthy();
109
+ });
110
+
111
+ it('should render with red appearance', () => {
112
+ const { getByText } = render(
113
+ <TestWrapper>
114
+ <DotCount value={5} size='lg' appearance='red' />
115
+ </TestWrapper>,
116
+ );
117
+
118
+ expect(getByText('5')).toBeTruthy();
119
+ });
120
+
121
+ it('should render with disabled state', () => {
122
+ const { getByText } = render(
123
+ <TestWrapper>
124
+ <DotCount value={5} size='lg' disabled />
125
+ </TestWrapper>,
126
+ );
127
+
128
+ expect(getByText('5')).toBeTruthy();
129
+ });
130
+
131
+ it('should render in md size', () => {
132
+ const { getByText } = render(
133
+ <TestWrapper>
134
+ <DotCount value={5} size='md' />
135
+ </TestWrapper>,
136
+ );
137
+
138
+ expect(getByText('5')).toBeTruthy();
139
+ });
140
+
141
+ it('should default size to md when omitted', () => {
142
+ const { getByText } = render(
143
+ <TestWrapper>
144
+ <DotCount value={5} />
145
+ </TestWrapper>,
146
+ );
147
+
148
+ expect(getByText('5')).toBeTruthy();
149
+ });
150
+ });