@drivenets/design-system 0.6.1 → 0.7.1

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 (138) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/index.cjs +12297 -8255
  3. package/dist/index.d.cts +817 -118
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +817 -118
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +12280 -8259
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +25 -23
  11. package/src/components/ds-autocomplete/ds-autocomplete.module.scss +2 -1
  12. package/src/components/ds-autocomplete/ds-autocomplete.tsx +47 -40
  13. package/src/components/ds-autocomplete/ds-autocomplete.types.tsx +37 -14
  14. package/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +3 -7
  15. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.module.scss +62 -0
  16. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.tsx +83 -0
  17. package/src/components/ds-comment-bubble/components/ds-comment-thread/ds-comment-thread.types.ts +12 -0
  18. package/src/components/ds-comment-bubble/components/ds-comment-thread/index.ts +2 -0
  19. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.module.scss +149 -0
  20. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx +147 -0
  21. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.types.ts +15 -0
  22. package/src/components/ds-comment-bubble/components/ds-thread-item/index.ts +2 -0
  23. package/src/components/ds-comment-bubble/ds-comment-bubble.module.scss +298 -0
  24. package/src/components/ds-comment-bubble/ds-comment-bubble.tsx +241 -0
  25. package/src/components/ds-comment-bubble/ds-comment-bubble.types.ts +27 -0
  26. package/src/components/ds-comment-bubble/hooks/index.ts +3 -0
  27. package/src/components/ds-comment-bubble/hooks/use-auto-resize.ts +17 -0
  28. package/src/components/ds-comment-bubble/hooks/use-click-outside.ts +22 -0
  29. package/src/components/ds-comment-bubble/hooks/use-scroll-overflow.ts +27 -0
  30. package/src/components/ds-comment-bubble/index.ts +6 -0
  31. package/src/components/ds-comment-card/ds-comment-card.module.scss +162 -0
  32. package/src/components/ds-comment-card/ds-comment-card.tsx +171 -0
  33. package/src/components/ds-comment-card/ds-comment-card.types.ts +45 -0
  34. package/src/components/ds-comment-card/index.ts +2 -0
  35. package/src/components/ds-comment-indicator/ds-comment-indicator.module.scss +65 -0
  36. package/src/components/ds-comment-indicator/ds-comment-indicator.tsx +48 -0
  37. package/src/components/ds-comment-indicator/ds-comment-indicator.types.ts +13 -0
  38. package/src/components/ds-comment-indicator/index.ts +2 -0
  39. package/src/components/ds-comments-drawer/comments-filters.test.ts +483 -0
  40. package/src/components/ds-comments-drawer/comments-filters.ts +134 -0
  41. package/src/components/ds-comments-drawer/comments-filters.types.ts +14 -0
  42. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.module.scss +92 -0
  43. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.tsx +219 -0
  44. package/src/components/ds-comments-drawer/components/comments-filter-modal/comments-filter-modal.types.ts +12 -0
  45. package/src/components/ds-comments-drawer/components/comments-filter-modal/index.ts +2 -0
  46. package/src/components/ds-comments-drawer/ds-comments-drawer.module.scss +67 -0
  47. package/src/components/ds-comments-drawer/ds-comments-drawer.tsx +215 -0
  48. package/src/components/ds-comments-drawer/ds-comments-drawer.types.ts +23 -0
  49. package/src/components/ds-comments-drawer/index.ts +3 -0
  50. package/src/components/ds-date-input/ds-date-input.tsx +3 -1
  51. package/src/components/ds-date-input/ds-date-input.types.tsx +6 -0
  52. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -0
  53. package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +8 -4
  54. package/src/components/ds-file-upload/components/file-upload/file-upload.tsx +2 -1
  55. package/src/components/ds-file-upload/components/file-upload/file-upload.types.tsx +5 -0
  56. package/src/components/ds-file-upload/types/accept-types.ts +0 -1
  57. package/src/components/ds-filter-status-icon/ds-filter-status-icon.module.scss +19 -0
  58. package/src/components/ds-filter-status-icon/ds-filter-status-icon.tsx +35 -0
  59. package/src/components/ds-filter-status-icon/ds-filter-status-icon.types.ts +31 -0
  60. package/src/components/ds-filter-status-icon/index.ts +3 -0
  61. package/src/components/ds-icon/custom-icons/index.ts +10 -0
  62. package/src/components/ds-icon/custom-icons/special-failed.tsx +16 -0
  63. package/src/components/ds-icon/custom-icons/special-paused.tsx +16 -0
  64. package/src/components/ds-icon/custom-icons/special-running.tsx +16 -0
  65. package/src/components/ds-icon/custom-icons/special-warning.tsx +16 -0
  66. package/src/components/ds-panel/ds-panel.module.scss +3 -3
  67. package/src/components/ds-progress-arc/ds-progress-arc.module.scss +57 -0
  68. package/src/components/ds-progress-arc/ds-progress-arc.tsx +111 -0
  69. package/src/components/ds-progress-arc/ds-progress-arc.types.ts +34 -0
  70. package/src/components/ds-progress-arc/index.ts +3 -0
  71. package/src/components/ds-progress-arc/utils.test.ts +79 -0
  72. package/src/components/ds-progress-arc/utils.ts +61 -0
  73. package/src/components/ds-progress-donut/ds-progress-donut.module.scss +67 -0
  74. package/src/components/ds-progress-donut/ds-progress-donut.tsx +83 -0
  75. package/src/components/ds-progress-donut/ds-progress-donut.types.ts +38 -0
  76. package/src/components/ds-progress-donut/index.ts +2 -0
  77. package/src/components/ds-progress-donut/utils.ts +1 -0
  78. package/src/components/ds-progress-linear/ds-progress-linear.module.scss +100 -0
  79. package/src/components/ds-progress-linear/ds-progress-linear.tsx +107 -0
  80. package/src/components/ds-progress-linear/ds-progress-linear.types.ts +72 -0
  81. package/src/components/ds-progress-linear/index.ts +2 -0
  82. package/src/components/ds-progress-linear/utils.test.ts +70 -0
  83. package/src/components/ds-progress-linear/utils.ts +7 -0
  84. package/src/components/ds-radio-group/ds-radio-group.tsx +2 -0
  85. package/src/components/ds-select/ds-select.tsx +2 -1
  86. package/src/components/ds-select/ds-select.types.tsx +7 -0
  87. package/src/components/ds-stepper/components/ds-step.tsx +1 -1
  88. package/src/components/ds-stepper/ds-stepper.module.scss +16 -2
  89. package/src/components/ds-stepper/ds-stepper.tsx +3 -1
  90. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +9 -0
  91. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +137 -0
  92. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +22 -0
  93. package/src/components/ds-table/components/ds-table-body-virtualized/index.ts +2 -0
  94. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +3 -1
  95. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +7 -10
  96. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +3 -4
  97. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +13 -53
  98. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +32 -85
  99. package/src/components/ds-table/components/ds-table-row/ds-table-row.types.ts +0 -6
  100. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.module.scss +10 -0
  101. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx +45 -0
  102. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.types.ts +7 -0
  103. package/src/components/ds-table/components/ds-table-row-expandable-cell/index.ts +2 -0
  104. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.module.scss +6 -0
  105. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.tsx +30 -0
  106. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.types.ts +6 -0
  107. package/src/components/ds-table/components/ds-table-row-selectable-cell/index.ts +2 -0
  108. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +80 -0
  109. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +95 -0
  110. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +11 -0
  111. package/src/components/ds-table/components/ds-table-row-virtualized/index.ts +2 -0
  112. package/src/components/ds-table/context/ds-table-context.tsx +0 -9
  113. package/src/components/ds-table/ds-table.module.scss +4 -17
  114. package/src/components/ds-table/ds-table.tsx +44 -77
  115. package/src/components/ds-table/ds-table.types.ts +57 -12
  116. package/src/components/ds-table/index.ts +1 -0
  117. package/src/components/ds-table/styles/_mixins.scss +15 -0
  118. package/src/components/ds-table/styles/_variables.scss +2 -2
  119. package/src/components/ds-table/utils/{column-styling.ts → column-size.ts} +4 -14
  120. package/src/components/ds-table/utils/constants.ts +4 -0
  121. package/src/components/ds-tag/index.tsx +1 -0
  122. package/src/components/ds-tag-filter/ds-tag-filter.module.scss +13 -15
  123. package/src/components/ds-tag-filter/ds-tag-filter.tsx +38 -72
  124. package/src/components/ds-tag-filter/ds-tag-filter.types.ts +2 -3
  125. package/src/components/ds-tag-filter/hooks/use-tag-overflow-calculation.ts +12 -62
  126. package/src/components/ds-tag-filter/utils.test.ts +2 -108
  127. package/src/components/ds-tag-filter/utils.ts +2 -12
  128. package/src/components/ds-tree/ds-tree.module.scss +283 -0
  129. package/src/components/ds-tree/ds-tree.tsx +172 -0
  130. package/src/components/ds-tree/ds-tree.types.ts +83 -0
  131. package/src/components/ds-tree/ds-tree.utils.ts +11 -0
  132. package/src/components/ds-tree/index.ts +3 -0
  133. package/src/index.ts +9 -0
  134. package/src/utils/format-relative-time.test.ts +74 -0
  135. package/src/utils/format-relative-time.ts +52 -0
  136. package/src/utils/numbers.test.ts +24 -0
  137. package/src/utils/numbers.ts +1 -0
  138. package/src/components/ds-table/utils/create-columns-grid-template.ts +0 -23
