@bethinkpl/design-system 26.10.2 → 26.11.0

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 (94) hide show
  1. package/.eslintrc.cjs +2 -3
  2. package/dist/design-system.umd.cjs +18 -18
  3. package/dist/design-system.umd.cjs.map +1 -1
  4. package/dist/lib/js/components/Avatar/Avatar.consts.d.ts +16 -1
  5. package/dist/lib/js/components/Avatar/Avatar.vue.d.ts +10 -21
  6. package/dist/lib/js/components/Badge/Badge.consts.d.ts +24 -0
  7. package/dist/lib/js/components/Badge/Badge.vue.d.ts +28 -0
  8. package/dist/lib/js/components/Badge/index.d.ts +4 -0
  9. package/dist/lib/js/components/BadgeScore/BadgeScore.vue.d.ts +2 -2
  10. package/dist/lib/js/components/Banner/Banner.vue.d.ts +7 -7
  11. package/dist/lib/js/components/Buttons/Button/Button.vue.d.ts +2 -2
  12. package/dist/lib/js/components/Buttons/IconButton/IconButton.vue.d.ts +4 -4
  13. package/dist/lib/js/components/Cards/CardExpandable/CardExpandable.vue.d.ts +1 -1
  14. package/dist/lib/js/components/Chip/Chip.vue.d.ts +5 -5
  15. package/dist/lib/js/components/DatePickers/DateBox/DateBox.vue.d.ts +1 -1
  16. package/dist/lib/js/components/DatePickers/DatePicker/DatePicker.vue.d.ts +4 -4
  17. package/dist/lib/js/components/DatePickers/DateRangePicker/DateRangePicker.vue.d.ts +1 -1
  18. package/dist/lib/js/components/Drawer/DrawerHeader/DrawerHeader.vue.d.ts +12 -12
  19. package/dist/lib/js/components/Drawer/DrawerListItem/DrawerListItem.vue.d.ts +1 -1
  20. package/dist/lib/js/components/Drawer/DrawerSection/DrawerSection.vue.d.ts +7 -7
  21. package/dist/lib/js/components/Drawer/DrawerTile/DrawerTile.vue.d.ts +1 -1
  22. package/dist/lib/js/components/Form/Checkbox/Checkbox.vue.d.ts +1 -1
  23. package/dist/lib/js/components/Form/RadioButton/RadioButton.vue.d.ts +1 -1
  24. package/dist/lib/js/components/Form/SelectionControl/SelectionControl.vue.d.ts +1 -1
  25. package/dist/lib/js/components/Headers/OverlayHeader/OverlayHeader.vue.d.ts +4 -4
  26. package/dist/lib/js/components/Headers/SectionHeader/SectionHeader.vue.d.ts +6 -6
  27. package/dist/lib/js/components/IconText/IconText.vue.d.ts +1 -1
  28. package/dist/lib/js/components/Icons/FeatureIcon/FeatureIcon.vue.d.ts +2 -2
  29. package/dist/lib/js/components/Icons/Icon/Icon.vue.d.ts +1 -1
  30. package/dist/lib/js/components/Modal/Modal.vue.d.ts +1 -1
  31. package/dist/lib/js/components/Modals/Modal/Modal.vue.d.ts +11 -11
  32. package/dist/lib/js/components/Modals/ModalDialog/ModalDialog.vue.d.ts +11 -11
  33. package/dist/lib/js/components/Outline/OutlineItem/OutlineItem.vue.d.ts +1 -1
  34. package/dist/lib/js/components/Pagination/Pagination.vue.d.ts +5 -5
  35. package/dist/lib/js/components/ProgressBar/ProgressBar.vue.d.ts +1 -1
  36. package/dist/lib/js/components/ProgressDonutChart/ProgressDonutChart.vue.d.ts +1 -1
  37. package/dist/lib/js/components/RichList/BasicRichListItem/BasicRichListItem.vue.d.ts +6 -6
  38. package/dist/lib/js/components/RichList/RichListItem/RichListItem.vue.d.ts +4 -4
  39. package/dist/lib/js/components/SelectList/SelectListItem/SelectListItem.vue.d.ts +1 -1
  40. package/dist/lib/js/components/SelectList/SelectListItemTile/SelectListItemTile.vue.d.ts +1 -1
  41. package/dist/lib/js/components/SelectList/SelectListItemToggle/SelectListItemToggle.vue.d.ts +1 -1
  42. package/dist/lib/js/components/SelectionTile/SelectionTile.vue.d.ts +5 -5
  43. package/dist/lib/js/components/Statuses/AccessStatus/AccessStatus.vue.d.ts +1 -1
  44. package/dist/lib/js/components/Statuses/BlockadeStatus/BlockadeStatus.vue.d.ts +1 -1
  45. package/dist/lib/js/components/SurveyQuestions/SurveyQuestionOpenEnded/SurveyQuestionOpenEnded.vue.d.ts +7 -7
  46. package/dist/lib/js/components/SurveyQuestions/SurveyQuestionScale/SurveyQuestionScale.vue.d.ts +8 -8
  47. package/dist/lib/js/components/SurveyToggle/SurveyToggle.vue.d.ts +1 -1
  48. package/dist/lib/js/components/Switch/Switch.vue.d.ts +1 -1
  49. package/dist/lib/js/components/TabItem/TabItem.vue.d.ts +2 -2
  50. package/dist/lib/js/components/Tile/Tile.sb.shared.d.ts +9 -9
  51. package/dist/lib/js/components/Tile/Tile.vue.d.ts +1 -1
  52. package/dist/lib/js/components/Toast/Toast.vue.d.ts +4 -4
  53. package/dist/lib/js/components/Toggles/CounterToggle/CounterToggle.vue.d.ts +1 -1
  54. package/dist/lib/js/components/Toggles/ToggleButton/ToggleButton.vue.d.ts +1 -1
  55. package/dist/lib/js/index.d.ts +2 -0
  56. package/docs/assets/Avatar-BY2FHjur.js +1 -0
  57. package/docs/assets/Avatar.stories-CHgyprX4.js +37 -0
  58. package/docs/assets/Badge-DWUXekFu.js +1 -0
  59. package/docs/assets/Badge.stories-B90k4kes.js +21 -0
  60. package/docs/assets/{BasicRichListItem.stories-CJLLsCH2.js → BasicRichListItem.stories-Cq_2y-ip.js} +2 -2
  61. package/docs/assets/{Color-ERTF36HU-06Iq2w_Z.js → Color-ERTF36HU-gYu0mrgM.js} +1 -1
  62. package/docs/assets/{DateBox.stories-DL_j7ydW.js → DateBox.stories-2Z3tCp_1.js} +1 -1
  63. package/docs/assets/{DatePicker-CcKDLhxp.js → DatePicker-C2ooefT-.js} +1 -1
  64. package/docs/assets/{DatePicker.stories-CK6I-6c-.js → DatePicker.stories-CwMU6bVO.js} +1 -1
  65. package/docs/assets/{DateRangePicker-laHPjs-v.js → DateRangePicker-BbtTUk2P.js} +1 -1
  66. package/docs/assets/{DateRangePicker.stories-BtP6-oba.js → DateRangePicker.stories-DLDTFPz3.js} +1 -1
  67. package/docs/assets/{DocsRenderer-CFRXHY34-D86268a8.js → DocsRenderer-CFRXHY34-CCozPr0-.js} +5 -5
  68. package/docs/assets/{RichListItem.stories-xEDsV2D_.js → RichListItem.stories-Bjn9_BVO.js} +2 -2
  69. package/docs/assets/{SelectionTile-DSpUGzPs.js → SelectionTile-BKeLO5-6.js} +1 -1
  70. package/docs/assets/{SelectionTile.stories-WMHY5FHJ.js → SelectionTile.stories-BSF1jx58.js} +1 -1
  71. package/docs/assets/{iframe-DJBrrd4-.js → iframe-DM9VVuiu.js} +4 -3
  72. package/docs/assets/{index-C_7Ic1-A.js → index-BLJge5bw.js} +1 -1
  73. package/docs/assets/{index-nq_4YiNl.js → index-GQAS3BWp.js} +1 -1
  74. package/docs/assets/{preview-TdvdvCat.js → preview-Bx8DeNCo.js} +2 -2
  75. package/docs/assets/preview-IY5bCRtO.js +64 -0
  76. package/docs/iframe.html +1 -1
  77. package/docs/index.json +1 -1
  78. package/docs/project.json +1 -1
  79. package/lib/js/components/Avatar/Avatar.consts.ts +20 -1
  80. package/lib/js/components/Avatar/Avatar.spec.ts +83 -3
  81. package/lib/js/components/Avatar/Avatar.stories.ts +24 -4
  82. package/lib/js/components/Avatar/Avatar.vue +210 -46
  83. package/lib/js/components/Badge/Badge.consts.ts +29 -0
  84. package/lib/js/components/Badge/Badge.spec.ts +168 -0
  85. package/lib/js/components/Badge/Badge.stories.ts +99 -0
  86. package/lib/js/components/Badge/Badge.vue +297 -0
  87. package/lib/js/components/Badge/index.ts +4 -0
  88. package/lib/js/components/Chip/Chip.stories.ts +1 -1
  89. package/lib/js/index.ts +2 -0
  90. package/package.json +3 -2
  91. package/docs/assets/Avatar-OqeGv3bT.js +0 -1
  92. package/docs/assets/Avatar.stories-Ran7nhCi.js +0 -35
  93. package/docs/assets/preview-hR283N6x.js +0 -64
  94. /package/lib/{js/components/Chip → images}/logo-badge.svg +0 -0
