@drivenets/design-system 0.6.1 → 0.7.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 (134) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/index.cjs +12394 -8354
  3. package/dist/index.d.cts +806 -118
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +806 -118
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +12376 -8357
  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-dropdown-menu/ds-dropdown-menu.tsx +2 -0
  51. package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +8 -4
  52. package/src/components/ds-file-upload/types/accept-types.ts +0 -1
  53. package/src/components/ds-filter-status-icon/ds-filter-status-icon.module.scss +19 -0
  54. package/src/components/ds-filter-status-icon/ds-filter-status-icon.tsx +35 -0
  55. package/src/components/ds-filter-status-icon/ds-filter-status-icon.types.ts +31 -0
  56. package/src/components/ds-filter-status-icon/index.ts +3 -0
  57. package/src/components/ds-icon/custom-icons/index.ts +10 -0
  58. package/src/components/ds-icon/custom-icons/special-failed.tsx +16 -0
  59. package/src/components/ds-icon/custom-icons/special-paused.tsx +16 -0
  60. package/src/components/ds-icon/custom-icons/special-running.tsx +16 -0
  61. package/src/components/ds-icon/custom-icons/special-warning.tsx +16 -0
  62. package/src/components/ds-panel/ds-panel.module.scss +3 -3
  63. package/src/components/ds-progress-arc/ds-progress-arc.module.scss +57 -0
  64. package/src/components/ds-progress-arc/ds-progress-arc.tsx +111 -0
  65. package/src/components/ds-progress-arc/ds-progress-arc.types.ts +34 -0
  66. package/src/components/ds-progress-arc/index.ts +3 -0
  67. package/src/components/ds-progress-arc/utils.test.ts +79 -0
  68. package/src/components/ds-progress-arc/utils.ts +61 -0
  69. package/src/components/ds-progress-donut/ds-progress-donut.module.scss +67 -0
  70. package/src/components/ds-progress-donut/ds-progress-donut.tsx +83 -0
  71. package/src/components/ds-progress-donut/ds-progress-donut.types.ts +38 -0
  72. package/src/components/ds-progress-donut/index.ts +2 -0
  73. package/src/components/ds-progress-donut/utils.ts +1 -0
  74. package/src/components/ds-progress-linear/ds-progress-linear.module.scss +100 -0
  75. package/src/components/ds-progress-linear/ds-progress-linear.tsx +107 -0
  76. package/src/components/ds-progress-linear/ds-progress-linear.types.ts +72 -0
  77. package/src/components/ds-progress-linear/index.ts +2 -0
  78. package/src/components/ds-progress-linear/utils.test.ts +70 -0
  79. package/src/components/ds-progress-linear/utils.ts +7 -0
  80. package/src/components/ds-radio-group/ds-radio-group.tsx +2 -0
  81. package/src/components/ds-select/ds-select.tsx +2 -1
  82. package/src/components/ds-select/ds-select.types.tsx +7 -0
  83. package/src/components/ds-stepper/components/ds-step.tsx +1 -1
  84. package/src/components/ds-stepper/ds-stepper.module.scss +16 -2
  85. package/src/components/ds-stepper/ds-stepper.tsx +3 -1
  86. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.module.scss +9 -0
  87. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +137 -0
  88. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +22 -0
  89. package/src/components/ds-table/components/ds-table-body-virtualized/index.ts +2 -0
  90. package/src/components/ds-table/components/ds-table-cell/ds-table-cell.module.scss +3 -1
  91. package/src/components/ds-table/components/ds-table-header/ds-table-header.module.scss +7 -10
  92. package/src/components/ds-table/components/ds-table-header/ds-table-header.tsx +3 -4
  93. package/src/components/ds-table/components/ds-table-row/ds-table-row.module.scss +13 -53
  94. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +32 -85
  95. package/src/components/ds-table/components/ds-table-row/ds-table-row.types.ts +0 -6
  96. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.module.scss +10 -0
  97. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.tsx +45 -0
  98. package/src/components/ds-table/components/ds-table-row-expandable-cell/ds-table-row-expandable-cell.types.ts +7 -0
  99. package/src/components/ds-table/components/ds-table-row-expandable-cell/index.ts +2 -0
  100. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.module.scss +6 -0
  101. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.tsx +30 -0
  102. package/src/components/ds-table/components/ds-table-row-selectable-cell/ds-table-row-selectable-cell.types.ts +6 -0
  103. package/src/components/ds-table/components/ds-table-row-selectable-cell/index.ts +2 -0
  104. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.module.scss +80 -0
  105. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.tsx +95 -0
  106. package/src/components/ds-table/components/ds-table-row-virtualized/ds-table-row-virtualized.types.ts +11 -0
  107. package/src/components/ds-table/components/ds-table-row-virtualized/index.ts +2 -0
  108. package/src/components/ds-table/context/ds-table-context.tsx +0 -9
  109. package/src/components/ds-table/ds-table.module.scss +4 -17
  110. package/src/components/ds-table/ds-table.tsx +44 -77
  111. package/src/components/ds-table/ds-table.types.ts +57 -12
  112. package/src/components/ds-table/index.ts +1 -0
  113. package/src/components/ds-table/styles/_mixins.scss +15 -0
  114. package/src/components/ds-table/styles/_variables.scss +2 -2
  115. package/src/components/ds-table/utils/{column-styling.ts → column-size.ts} +4 -14
  116. package/src/components/ds-table/utils/constants.ts +4 -0
  117. package/src/components/ds-tag/index.tsx +1 -0
  118. package/src/components/ds-tag-filter/ds-tag-filter.module.scss +13 -15
  119. package/src/components/ds-tag-filter/ds-tag-filter.tsx +38 -72
  120. package/src/components/ds-tag-filter/ds-tag-filter.types.ts +2 -3
  121. package/src/components/ds-tag-filter/hooks/use-tag-overflow-calculation.ts +12 -62
  122. package/src/components/ds-tag-filter/utils.test.ts +2 -108
  123. package/src/components/ds-tag-filter/utils.ts +2 -12
  124. package/src/components/ds-tree/ds-tree.module.scss +283 -0
  125. package/src/components/ds-tree/ds-tree.tsx +172 -0
  126. package/src/components/ds-tree/ds-tree.types.ts +83 -0
  127. package/src/components/ds-tree/ds-tree.utils.ts +11 -0
  128. package/src/components/ds-tree/index.ts +3 -0
  129. package/src/index.ts +9 -0
  130. package/src/utils/format-relative-time.test.ts +74 -0
  131. package/src/utils/format-relative-time.ts +52 -0
  132. package/src/utils/numbers.test.ts +24 -0
  133. package/src/utils/numbers.ts +1 -0
  134. package/src/components/ds-table/utils/create-columns-grid-template.ts +0 -23