@@ -0,0 +1,67 @@
1
+ $transition-duration: 0.2s;
2
+
3
+ .root {
4
+ display: inline-grid;
5
+ }
6
+
7
+ .small {
8
+ --size: 80px;
9
+ --thickness: 4px;
10
+ }
11
+
12
+ .medium {
13
+ --size: 120px;
14
+ --thickness: 6px;
15
+ }
16
+
17
+ .circle {
18
+ grid-area: 1 / 1;
19
+ display: block;
20
+ }
21
+
22
+ .track {
23
+ stroke: var(--color-background-tertiary);
24
+ transition: stroke $transition-duration;
25
+ }
26
+
27
+ .range {
28
+ transition: stroke-dashoffset $transition-duration;
29
+ stroke-linecap: round;
30
+ }
31
+
32
+ .default .range {
33
+ stroke: var(--color-background-selected);
34
+ }
35
+
36
+ .success .track,
37
+ .success .range {
38
+ stroke: var(--color-background-success-strong);
39
+ }
40
+
41
+ .error .range {
42
+ stroke: var(--color-background-danger-strong);
43
+ }
44
+
45
+ .center {
46
+ grid-area: 1 / 1;
47
+ display: flex;
48
+ align-items: center;
49
+ justify-content: center;
50
+ color: var(--color-font-main);
51
+ }
52
+
53
+ .iconSuccess {
54
+ color: var(--color-icon-success);
55
+ }
56
+
57
+ .iconError {
58
+ color: var(--color-icon-danger);
59
+ }
60
+
61
+ $icon-medium-size: 44px;
62
+
63
+ .iconMediumSize.iconMediumSize {
64
+ font-size: $icon-medium-size;
65
+ width: 1em;
66
+ height: 1em;
67
+ }
@@ -0,0 +1,83 @@
1
+ import type React from 'react';
2
+ import { Progress } from '@ark-ui/react';
3
+ import classNames from 'classnames';
4
+
5
+ import { DsIcon } from '../ds-icon';
6
+ import { DsTypography } from '../ds-typography';
7
+ import styles from './ds-progress-donut.module.scss';
8
+ import type {
9
+ DsProgressDonutProps,
10
+ ProgressDonutSize,
11
+ ProgressDonutVariant,
12
+ } from './ds-progress-donut.types';
13
+ import { clampValue } from './utils';
14
+
15
+ const sizeStyleMap: Record<ProgressDonutSize, string> = Object.freeze({
16
+ small: styles.small,
17
+ medium: styles.medium,
18
+ });
19
+
20
+ const variantStyleMap: Record<ProgressDonutVariant, string> = Object.freeze({
21
+ default: styles.default,
22
+ success: styles.success,
23
+ error: styles.error,
24
+ });
25
+
26
+ const DsProgressDonut = ({
27
+ value = 0,
28
+ size = 'medium',
29
+ variant = 'default',
30
+ children,
31
+ className,
32
+ style,
33
+ ref,
34
+ }: DsProgressDonutProps) => {
35
+ const effectiveValue = variant === 'success' ? 100 : clampValue(value);
36
+
37
+ const renderIcon = (iconName: 'check' | 'close', colorClass: string) => (
38
+ <DsIcon
39
+ variant="rounded"
40
+ icon={iconName}
41
+ size="large"
42
+ className={classNames({ [styles.iconMediumSize]: size === 'medium' }, colorClass)}
43
+ />
44
+ );
45
+
46
+ const renderCenter = () => {
47
+ if (children) {
48
+ return children;
49
+ }
50
+
51
+ if (variant === 'success') {
52
+ return renderIcon('check', styles.iconSuccess);
53
+ }
54
+
55
+ if (variant === 'error') {
56
+ return renderIcon('close', styles.iconError);
57
+ }
58
+
59
+ return (
60
+ <DsTypography variant={size === 'medium' ? 'heading3' : 'body-md-md'} asChild>
61
+ <span>{effectiveValue}%</span>
62
+ </DsTypography>
63
+ );
64
+ };
65
+
66
+ return (
67
+ <Progress.Root
68
+ ref={ref as React.Ref<HTMLDivElement>}
69
+ value={effectiveValue}
70
+ className={classNames(styles.root, sizeStyleMap[size], variantStyleMap[variant], className)}
71
+ style={style}
72
+ >
73
+ <Progress.Circle className={styles.circle}>
74
+ <Progress.CircleTrack className={styles.track} />
75
+ <Progress.CircleRange className={styles.range} />
76
+ </Progress.Circle>
77
+
78
+ <div className={styles.center}>{renderCenter()}</div>
79
+ </Progress.Root>
80
+ );
81
+ };
82
+
83
+ export default DsProgressDonut;
@@ -0,0 +1,38 @@
1
+ import type { CSSProperties, ReactNode, Ref } from 'react';
2
+
3
+ export const progressDonutSizes = ['small', 'medium'] as const;
4
+ export type ProgressDonutSize = (typeof progressDonutSizes)[number];
5
+
6
+ export const progressDonutVariants = ['default', 'success', 'error'] as const;
7
+ export type ProgressDonutVariant = (typeof progressDonutVariants)[number];
8
+
9
+ export interface DsProgressDonutProps {
10
+ /**
11
+ * Progress value between 0 and 100
12
+ * @default 0
13
+ */
14
+ value?: number;
15
+
16
+ /**
17
+ * Size of the donut progress indicator
18
+ * @default 'medium'
19
+ */
20
+ size?: ProgressDonutSize;
21
+
22
+ /**
23
+ * Visual variant of the donut progress indicator
24
+ * @default 'default'
25
+ */
26
+ variant?: ProgressDonutVariant;
27
+
28
+ /**
29
+ * Custom content to display in the center of the donut, overriding the default text or icon
30
+ */
31
+ children?: ReactNode;
32
+
33
+ className?: string;
34
+
35
+ style?: CSSProperties;
36
+
37
+ ref?: Ref<HTMLElement>;
38
+ }
@@ -0,0 +1,2 @@
1
+ export { default as DsProgressDonut } from './ds-progress-donut';
2
+ export * from './ds-progress-donut.types';
@@ -0,0 +1 @@
1
+ export { clampValue } from '../../utils/numbers';
@@ -0,0 +1,100 @@
1
+ @use '../../styles/typography';
2
+
3
+ $border-radius: 4px;
4
+
5
+ .root {
6
+ display: flex;
7
+ flex-direction: column;
8
+ width: 100%;
9
+
10
+ &.sizeSmall {
11
+ gap: var(--spacing-4xs);
12
+ }
13
+
14
+ &.sizeMedium,
15
+ &.sizeLarge {
16
+ gap: var(--spacing-3xs);
17
+ }
18
+ }
19
+
20
+ .header {
21
+ display: flex;
22
+ align-items: flex-start;
23
+ justify-content: space-between;
24
+ }
25
+
26
+ .label {
27
+ @include typography.body-sm-md;
28
+ color: var(--color-font-main);
29
+ }
30
+
31
+ .valueText {
32
+ @include typography.body-sm-reg;
33
+ color: var(--color-font-main);
34
+ flex-shrink: 0;
35
+ }
36
+
37
+ .track {
38
+ width: 100%;
39
+ background-color: var(--color-background-tertiary);
40
+ border-radius: $border-radius;
41
+ overflow: hidden;
42
+ }
43
+
44
+ .trackSmall {
45
+ height: 2px;
46
+ }
47
+
48
+ .trackMedium {
49
+ height: 4px;
50
+ }
51
+
52
+ .trackLarge {
53
+ height: 8px;
54
+ }
55
+
56
+ .range {
57
+ height: 100%;
58
+ border-radius: $border-radius;
59
+ transition: width 0.2s ease;
60
+ }
61
+
62
+ .variantProgress {
63
+ background-color: var(--color-background-selected);
64
+ }
65
+
66
+ .variantInterrupted {
67
+ background-color: var(--color-background-warning-strong);
68
+ }
69
+
70
+ .variantSuccess {
71
+ background-color: var(--color-background-success-strong);
72
+ }
73
+
74
+ .variantError {
75
+ background-color: var(--color-background-danger-strong);
76
+ }
77
+
78
+ .caption {
79
+ @include typography.body-xs-reg;
80
+ display: flex;
81
+ align-items: center;
82
+ gap: var(--spacing-3xs);
83
+ padding-top: var(--spacing-4xs);
84
+ }
85
+
86
+ .captionDefault {
87
+ color: var(--color-font-secondary);
88
+ }
89
+
90
+ .captionSuccess {
91
+ color: var(--color-background-success-strong);
92
+ }
93
+
94
+ .captionError {
95
+ color: var(--color-background-danger-strong);
96
+ }
97
+
98
+ .captionInterrupted {
99
+ color: var(--color-font-warning);
100
+ }
@@ -0,0 +1,107 @@
1
+ import { Progress } from '@ark-ui/react';
2
+ import classNames from 'classnames';
3
+
4
+ import { DsIcon, type IconName } from '../ds-icon';
5
+
6
+ import styles from './ds-progress-linear.module.scss';
7
+ import type { DsProgressLinearProps, ProgressLinearVariant } from './ds-progress-linear.types';
8
+ import { calculatePercentage } from './utils';
9
+
10
+ const rangeVariantClass: Record<ProgressLinearVariant, string | undefined> = Object.freeze({
11
+ initial: undefined,
12
+ progress: styles.variantProgress,
13
+ interrupted: styles.variantInterrupted,
14
+ success: styles.variantSuccess,
15
+ error: styles.variantError,
16
+ });
17
+
18
+ const captionVariantClass: Record<ProgressLinearVariant, string> = Object.freeze({
19
+ initial: styles.captionDefault,
20
+ progress: styles.captionDefault,
21
+ interrupted: styles.captionInterrupted,
22
+ success: styles.captionSuccess,
23
+ error: styles.captionError,
24
+ });
25
+
26
+ const captionIcon: Partial<Record<ProgressLinearVariant, IconName>> = Object.freeze({
27
+ success: 'check_circle',
28
+ error: 'cancel',
29
+ interrupted: 'warning',
30
+ });
31
+
32
+ const sizeClass: Record<string, string> = Object.freeze({
33
+ small: styles.sizeSmall,
34
+ medium: styles.sizeMedium,
35
+ large: styles.sizeLarge,
36
+ });
37
+
38
+ const trackSizeClass: Record<string, string> = Object.freeze({
39
+ small: styles.trackSmall,
40
+ medium: styles.trackMedium,
41
+ large: styles.trackLarge,
42
+ });
43
+
44
+ /**
45
+ * Design system ProgressLinear component
46
+ */
47
+ const DsProgressLinear = ({
48
+ value = 0,
49
+ min = 0,
50
+ max = 100,
51
+ variant = 'progress',
52
+ size = 'medium',
53
+ label,
54
+ showValue = true,
55
+ caption,
56
+ className,
57
+ style,
58
+ ref,
59
+ }: DsProgressLinearProps) => {
60
+ const hasHeader = label !== undefined || showValue;
61
+ const icon = captionIcon[variant];
62
+ const percentage = calculatePercentage(value, min, max);
63
+
64
+ const renderCaption = (caption?: DsProgressLinearProps['caption']) => {
65
+ if (caption === undefined) {
66
+ return null;
67
+ }
68
+
69
+ if (typeof caption !== 'string') {
70
+ return caption;
71
+ }
72
+
73
+ return (
74
+ <div className={classNames(styles.caption, captionVariantClass[variant])}>
75
+ {icon && <DsIcon icon={icon} size="tiny" filled />}
76
+ <span>{caption}</span>
77
+ </div>
78
+ );
79
+ };
80
+
81
+ return (
82
+ <Progress.Root
83
+ ref={ref}
84
+ value={value}
85
+ min={min}
86
+ max={max}
87
+ className={classNames(styles.root, sizeClass[size], className)}
88
+ style={style}
89
+ >
90
+ {hasHeader && (
91
+ <div className={styles.header}>
92
+ {label && <Progress.Label className={styles.label}>{label}</Progress.Label>}
93
+
94
+ {showValue && <Progress.ValueText className={styles.valueText}>{percentage}%</Progress.ValueText>}
95
+ </div>
96
+ )}
97
+
98
+ <Progress.Track className={classNames(styles.track, trackSizeClass[size])}>
99
+ <Progress.Range className={classNames(styles.range, rangeVariantClass[variant])} />
100
+ </Progress.Track>
101
+
102
+ {renderCaption(caption)}
103
+ </Progress.Root>
104
+ );
105
+ };
106
+
107
+ export default DsProgressLinear;
@@ -0,0 +1,72 @@
1
+ import type React from 'react';
2
+
3
+ export const progressLinearVariants = ['initial', 'progress', 'interrupted', 'success', 'error'] as const;
4
+ export type ProgressLinearVariant = (typeof progressLinearVariants)[number];
5
+
6
+ export const progressLinearSizes = ['small', 'medium', 'large'] as const;
7
+ export type ProgressLinearSize = (typeof progressLinearSizes)[number];
8
+
9
+ export interface DsProgressLinearProps {
10
+ /**
11
+ * The current progress value (0–100)
12
+ * @default 0
13
+ */
14
+ value?: number;
15
+
16
+ /**
17
+ * The minimum value
18
+ * @default 0
19
+ */
20
+ min?: number;
21
+
22
+ /**
23
+ * The maximum value
24
+ * @default 100
25
+ */
26
+ max?: number;
27
+
28
+ /**
29
+ * The visual variant controlling bar color and caption styling
30
+ * @default 'progress'
31
+ */
32
+ variant?: ProgressLinearVariant;
33
+
34
+ /**
35
+ * The size of the progress bar track
36
+ * @default 'medium'
37
+ */
38
+ size?: ProgressLinearSize;
39
+
40
+ /**
41
+ * Label displayed above the progress bar (left side)
42
+ */
43
+ label?: React.ReactNode;
44
+
45
+ /**
46
+ * Whether to show the percentage value text (right side of label row)
47
+ * @default true
48
+ */
49
+ showValue?: boolean;
50
+
51
+ /**
52
+ * Caption/helper text below the progress bar.
53
+ * When a string is provided, it is auto-styled with an icon and color based on the variant.
54
+ * When a ReactNode is provided, it is rendered as-is.
55
+ */
56
+ caption?: React.ReactNode | string;
57
+
58
+ /**
59
+ * Additional CSS class names
60
+ */
61
+ className?: string;
62
+
63
+ /**
64
+ * Additional styles to apply to the component
65
+ */
66
+ style?: React.CSSProperties;
67
+
68
+ /**
69
+ * Ref to the root element
70
+ */
71
+ ref?: React.Ref<HTMLDivElement>;
72
+ }
@@ -0,0 +1,2 @@
1
+ export { default as DsProgressLinear } from './ds-progress-linear';
2
+ export * from './ds-progress-linear.types';
@@ -0,0 +1,70 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { calculatePercentage } from './utils';
4
+
5
+ describe('calculatePercentage', () => {
6
+ it('should return 0 when max equals min', () => {
7
+ expect(calculatePercentage(50, 100, 100)).toBe(0);
8
+ });
9
+
10
+ it('should return 0 when max is less than min', () => {
11
+ expect(calculatePercentage(50, 100, 50)).toBe(0);
12
+ });
13
+
14
+ it('should calculate 0% for value at minimum', () => {
15
+ expect(calculatePercentage(0, 0, 100)).toBe(0);
16
+ });
17
+
18
+ it('should calculate 100% for value at maximum', () => {
19
+ expect(calculatePercentage(100, 0, 100)).toBe(100);
20
+ });
21
+
22
+ it('should calculate 50% for value at midpoint', () => {
23
+ expect(calculatePercentage(50, 0, 100)).toBe(50);
24
+ });
25
+
26
+ it('should round to nearest integer for fractional percentages', () => {
27
+ expect(calculatePercentage(33, 0, 100)).toBe(33);
28
+ expect(calculatePercentage(66, 0, 100)).toBe(66);
29
+ expect(calculatePercentage(67, 0, 100)).toBe(67);
30
+ });
31
+
32
+ it('should handle non-zero minimum values', () => {
33
+ expect(calculatePercentage(50, 25, 75)).toBe(50);
34
+ expect(calculatePercentage(25, 25, 75)).toBe(0);
35
+ expect(calculatePercentage(75, 25, 75)).toBe(100);
36
+ });
37
+
38
+ it('should handle negative values', () => {
39
+ expect(calculatePercentage(-50, -100, 0)).toBe(50);
40
+ expect(calculatePercentage(-100, -100, 0)).toBe(0);
41
+ expect(calculatePercentage(0, -100, 0)).toBe(100);
42
+ });
43
+
44
+ it('should handle decimal values and round correctly', () => {
45
+ expect(calculatePercentage(33.3, 0, 100)).toBe(33);
46
+ expect(calculatePercentage(33.5, 0, 100)).toBe(34);
47
+ expect(calculatePercentage(33.7, 0, 100)).toBe(34);
48
+ });
49
+
50
+ it('should handle value below minimum (clamping not enforced)', () => {
51
+ // Component doesn't clamp values, so negative percentages are possible
52
+ expect(calculatePercentage(-10, 0, 100)).toBe(-10);
53
+ });
54
+
55
+ it('should handle value above maximum (clamping not enforced)', () => {
56
+ // Component doesn't clamp values, so > 100% is possible
57
+ expect(calculatePercentage(150, 0, 100)).toBe(150);
58
+ });
59
+
60
+ it('should handle large numbers', () => {
61
+ expect(calculatePercentage(5000, 0, 10000)).toBe(50);
62
+ expect(calculatePercentage(1000000, 0, 2000000)).toBe(50);
63
+ });
64
+
65
+ it('should handle small decimal ranges', () => {
66
+ expect(calculatePercentage(0.5, 0, 1)).toBe(50);
67
+ expect(calculatePercentage(0.25, 0, 1)).toBe(25);
68
+ expect(calculatePercentage(0.75, 0, 1)).toBe(75);
69
+ });
70
+ });
@@ -0,0 +1,7 @@
1
+ export function calculatePercentage(value: number, min: number, max: number): number {
2
+ if (max <= min) {
3
+ return 0;
4
+ }
5
+
6
+ return Math.round(((value - min) / (max - min)) * 100);
7
+ }
@@ -64,6 +64,7 @@ const Item: React.FC<DsRadioGroupItemProps> = ({
64
64
  * Use compound component pattern instead: DsRadioGroup.Root + DsRadioGroup.Item
65
65
  * @deprecated
66
66
  */
67
+ /* c8 ignore start */
67
68
  export const DsRadioGroupLegacy: React.FC<DsRadioGroupLegacyProps> = ({
68
69
  options,
69
70
  value,
@@ -99,6 +100,7 @@ export const DsRadioGroupLegacy: React.FC<DsRadioGroupLegacyProps> = ({
99
100
  ))}
100
101
  </RadioGroupPrimitive.Root>
101
102
  );
103
+ /* c8 ignore stop */
102
104
 
103
105
  /**
104
106
  * Design system RadioGroup component
@@ -30,6 +30,7 @@ const DsSelect = ({
30
30
  className,
31
31
  placeholder = 'Click to select a value',
32
32
  disabled,
33
+ renderOption,
33
34
  ...multiselectProps
34
35
  }: DsSelectProps) => {
35
36
  const [searchTerm, setSearchTerm] = useState('');
@@ -191,7 +192,7 @@ const DsSelect = ({
191
192
  <Select.Item item={item}>
192
193
  {multiselectProps.multiple && <DsCheckbox checked={checked} />}
193
194
  {item.icon && <DsIcon className={styles.itemIcon} icon={item.icon} aria-hidden="true" />}
194
- <Select.ItemText>{item.label}</Select.ItemText>
195
+ <Select.ItemText>{renderOption ? renderOption(item) : item.label}</Select.ItemText>
195
196
  </Select.Item>
196
197
  </DsTypography>
197
198
  );
@@ -1,4 +1,5 @@
1
1
  import type React from 'react';
2
+ import type { ReactNode } from 'react';
2
3
  import type { IconName } from '../ds-icon';
3
4
 
4
5
  export type SelectSize = 'default' | 'small';
@@ -56,6 +57,12 @@ export type DsSelectProps = {
56
57
  * @default 'default'
57
58
  */
58
59
  size?: SelectSize;
60
+ /**
61
+ * Custom render function for dropdown options.
62
+ * When provided, replaces the default label text inside each dropdown item.
63
+ * The string `label` is still used for search, trigger text, chips, and accessibility.
64
+ */
65
+ renderOption?: (option: DsSelectOption) => ReactNode;
59
66
  } & (
60
67
  | {
61
68
  clearable?: undefined | false;
@@ -109,7 +109,7 @@ export function DsStep({
109
109
  <div {...context.stepsApi.getIndicatorProps({ index })} className={styles.indicatorContainer}>
110
110
  {renderIndicator()}
111
111
 
112
- {!isHorizontal && <DsStepSeparator index={index} />}
112
+ {!isHorizontal && !isSingleVariant && <DsStepSeparator index={index} />}
113
113
  </div>
114
114
 
115
115
  {children}
@@ -32,6 +32,7 @@ $actions-gap: var(--spacing-3xs);
32
32
  $drag-handle-size: 20px;
33
33
 
34
34
  $horizontal-current-min-width: 256px;
35
+ $horizontal-other-min-width: 50px;
35
36
  $horizontal-description-line-clamp: 2;
36
37
 
37
38
  .dragHandle {
@@ -46,6 +47,10 @@ $horizontal-description-line-clamp: 2;
46
47
  .root {
47
48
  background: var(--color-background);
48
49
  overflow-y: auto;
50
+
51
+ &.floating {
52
+ border-radius: inherit;
53
+ }
49
54
  }
50
55
 
51
56
  .verticalActions {
@@ -213,7 +218,7 @@ $horizontal-description-line-clamp: 2;
213
218
  .stepsContainer {
214
219
  display: flex;
215
220
  flex: 1 1 0;
216
- align-items: flex-start;
221
+ align-items: stretch;
217
222
  min-width: 0;
218
223
  overflow-x: auto;
219
224
  }
@@ -227,14 +232,23 @@ $horizontal-description-line-clamp: 2;
227
232
 
228
233
  .step {
229
234
  flex: 1 1 0;
235
+ align-self: stretch;
236
+ align-items: center;
230
237
  padding: var(--spacing-3xs) var(--spacing-xs);
231
238
  gap: var(--spacing-sm);
232
239
  border-radius: var(--spacing-2xs);
233
240
  overflow: clip;
241
+ min-width: $horizontal-other-min-width;
234
242
 
235
243
  &[data-current] {
244
+ flex-grow: 2;
245
+ max-width: none;
236
246
  min-width: $horizontal-current-min-width;
237
247
  border-radius: var(--spacing-2xs);
248
+
249
+ .stepLabel {
250
+ flex-shrink: 0;
251
+ }
238
252
  }
239
253
  }
240
254
 
@@ -266,7 +280,7 @@ $horizontal-description-line-clamp: 2;
266
280
  white-space: nowrap;
267
281
  overflow: hidden;
268
282
  text-overflow: ellipsis;
269
- flex-shrink: 0;
283
+ flex-shrink: 1;
270
284
  }
271
285
 
272
286
  .horizontalSeparator {
@@ -33,15 +33,17 @@ export function DsStepper({
33
33
  {...stepsApi.getRootProps()}
34
34
  className={classnames(
35
35
  styles.root,
36
+ 'scrollbar-thin',
36
37
  {
37
38
  [styles.variantSingle]: variant === 'single',
39
+ [styles.floating]: floating,
38
40
  },
39
41
  className,
40
42
  )}
41
43
  >
42
44
  {isHorizontal ? (
43
45
  <>
44
- <div className={styles.stepsContainer}>{children}</div>
46
+ <div className={classnames(styles.stepsContainer, 'scrollbar-thin')}>{children}</div>
45
47
 
46
48
  {actions && <div className={styles.horizontalActions}>{actions}</div>}
47
49
  </>