@@ -19,6 +19,22 @@
19
19
  <img v-if="!!avatarUrl" :src="avatarUrl" :alt="username" class="ds-avatar__image" />
20
20
  <span v-else class="ds-avatar__initials">{{ initials }}</span>
21
21
  </div>
22
+ <div v-if="activityStatus" class="ds-avatar__activityStatus">
23
+ <ds-badge
24
+ :color="activityStatusColor"
25
+ :elevation="BADGE_ELEVATIONS.X_SMALL"
26
+ :size="activityStatusSize"
27
+ />
28
+ </div>
29
+ <div v-if="accessStatus" class="ds-avatar__accessStatus">
30
+ <ds-badge
31
+ :color="accessStatusColor"
32
+ :elevation="BADGE_ELEVATIONS.SMALL"
33
+ :icon="accessStatusIcon"
34
+ :size="accessStatusSize"
35
+ :image-url="accessStatusImage"
36
+ />
37
+ </div>
22
38
  </div>
23
39
  </template>
24
40
 
@@ -31,6 +47,7 @@
31
47
  $root: &;
32
48
 
33
49
  display: flex;
50
+ position: relative;
34
51
 
35
52
  &.-ds-xx-small {
36
53
  height: 24px;
@@ -117,62 +134,209 @@
117
134
  height: 100%;
118
135
  width: 100%;
119
136
  }