@@ -13,7 +13,8 @@ import { DsIcon } from '../ds-icon';
13
13
  * Design system TagFilter component
14
14
  *
15
15
  * A component for displaying active filters as tags with overflow handling.
16
- * Shows visible tags in up to 2 rows, with the Expand button to show hidden items.
16
+ * Non-tag elements (label, expand/collapse, clear) sit in a header row.
17
+ * Tags occupy the full width below, wrapping as needed.
17
18
  */
18
19
  const DsTagFilter = ({
19
20
  items,
@@ -29,7 +30,7 @@ const DsTagFilter = ({
29
30
  const containerRef = useRef<HTMLDivElement>(null);
30
31
  const measurementRef = useRef<HTMLDivElement>(null);
31
32
 
32
- const { row1TagCount, row2TagCount, hasOverflow } = useTagOverflowCalculation({
33
+ const { visibleTagCount, hasOverflow } = useTagOverflowCalculation({
33
34
  containerRef,
34
35
  measurementRef,
35
36
  totalItems: items.length,
@@ -39,23 +40,17 @@ const DsTagFilter = ({
39
40
  const {
40
41
  label = 'Filtered by:',
41
42
  clearButton = 'Clear all filters',
42
- hiddenCountPlural = 'filters',
43
- hiddenCountSingular = 'filter',
44
- collapseTagLabel = 'Collapse',
43
+ showMore = 'Show more',
44
+ showLess = 'Show less',
45
45
  } = locale;
46
46
 
47
- // Split items into row sections
48
- const row1Tags = items.slice(0, row1TagCount);
49
- const row2Tags = items.slice(row1TagCount, row1TagCount + row2TagCount);
50
- const hiddenTags = expanded ? [] : items.slice(row1TagCount + row2TagCount);
47
+ const visibleTags = expanded ? items : items.slice(0, visibleTagCount);
48
+ const hiddenCount = items.length - visibleTagCount;
51
49
 
52
50
  if (items.length === 0) {
53
51
  return null;
54
52
  }
55
53
 
56
- const hiddenCount = hiddenTags.length;
57
- const hasRow2Content = row2Tags.length > 0 || hasOverflow;
58
-
59
54
  const renderTag = (item: TagFilterItem) => {
60
55
  const tagProps = item.slotProps?.tag || {};
61
56
 
@@ -77,18 +72,8 @@ const DsTagFilter = ({
77
72
  onExpand?.(newExpanded);
78
73
  };
79
74
 
80
- // Measurement container rendered via portal to keep it outside the component's DOM tree
81
- // This prevents Testing Library from finding duplicate elements
82
- // We use it to calculate the number of remaining tags to render
83
75
  const measurementContainer = (
84
76
  <div ref={measurementRef} className={styles.measurementContainer} aria-hidden="true">
85
- {label && (
86
- <span data-measure-label="">
87
- <DsTypography variant="body-sm-reg" className={styles.label}>
88
- {label}
89
- </DsTypography>
90
- </span>
91
- )}
92
77
  {items.map((item) => (
93
78
  <DsTag
94
79
  key={item.id}
@@ -99,29 +84,9 @@ const DsTagFilter = ({
99
84
  onDelete={onItemDelete ? () => onItemDelete(item) : undefined}
100
85
  />
101
86
  ))}
102
- {onClearAll && (
103
- <DsButton
104
- data-measure-clear=""
105
- design="v1.2"
106
- buttonType="tertiary"
107
- variant="ghost"
108
- className={styles.clearButton}
109
- contentClassName={styles.clearContent}
110
- size="small"
111
- >
112
- <DsIcon icon="close" />
113
- {clearButton}
114
- </DsButton>
115
- )}
116
- <DsTag data-measure-expand="" label="+99 filters" className={styles.expandTag} />
117
87
  </div>
118
88
  );
119
89
 
120
- /**
121
- * IMPORTANT!
122
- * If you make any changes to this layout (styling, JSX, anything that affects it.)
123
- * please, apply the exact same changes to the "measurementContainer"
124
- */
125
90
  return (
126
91
  <>
127
92
  <div
@@ -129,46 +94,47 @@ const DsTagFilter = ({
129
94
  className={classNames(styles.container, expanded && styles.expanded, className)}
130
95
  style={style}
131
96
  >
132
- <div className={styles.row1}>
97
+ <div className={styles.header}>
133
98
  {label && (
134
99
  <DsTypography variant="body-sm-reg" className={styles.label}>
135
100
  {label}
136
101
  </DsTypography>
137
102
  )}
138
- {row1Tags.map((item) => renderTag(item))}
139
- {onClearAll && (
140
- <DsButton
141
- design="v1.2"
142
- buttonType="tertiary"
143
- variant="ghost"
144
- className={styles.clearButton}
145
- contentClassName={styles.clearContent}
146
- size="small"
147
- onClick={onClearAll}
148
- >
149
- <DsIcon icon="close" />
150
- {clearButton}
151
- </DsButton>
152
- )}
153
- </div>
154
103
 
155
- {hasRow2Content && (
156
- <div className={styles.row2}>
157
- {row2Tags.map((item) => renderTag(item))}
104
+ <div className={styles.headerActions}>
158
105
  {hasOverflow && (
159
- <DsTag
160
- label={
161
- expanded
162
- ? collapseTagLabel
163
- : `+${String(hiddenCount)} ${hiddenCount === 1 ? hiddenCountSingular : hiddenCountPlural}`
164
- }
165
- selected={expanded}
166
- className={styles.expandTag}
106
+ <DsButton
107
+ design="v1.2"
108
+ buttonType="tertiary"
109
+ variant="ghost"
110
+ className={styles.actionButton}
111
+ contentClassName={styles.ghostButtonContent}
112
+ size="small"
167
113
  onClick={handleExpandToggle}
168
- />
114
+ >
115
+ <DsIcon icon={expanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down'} />
116
+ {expanded ? showLess : `${showMore} (${String(hiddenCount)})`}
117
+ </DsButton>
118
+ )}
119
+
120
+ {onClearAll && (
121
+ <DsButton
122
+ design="v1.2"
123
+ buttonType="tertiary"
124
+ variant="ghost"
125
+ className={styles.actionButton}
126
+ contentClassName={styles.ghostButtonContent}
127
+ size="small"
128
+ onClick={onClearAll}
129
+ >
130
+ <DsIcon icon="close" />
131
+ {clearButton}
132
+ </DsButton>
169
133
  )}
170
134
  </div>
171
- )}
135
+ </div>
136
+
137
+ <div className={styles.tagsArea}>{visibleTags.map((item) => renderTag(item))}</div>
172
138
  </div>
173
139
  {createPortal(measurementContainer, document.body)}
174
140
  </>
@@ -37,9 +37,8 @@ export interface DsTagFilterProps {
37
37
  locale?: {
38
38
  label?: string;
39
39
  clearButton?: string;
40
- collapseTagLabel?: string;
41
- hiddenCountSingular?: string;
42
- hiddenCountPlural?: string;
40
+ showMore?: string;
41
+ showLess?: string;
43
42
  };
44
43
  /**
45
44
  * Callback when "Clear all" is clicked
@@ -9,21 +9,15 @@ interface UseTagOverflowCalculationOptions {
9
9
  }
10
10
 
11
11
  interface UseTagOverflowCalculationResult {
12
- row1TagCount: number;
13
- row2TagCount: number;
12
+ visibleTagCount: number;
14
13
  hasOverflow: boolean;
15
14
  }
16
15
 
17
- // Extra space needed per row for animated delete button that appears on hover
18
- const TAG_HOVER_BUTTON_SPACE = 20;
19
-
20
16
  /**
21
- * Custom hook to calculate how many tags can fit in 2 rows.
22
- *
23
- * Row 1: Label + Tags + Clear Button
24
- * Row 2: Remaining Tags + Expand Tag (only if overflow)
17
+ * Custom hook to calculate how many tags fit in the available container width.
25
18
  *
26
- * Measures actual element widths to determine optimal placement.
19
+ * Tags occupy the full container width. Non-tag elements (label, clear button,
20
+ * expand control) live in a separate header row and don't affect the calculation.
27
21
  */
28
22
  export const useTagOverflowCalculation = ({
29
23
  containerRef,
@@ -32,8 +26,7 @@ export const useTagOverflowCalculation = ({
32
26
  expanded,
33
27
  }: UseTagOverflowCalculationOptions): UseTagOverflowCalculationResult => {
34
28
  const [state, setState] = useState<UseTagOverflowCalculationResult>({
35
- row1TagCount: 0,
36
- row2TagCount: 0,
29
+ visibleTagCount: 0,
37
30
  hasOverflow: false,
38
31
  });
39
32
 
@@ -45,63 +38,22 @@ export const useTagOverflowCalculation = ({
45
38
  const container = containerRef.current;
46
39
  const measurementContainer = measurementRef.current;
47
40
 
48
- const { tagWidths, labelWidth, clearButtonWidth, expandTagWidth, gap } =
49
- getElementMeasurements(measurementContainer);
41
+ const { tagWidths, gap } = getElementMeasurements(measurementContainer);
50
42
 
51
43
  if (tagWidths.length === 0) {
52
- setState({ row1TagCount: 0, row2TagCount: 0, hasOverflow: false });
44
+ setState({ visibleTagCount: 0, hasOverflow: false });
53
45
  return;
54
46
  }
55
47
 
56
48
  const containerWidth = getContainerAvailableWidth(container);
57
49
 
58
- // Calculate Row 1 available space (Label + Tags + Clear Button)
59
- const row1AvailableWidth = containerWidth - labelWidth - clearButtonWidth - TAG_HOVER_BUTTON_SPACE;
60
-
61
- // Calculate how many tags fit in Row 1
62
- const row1Result = fitTagsInRow(tagWidths, row1AvailableWidth, gap);
63
- const remainingTags = tagWidths.slice(row1Result.count);
64
-
65
- if (remainingTags.length === 0) {
66
- // All tags fit in Row 1, no overflow
67
- setState({ row1TagCount: row1Result.count, row2TagCount: 0, hasOverflow: false });
68
- return;
69
- }
70
-
71
- // Calculate with expand tag reserved (pessimistic)
72
- const row2AvailableWithExpand = containerWidth - expandTagWidth - TAG_HOVER_BUTTON_SPACE;
73
- const row2WithExpandResult = fitTagsInRow(remainingTags, row2AvailableWithExpand, gap);
74
-
75
- // Check if there's overflow (more remaining tags than fit in Row 2 with expand)
76
- const overflowExists = remainingTags.length > row2WithExpandResult.count;
50
+ const { count } = fitTagsInRow(tagWidths, containerWidth, gap);
51
+ const hasOverflow = count < tagWidths.length;
77
52
 
78
- if (overflowExists) {
79
- // Use the count with expand tag space reserved
80
- setState({
81
- row1TagCount: row1Result.count,
82
- row2TagCount: row2WithExpandResult.count,
83
- hasOverflow: true,
84
- });
85
- } else {
86
- // No overflow - recalculate Row 2 without reserving expand tag space
87
- const row2AvailableWithoutExpand = containerWidth - TAG_HOVER_BUTTON_SPACE;
88
- const row2FullResult = fitTagsInRow(remainingTags, row2AvailableWithoutExpand, gap);
89
-
90
- // Double-check: if even without expand tag we can't fit all, we have overflow
91
- if (row2FullResult.count < remainingTags.length) {
92
- setState({
93
- row1TagCount: row1Result.count,
94
- row2TagCount: row2WithExpandResult.count,
95
- hasOverflow: true,
96
- });
97
- } else {
98
- setState({ row1TagCount: row1Result.count, row2TagCount: row2FullResult.count, hasOverflow: false });
99
- }
100
- }
53
+ setState({ visibleTagCount: count, hasOverflow });
101
54
  }, [containerRef, measurementRef]);
102
55
 
103
56
  useLayoutEffect(() => {
104
- // Use requestAnimationFrame to ensure DOM is fully laid out
105
57
  const rafId = requestAnimationFrame(() => {
106
58
  calculateLayout();
107
59
  });
@@ -122,12 +74,10 @@ export const useTagOverflowCalculation = ({
122
74
  };
123
75
  }, [containerRef, measurementRef, totalItems, expanded, calculateLayout]);
124
76
 
125
- // When expanded, show all tags
126
77
  if (expanded) {
127
78
  return {
128
- row1TagCount: state.row1TagCount,
129
- row2TagCount: totalItems - state.row1TagCount,
130
- hasOverflow: true, // Keep expand button visible to allow collapse
79
+ visibleTagCount: totalItems,
80
+ hasOverflow: true,
131
81
  };
132
82
  }
133
83
 
@@ -212,26 +212,15 @@ describe('Tag Filter Utils', () => {
212
212
  vi.restoreAllMocks();
213
213
  });
214
214
 
215
- it('should measure all elements with default gap', () => {
216
- // Create mock elements
215
+ it('should measure tag widths and gap', () => {
217
216
  const tag1 = document.createElement('div');
218
217
  tag1.setAttribute('data-measure-tag', '');
219
218
  const tag2 = document.createElement('div');
220
219
  tag2.setAttribute('data-measure-tag', '');
221
- const label = document.createElement('div');
222
- label.setAttribute('data-measure-label', '');
223
- const clearButton = document.createElement('div');
224
- clearButton.setAttribute('data-measure-clear', '');
225
- const expandTag = document.createElement('div');
226
- expandTag.setAttribute('data-measure-expand', '');
227
220
 
228
221
  mockContainer.appendChild(tag1);
229
222
  mockContainer.appendChild(tag2);
230
- mockContainer.appendChild(label);
231
- mockContainer.appendChild(clearButton);
232
- mockContainer.appendChild(expandTag);
233
223
 
234
- // Mock getBoundingClientRect for each element
235
224
  vi.spyOn(tag1, 'getBoundingClientRect').mockReturnValue({
236
225
  width: 80,
237
226
  height: 30,
@@ -256,42 +245,6 @@ describe('Tag Filter Utils', () => {
256
245
  toJSON: () => ({}),
257
246
  });
258
247
 
259
- vi.spyOn(label, 'getBoundingClientRect').mockReturnValue({
260
- width: 60,
261
- height: 30,
262
- top: 0,
263
- left: 0,
264
- bottom: 30,
265
- right: 60,
266
- x: 0,
267
- y: 0,
268
- toJSON: () => ({}),
269
- });
270
-
271
- vi.spyOn(clearButton, 'getBoundingClientRect').mockReturnValue({
272
- width: 40,
273
- height: 30,
274
- top: 0,
275
- left: 0,
276
- bottom: 30,
277
- right: 40,
278
- x: 0,
279
- y: 0,
280
- toJSON: () => ({}),
281
- });
282
-
283
- vi.spyOn(expandTag, 'getBoundingClientRect').mockReturnValue({
284
- width: 100,
285
- height: 30,
286
- top: 0,
287
- left: 0,
288
- bottom: 30,
289
- right: 100,
290
- x: 0,
291
- y: 0,
292
- toJSON: () => ({}),
293
- });
294
-
295
248
  vi.spyOn(window, 'getComputedStyle').mockReturnValue({
296
249
  gap: '8px',
297
250
  } as CSSStyleDeclaration);
@@ -299,42 +252,9 @@ describe('Tag Filter Utils', () => {
299
252
  const result = getElementMeasurements(mockContainer);
300
253
 
301
254
  expect(result.tagWidths).toEqual([80, 90]);
302
- expect(result.labelWidth).toBe(68); // 60 + 8
303
- expect(result.clearButtonWidth).toBe(48); // 40 + 8
304
- expect(result.expandTagWidth).toBe(108); // 100 + 8
305
255
  expect(result.gap).toBe(8);
306
256
  });
307
257
 
308
- it('should handle missing optional elements', () => {
309
- const tag1 = document.createElement('div');
310
- tag1.setAttribute('data-measure-tag', '');
311
- mockContainer.appendChild(tag1);
312
-
313
- vi.spyOn(tag1, 'getBoundingClientRect').mockReturnValue({
314
- width: 80,
315
- height: 30,
316
- top: 0,
317
- left: 0,
318
- bottom: 30,
319
- right: 80,
320
- x: 0,
321
- y: 0,
322
- toJSON: () => ({}),
323
- });
324
-
325
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
326
- gap: '12px',
327
- } as CSSStyleDeclaration);
328
-
329
- const result = getElementMeasurements(mockContainer);
330
-
331
- expect(result.tagWidths).toEqual([80]);
332
- expect(result.labelWidth).toBe(0); // Missing label
333
- expect(result.clearButtonWidth).toBe(0); // Missing clear button
334
- expect(result.expandTagWidth).toBe(100); // Fallback width
335
- expect(result.gap).toBe(12);
336
- });
337
-
338
258
  it('should use fallback gap when gap is invalid', () => {
339
259
  const tag1 = document.createElement('div');
340
260
  tag1.setAttribute('data-measure-tag', '');
@@ -358,7 +278,7 @@ describe('Tag Filter Utils', () => {
358
278
 
359
279
  const result = getElementMeasurements(mockContainer);
360
280
 
361
- expect(result.gap).toBe(8); // Fallback to 8
281
+ expect(result.gap).toBe(8);
362
282
  });
363
283
 
364
284
  it('should handle no tags present', () => {
@@ -371,31 +291,5 @@ describe('Tag Filter Utils', () => {
371
291
  expect(result.tagWidths).toEqual([]);
372
292
  expect(result.gap).toBe(10);
373
293
  });
374
-
375
- it('should use fallback for expand tag when width is 0', () => {
376
- const expandTag = document.createElement('div');
377
- expandTag.setAttribute('data-measure-expand', '');
378
- mockContainer.appendChild(expandTag);
379
-
380
- vi.spyOn(expandTag, 'getBoundingClientRect').mockReturnValue({
381
- width: 0,
382
- height: 30,
383
- top: 0,
384
- left: 0,
385
- bottom: 30,
386
- right: 0,
387
- x: 0,
388
- y: 0,
389
- toJSON: () => ({}),
390
- });
391
-
392
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
393
- gap: '8px',
394
- } as CSSStyleDeclaration);
395
-
396
- const result = getElementMeasurements(mockContainer);
397
-
398
- expect(result.expandTagWidth).toBe(8); // 0 + 8 (not using fallback since element exists)
399
- });
400
294
  });
401
295
  });
@@ -5,9 +5,6 @@ export interface FitTagsResult {
5
5
 
6
6
  export interface ElementMeasurements {
7
7
  tagWidths: number[];
8
- labelWidth: number;
9
- clearButtonWidth: number;
10
- expandTagWidth: number;
11
8
  gap: number;
12
9
  }
13
10
 
@@ -44,22 +41,15 @@ export function getContainerAvailableWidth(container: HTMLDivElement): number {
44
41
  }
45
42
 
46
43
  /**
47
- * Measure all relevant elements (tags, label, buttons) inside the measurement container.
44
+ * Measure tag widths and gap inside the measurement container.
48
45
  */
49
46
  export function getElementMeasurements(measurementContainer: HTMLDivElement): ElementMeasurements {
50
47
  const tags = Array.from(measurementContainer.querySelectorAll('[data-measure-tag]'));
51
- const label = measurementContainer.querySelector('[data-measure-label]');
52
- const clearButton = measurementContainer.querySelector('[data-measure-clear]');
53
- const expandTag = measurementContainer.querySelector('[data-measure-expand]');
54
48
 
55
49
  const computedStyle = getComputedStyle(measurementContainer);
56
50
  const gap = parseFloat(computedStyle.gap) || 8;
57
51
 
58
52
  const tagWidths = tags.map((tag) => tag.getBoundingClientRect().width);
59
- const labelWidth = label ? label.getBoundingClientRect().width + gap : 0;
60
- const clearButtonWidth = clearButton ? clearButton.getBoundingClientRect().width + gap : 0;
61
- // Fallback width for expand tag (~100px for "+99 filters" text) if measurement fails
62
- const expandTagWidth = expandTag ? expandTag.getBoundingClientRect().width + gap : 100;
63
53
 
64
- return { tagWidths, labelWidth, clearButtonWidth, expandTagWidth, gap };
54
+ return { tagWidths, gap };
65
55
  }