137
+
138
+ &__accessStatus {
139
+ @at-root {
140
+ .ds-avatar.-ds-xx-small & {
141
+ bottom: -2px;
142
+ right: -2px;
143
+ }
144
+
145
+ .ds-avatar.-ds-large & {
146
+ bottom: 1px;
147
+ right: 1px;
148
+ }
149
+ }
150
+
151
+ bottom: 0;
152
+ display: flex;
153
+ position: absolute;
154
+ right: 0;
155
+ }
156
+
157
+ &__activityStatus {
158
+ display: flex;
159
+ left: -4px;
160
+ position: absolute;
161
+ top: -4px;
162
+
163
+ .ds-avatar.-ds-xx-small &,
164
+ .ds-avatar.-ds-x-small &,
165
+ .ds-avatar.-ds-small & {
166
+ left: -3px;
167
+ top: -3px;
168
+ }
169
+ }
120
170
  }
121
171
  </style>
122
172
 
123
173
  <script setup lang="ts">
124
- import { AVATAR_SIZES, AvatarSize } from './Avatar.consts';
174
+ import {
175
+ AVATAR_ACCESS_STATUSES,
176
+ AVATAR_ACTIVITY_STATUSES,
177
+ AVATAR_SIZES,
178
+ AvatarAccessStatus,
179
+ AvatarActivityStatus,
180
+ AvatarSize,
181
+ } from './Avatar.consts';
182
+ import DsBadge, { BADGE_ELEVATIONS, BADGE_SIZES, BADGE_COLORS } from '../Badge';
125
183
  import { computed } from 'vue';
184
+ import { ICONS } from '../Icons/Icon';
185
+
186
+ const {
187
+ size = AVATAR_SIZES.X_SMALL,
188
+ username,
189
+ avatarUrl,
190
+ activityStatus,
191
+ accessStatus,
192
+ teamMemberImageUrl,
193
+ } = defineProps<{
194
+ username: string;
195
+ avatarUrl?: string;
196
+ size?: AvatarSize;
197
+ activityStatus?: AvatarActivityStatus;
198
+ accessStatus?: AvatarAccessStatus;
199
+ teamMemberImageUrl?: string;
200
+ }>();
201
+
202
+ const { initials, initialBackgroundColor } = useInitials();
203
+ const { accessStatusColor, accessStatusIcon, accessStatusSize, accessStatusImage } =
204
+ useAccessStatus();
205
+ const { activityStatusColor, activityStatusSize } = useActivityStatus();
206
+ function useInitials() {
207
+ const initialsBackgrounds = [
208
+ '#1abc9c',
209
+ '#2ecc71',
210
+ '#3498db',
211
+ '#9b59b6',
212
+ '#34495e',
213
+ '#16a085',
214
+ '#27ae60',
215
+ '#2980b9',
216
+ '#8e44ad',
217
+ '#2c3e50',
218
+ '#f1c40f',
219
+ '#e67e22',
220
+ '#e74c3c',
221
+ '#f39c12',
222
+ '#d35400',
223
+ '#c0392b',
224
+ ];
126
225
 
127
- const props = withDefaults(
128
- defineProps<{
129
- username: string;
130
- avatarUrl?: string;
131
- size?: AvatarSize;
132
- }>(),
133
- {
134
- size: AVATAR_SIZES.X_SMALL,
135
- avatarUrl: undefined,
136
- },
137
- );
138
-
139
- const initialsBackgrounds = [
140
- '#1abc9c',
141
- '#2ecc71',
142
- '#3498db',
143
- '#9b59b6',
144
- '#34495e',
145
- '#16a085',
146
- '#27ae60',
147
- '#2980b9',
148
- '#8e44ad',
149
- '#2c3e50',
150
- '#f1c40f',
151
- '#e67e22',
152
- '#e74c3c',
153
- '#f39c12',
154
- '#d35400',
155
- '#c0392b',
156
- ];
157
-
158
- function getInitials(username: string) {
159
- const [first, second] = username.split(/\s+/);
160
-
161
- if (first && second) {
162
- return `${first[0]}${second[0]}`.toUpperCase();
226
+ function getInitials(username: string) {
227
+ const [first, second] = username.split(/\s+/);
228
+
229
+ if (first && second) {
230
+ return `${first[0]}${second[0]}`.toUpperCase();
231
+ }
232
+
233
+ return first.substring(0, 2).toUpperCase();
163
234
  }
164
235
 
165
- return first.substring(0, 2).toUpperCase();
236
+ const initialBackgroundColor = computed(() => {
237
+ if (avatarUrl) {
238
+ return;
239
+ }
240
+
241
+ const colorIndex = (username.charCodeAt(0) - 65) % 16;
242
+
243
+ return initialsBackgrounds[colorIndex];
244
+ });
245
+
246
+ const initials = computed(() => getInitials(username));
247
+
248
+ return { initials, initialBackgroundColor };
166
249
  }
167
250
 
168
- const initialBackgroundColor = computed(() => {
169
- if (props.avatarUrl) {
170
- return;
171
- }
251
+ function useAccessStatus() {
252
+ const accessStatusColor = computed(() => {
253
+ switch (accessStatus) {
254
+ case AVATAR_ACCESS_STATUSES.ACTIVE:
255
+ return BADGE_COLORS.SUCCESS;
256
+ case AVATAR_ACCESS_STATUSES.INACTIVE:
257
+ case AVATAR_ACCESS_STATUSES.AWAITING:
258
+ return BADGE_COLORS.NEUTRAL;
259
+ case AVATAR_ACCESS_STATUSES.BLOCKED:
260
+ return BADGE_COLORS.DANGER;
261
+ default:
262
+ return undefined;
263
+ }
264
+ });
172
265
 
173
- const colorIndex = (props.username.charCodeAt(0) - 65) % 16;
266
+ const accessStatusIcon = computed(() => {
267
+ switch (accessStatus) {
268
+ case AVATAR_ACCESS_STATUSES.ACTIVE:
269
+ return ICONS.FA_UNLOCK_KEYHOLE;
270
+ case AVATAR_ACCESS_STATUSES.BLOCKED:
271
+ case AVATAR_ACCESS_STATUSES.INACTIVE:
272
+ return ICONS.FA_LOCK_KEYHOLE;
273
+ case AVATAR_ACCESS_STATUSES.AWAITING:
274
+ return ICONS.FA_HOURGLASS_START;
275
+ default:
276
+ return undefined;
277
+ }
278
+ });
174
279
 
175
- return initialsBackgrounds[colorIndex];
176
- });
177
- const initials = computed(() => getInitials(props.username));
280
+ const accessStatusSize = computed(() => {
281
+ // Casting to AvatarSize to work around an IDE issue (PhpStorm incorrectly flags some case branches as unreachable)
282
+ switch (size as AvatarSize) {
283
+ case AVATAR_SIZES.XX_SMALL:
284
+ return BADGE_SIZES.SMALL;
285
+ case AVATAR_SIZES.X_SMALL:
286
+ return BADGE_SIZES.SMALL;
287
+ case AVATAR_SIZES.SMALL:
288
+ return BADGE_SIZES.SMALL;
289
+ case AVATAR_SIZES.MEDIUM:
290
+ return BADGE_SIZES.MEDIUM;
291
+ case AVATAR_SIZES.LARGE:
292
+ return BADGE_SIZES.MEDIUM;
293
+ case AVATAR_SIZES.X_LARGE:
294
+ default:
295
+ return BADGE_SIZES.X_LARGE;
296
+ }
297
+ });
298
+
299
+ const accessStatusImage = computed(() => {
300
+ if (accessStatus !== AVATAR_ACCESS_STATUSES.TEAM_MEMBER) {
301
+ return undefined;
302
+ }
303
+
304
+ return teamMemberImageUrl;
305
+ });
306
+
307
+ return { accessStatusColor, accessStatusIcon, accessStatusSize, accessStatusImage };
308
+ }
309
+
310
+ function useActivityStatus() {
311
+ const activityStatusColor = computed(() => {
312
+ switch (activityStatus) {
313
+ case AVATAR_ACTIVITY_STATUSES.ACTIVE:
314
+ return BADGE_COLORS.SUCCESS;
315
+ default:
316
+ case AVATAR_ACTIVITY_STATUSES.INACTIVE:
317
+ return BADGE_COLORS.NEUTRAL;
318
+ }
319
+ });
320
+
321
+ const activityStatusSize = computed(() => {
322
+ // Casting to AvatarSize to work around an IDE issue (PhpStorm incorrectly flags some case branches as unreachable)
323
+ switch (size as AvatarSize) {
324
+ case AVATAR_SIZES.XX_SMALL:
325
+ return BADGE_SIZES.X_SMALL;
326
+ case AVATAR_SIZES.X_SMALL:
327
+ return BADGE_SIZES.SMALL;
328
+ case AVATAR_SIZES.SMALL:
329
+ return BADGE_SIZES.SMALL;
330
+ case AVATAR_SIZES.MEDIUM:
331
+ return BADGE_SIZES.MEDIUM;
332
+ case AVATAR_SIZES.LARGE:
333
+ return BADGE_SIZES.MEDIUM;
334
+ case AVATAR_SIZES.X_LARGE:
335
+ default:
336
+ return BADGE_SIZES.MEDIUM;
337
+ }
338
+ });
339
+
340
+ return { activityStatusColor, activityStatusSize };
341
+ }
178
342
  </script>
@@ -0,0 +1,29 @@
1
+ import { Value } from '../../utils/type.utils';
2
+
3
+ export const BADGE_SIZES = {
4
+ X_SMALL: 'x-small',
5
+ SMALL: 'small',
6
+ MEDIUM: 'medium',
7
+ LARGE: 'large',
8
+ X_LARGE: 'x-large',
9
+ } as const;
10
+
11
+ export type BadgeSize = Value<typeof BADGE_SIZES>;
12
+
13
+ export const BADGE_COLORS = {
14
+ PRIMARY: 'primary',
15
+ SUCCESS: 'success',
16
+ DANGER: 'danger',
17
+ FAIL: 'fail',
18
+ NEUTRAL: 'neutral',
19
+ } as const;
20
+
21
+ export type BadgeColor = Value<typeof BADGE_COLORS>;
22
+
23
+ export const BADGE_ELEVATIONS = {
24
+ NONE: 'none',
25
+ X_SMALL: 'x-small',
26
+ SMALL: 'small',
27
+ } as const;
28
+
29
+ export type BadgeElevation = Value<typeof BADGE_ELEVATIONS>;
@@ -0,0 +1,168 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import Badge from './Badge.vue';
3
+ import { BADGE_COLORS, BADGE_ELEVATIONS, BADGE_SIZES } from './Badge.consts';
4
+ import { ICONS } from '../Icons/Icon';
5
+
6
+ describe('Badge', () => {
7
+ it('should render with label', () => {
8
+ const wrapper = mount(Badge, {
9
+ props: {
10
+ label: '1',
11
+ },
12
+ });
13
+
14
+ expect(wrapper.find('.ds-badge__content').text()).toBe('1');
15
+ });
16
+
17
+ it('should render with icon', () => {
18
+ const wrapper = mount(Badge, {
19
+ props: {
20
+ icon: ICONS.FA_BELL,
21
+ },
22
+ });
23
+
24
+ expect(wrapper.find('.ds-icon .fa-bell').exists()).toBe(true);
25
+ });
26
+
27
+ it('does not render label if icon is present', () => {
28
+ const wrapper = mount(Badge, {
29
+ props: {
30
+ label: '1',
31
+ icon: ICONS.FA_BELL,
32
+ },
33
+ });
34
+
35
+ expect(wrapper.find('.ds-badge__content').exists()).toBe(false);
36
+ });
37
+
38
+ it('should render image', () => {
39
+ const imageUrl = 'https://via.placeholder.com/150';
40
+ const wrapper = mount(Badge, {
41
+ props: {
42
+ imageUrl,
43
+ },
44
+ });
45
+
46
+ expect(wrapper.find('.ds-badge__image').exists()).toBe(true);
47
+ expect(wrapper.find('.ds-badge__image').attributes('src')).toBe(imageUrl);
48
+ });
49
+
50
+ it('should not render icon if image is present', () => {
51
+ const wrapper = mount(Badge, {
52
+ props: {
53
+ icon: ICONS.FA_BELL,
54
+ imageUrl: 'https://via.placeholder.com/150',
55
+ },
56
+ });
57
+
58
+ expect(wrapper.find('.ds-badge__image').exists()).toBe(true);
59
+ expect(wrapper.find('.ds-icon').exists()).toBe(false);
60
+ });
61
+
62
+ it('should not render label if image is present', () => {
63
+ const wrapper = mount(Badge, {
64
+ props: {
65
+ label: '1',
66
+ imageUrl: 'https://via.placeholder.com/150',
67
+ },
68
+ });
69
+
70
+ expect(wrapper.find('.ds-badge__image').exists()).toBe(true);
71
+ expect(wrapper.find('.ds-badge__content').exists()).toBe(false);
72
+ });
73
+
74
+ it.each([
75
+ {
76
+ size: BADGE_SIZES.X_LARGE,
77
+ color: undefined,
78
+ expectedClassName: '-ds-x-large',
79
+ },
80
+ {
81
+ size: BADGE_SIZES.LARGE,
82
+ color: undefined,
83
+ expectedClassName: '-ds-large',
84
+ },
85
+ {
86
+ size: BADGE_SIZES.MEDIUM,
87
+ color: undefined,
88
+ expectedClassName: '-ds-medium',
89
+ },
90
+ {
91
+ size: BADGE_SIZES.SMALL,
92
+ color: undefined,
93
+ expectedClassName: '-ds-small',
94
+ },
95
+ {
96
+ size: BADGE_SIZES.X_SMALL,
97
+ color: undefined,
98
+ expectedClassName: '-ds-x-small',
99
+ },
100
+ {
101
+ size: undefined,
102
+ color: BADGE_COLORS.PRIMARY,
103
+ expectedClassName: '-ds-color-primary',
104
+ },
105
+ {
106
+ size: undefined,
107
+ color: BADGE_COLORS.SUCCESS,
108
+ expectedClassName: '-ds-color-success',
109
+ },
110
+ {
111
+ size: undefined,
112
+ color: BADGE_COLORS.DANGER,
113
+ expectedClassName: '-ds-color-danger',
114
+ },
115
+ {
116
+ size: undefined,
117
+ color: BADGE_COLORS.FAIL,
118
+ expectedClassName: '-ds-color-fail',
119
+ },
120
+ {
121
+ size: undefined,
122
+ color: BADGE_COLORS.NEUTRAL,
123
+ expectedClassName: '-ds-color-neutral',
124
+ },
125
+ ])(
126
+ 'should have valid class names for size: $size, color: $color',
127
+ ({ size, color, expectedClassName }) => {
128
+ const wrapper = mount(Badge, {
129
+ props: {
130
+ size,
131
+ color,
132
+ },
133
+ });
134
+
135
+ expect(wrapper.find('.ds-badge').classes()).toContain(expectedClassName);
136
+ },
137
+ );
138
+
139
+ it('should have -ds-no-elevation class when elevation is none', () => {
140
+ const wrapper = mount(Badge, {
141
+ props: {
142
+ elevation: BADGE_ELEVATIONS.NONE,
143
+ },
144
+ });
145
+
146
+ expect(wrapper.find('.ds-badge__elevation').classes()).toContain('-ds-no-elevation');
147
+ });
148
+
149
+ it('should have -ds-elevation-s class when elevation is small', () => {
150
+ const wrapper = mount(Badge, {
151
+ props: {
152
+ elevation: BADGE_ELEVATIONS.SMALL,
153
+ },
154
+ });
155
+
156
+ expect(wrapper.find('.ds-badge__elevation').classes()).toContain('-ds-elevation-s');
157
+ });
158
+
159
+ it('should not have elevation modifier class when elevation is x-small', () => {
160
+ const wrapper = mount(Badge, {
161
+ props: {
162
+ elevation: BADGE_ELEVATIONS.X_SMALL,
163
+ },
164
+ });
165
+
166
+ expect(wrapper.find('.ds-badge__elevation').classes()).toEqual(['ds-badge__elevation']);
167
+ });
168
+ });
@@ -0,0 +1,99 @@
1
+ import { ComponentProps } from 'vue-component-type-helpers';
2
+ import { Meta, StoryObj } from '@storybook/vue3';
3
+
4
+ import Badge from './Badge.vue';
5
+ import { BADGE_COLORS, BADGE_ELEVATIONS, BADGE_SIZES } from './Badge.consts';
6
+ import { IconKey, ICONS } from '../Icons/Icon';
7
+ import DsBanner, { BANNER_COLORS } from '../Banner';
8
+
9
+ type BadgeProps = ComponentProps<typeof Badge>;
10
+
11
+ function wrapWithContainer(template: string): string {
12
+ // line-height: 0; is to remove extra space below the badge (as it's an inline element)
13
+ return `<div style="display: inline-flex; background: #ccc; padding: 16px; line-height: 0; margin-bottom: 16px;">${template}</div>
14
+ <ds-banner :color="BANNER_COLORS.WARNING" title="Taka kombinacja jest niezgodna z design systemem!" v-if="invalidUsage" />
15
+ `;
16
+ }
17
+
18
+ const meta: Meta<typeof Badge> = {
19
+ title: 'Components/Badges/Badge',
20
+ component: Badge,
21
+ render: (args: BadgeProps) => ({
22
+ components: { Badge, DsBanner },
23
+ setup() {
24
+ return {
25
+ BANNER_COLORS,
26
+ };
27
+ },
28
+ computed: {
29
+ invalidUsage() {
30
+ const invalidSizeWithLabel =
31
+ (args.size === BADGE_SIZES.X_SMALL || args.size === BADGE_SIZES.SMALL) &&
32
+ args.label;
33
+
34
+ const invalidSizeWithIconOrImage =
35
+ args.size === BADGE_SIZES.X_SMALL && (args.icon || args.imageUrl);
36
+
37
+ return invalidSizeWithLabel || invalidSizeWithIconOrImage;
38
+ },
39
+ props() {
40
+ return {
41
+ ...args,
42
+ icon: ICONS[args.icon as IconKey],
43
+ };
44
+ },
45
+ },
46
+ // line-height: 0; is to remove extra space below the badge (as it's an inline element)
47
+ template: wrapWithContainer('<Badge v-bind="props" />'),
48
+ }),
49
+ argTypes: {
50
+ color: {
51
+ control: 'select',
52
+ options: Object.values(BADGE_COLORS),
53
+ },
54
+ size: {
55
+ control: 'select',
56
+ options: Object.values(BADGE_SIZES),
57
+ },
58
+ icon: {
59
+ control: 'select',
60
+ options: [null, ...Object.keys(ICONS)],
61
+ },
62
+ elevation: {
63
+ control: 'select',
64
+ options: Object.values(BADGE_ELEVATIONS),
65
+ },
66
+ },
67
+ };
68
+ export default meta;
69
+
70
+ type Story = StoryObj<typeof Badge>;
71
+
72
+ export const Interactive: Story = {
73
+ args: {
74
+ color: BADGE_COLORS.PRIMARY,
75
+ size: BADGE_SIZES.SMALL,
76
+ label: '',
77
+ icon: undefined,
78
+ imageUrl: '',
79
+ elevation: BADGE_ELEVATIONS.X_SMALL,
80
+ },
81
+ };
82
+
83
+ Interactive.parameters = {
84
+ design: {
85
+ type: 'figma',
86
+ url: 'https://www.figma.com/design/izQdYyiBR1GQgFkaOIfIJI/LMS---DS-Components?node-id=12364-10601&m=dev',
87
+ },
88
+ };
89
+
90
+ export const InteractiveWithImage: Story = {
91
+ args: {
92
+ color: BADGE_COLORS.PRIMARY,
93
+ size: BADGE_SIZES.SMALL,
94
+ label: '',
95
+ icon: undefined,
96
+ imageUrl: 'https://lek.wiecejnizlek.pl/images/lek/logo-badge.svg',
97
+ elevation: BADGE_ELEVATIONS.X_SMALL,
98
+ },
99
+ };