@drivenets/design-system 0.7.1 → 0.7.2

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 (106) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/index.cjs +414 -679
  3. package/dist/index.d.cts +39 -3
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.ts +39 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +410 -670
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.css +1 -1
  10. package/package.json +24 -20
  11. package/src/components/ds-breadcrumb/ds-breadcrumb.tsx +1 -1
  12. package/src/components/ds-button/versions/ds-button-legacy/ds-button-legacy.tsx +1 -0
  13. package/src/components/ds-button/versions/ds-button-new/ds-button-new.module.scss +13 -0
  14. package/src/components/ds-button/versions/ds-button-new/ds-button-new.tsx +1 -1
  15. package/src/components/ds-button/versions/ds-button-new/ds-button-new.types.ts +1 -1
  16. package/src/components/ds-checkbox/ds-checkbox.module.scss +36 -5
  17. package/src/components/ds-checkbox/ds-checkbox.tsx +2 -1
  18. package/src/components/ds-checkbox/{ds-checkbox.types.tsx → ds-checkbox.types.ts} +7 -0
  19. package/src/components/ds-chip-group/ds-chip-group.tsx +1 -1
  20. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.module.scss +0 -23
  21. package/src/components/ds-comment-bubble/components/ds-thread-item/ds-thread-item.tsx +2 -11
  22. package/src/components/ds-comment-bubble/ds-comment-bubble.module.scss +0 -39
  23. package/src/components/ds-comment-bubble/ds-comment-bubble.tsx +21 -20
  24. package/src/components/ds-comment-card/ds-comment-card.tsx +2 -2
  25. package/src/components/ds-confirmation/ds-confirmation.tsx +1 -1
  26. package/src/components/ds-dropdown-menu/ds-dropdown-menu.module.scss +1 -1
  27. package/src/components/ds-dropdown-menu/ds-dropdown-menu.tsx +2 -2
  28. package/src/components/ds-expandable-text-input/ds-expandable-text-input.tsx +6 -2
  29. package/src/components/ds-segment-group/ds-segment-group.tsx +1 -1
  30. package/src/components/ds-select/ds-select.tsx +41 -7
  31. package/src/components/ds-select/{ds-select.types.tsx → ds-select.types.ts} +12 -0
  32. package/src/components/ds-select/select-items-chips.tsx +3 -2
  33. package/src/components/ds-select/utils.test.ts +37 -0
  34. package/src/components/ds-select/utils.ts +7 -0
  35. package/src/components/ds-smart-tabs/{ds-smart-tabs.types.tsx → ds-smart-tabs.types.ts} +1 -1
  36. package/src/components/ds-stepper/components/ds-step-next-button.tsx +7 -3
  37. package/src/components/ds-stepper/components/ds-step.tsx +1 -1
  38. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.tsx +4 -1
  39. package/src/components/ds-table/components/ds-table-body-virtualized/ds-table-body-virtualized.types.ts +4 -0
  40. package/src/components/ds-table/components/ds-table-row/ds-table-row.tsx +1 -1
  41. package/src/components/ds-tabs/components/ds-tabs-tab/ds-tabs-tab.tsx +1 -2
  42. package/src/components/ds-tag-filter/{utils.test.ts → utils.browser.test.ts} +20 -113
  43. package/src/components/ds-text-input/ds-text-input.tsx +4 -0
  44. package/src/components/ds-text-input/{ds-text-input.types.tsx → ds-text-input.types.ts} +8 -1
  45. /package/src/components/ds-alert-banner/{ds-alert-banner.types.tsx → ds-alert-banner.types.ts} +0 -0
  46. /package/src/components/ds-alert-banner/{index.tsx → index.ts} +0 -0
  47. /package/src/components/ds-autocomplete/{ds-autocomplete.types.tsx → ds-autocomplete.types.ts} +0 -0
  48. /package/src/components/ds-breadcrumb/{ds-breadcrumb.types.tsx → ds-breadcrumb.types.ts} +0 -0
  49. /package/src/components/ds-breadcrumb/{index.tsx → index.ts} +0 -0
  50. /package/src/components/ds-checkbox/{index.tsx → index.ts} +0 -0
  51. /package/src/components/ds-chip/{ds-chip.types.tsx → ds-chip.types.ts} +0 -0
  52. /package/src/components/ds-chip/{index.tsx → index.ts} +0 -0
  53. /package/src/components/ds-comments-drawer/{comments-filters.test.ts → comments-filters.unit.test.ts} +0 -0
  54. /package/src/components/ds-confirmation/{ds-confirmation.types.tsx → ds-confirmation.types.ts} +0 -0
  55. /package/src/components/ds-date-input/{ds-date-input.types.tsx → ds-date-input.types.ts} +0 -0
  56. /package/src/components/ds-dialog/{ds-dialog.types.tsx → ds-dialog.types.ts} +0 -0
  57. /package/src/components/ds-dialog/{index.tsx → index.ts} +0 -0
  58. /package/src/components/ds-drawer/{ds-drawer.types.tsx → ds-drawer.types.ts} +0 -0
  59. /package/src/components/ds-dropdown-menu/{ds-dropdown-menu.types.tsx → ds-dropdown-menu.types.ts} +0 -0
  60. /package/src/components/ds-dropdown-menu/{index.tsx → index.ts} +0 -0
  61. /package/src/components/ds-expandable-text-input/{ds-expandable-text-input.types.tsx → ds-expandable-text-input.types.ts} +0 -0
  62. /package/src/components/ds-file-upload/components/file-upload/{file-upload.types.tsx → file-upload.types.ts} +0 -0
  63. /package/src/components/ds-file-upload/components/file-upload-item/{file-upload-item.types.tsx → file-upload-item.types.ts} +0 -0
  64. /package/src/components/ds-file-upload/{ds-file-upload.types.tsx → ds-file-upload.types.ts} +0 -0
  65. /package/src/components/ds-form-control/{ds-form-control.types.tsx → ds-form-control.types.ts} +0 -0
  66. /package/src/components/ds-form-control/{index.tsx → index.ts} +0 -0
  67. /package/src/components/ds-modal/{ds-modal.types.tsx → ds-modal.types.ts} +0 -0
  68. /package/src/components/ds-modal/{index.tsx → index.ts} +0 -0
  69. /package/src/components/ds-number-input/{ds-number-input.types.tsx → ds-number-input.types.ts} +0 -0
  70. /package/src/components/ds-number-input/{index.tsx → index.ts} +0 -0
  71. /package/src/components/ds-panel/{index.tsx → index.ts} +0 -0
  72. /package/src/components/ds-password-input/{ds-password-input.types.tsx → ds-password-input.types.ts} +0 -0
  73. /package/src/components/ds-password-input/{index.tsx → index.ts} +0 -0
  74. /package/src/components/ds-popover/{ds-popover.types.tsx → ds-popover.types.ts} +0 -0
  75. /package/src/components/ds-popover/{index.tsx → index.ts} +0 -0
  76. /package/src/components/ds-progress-arc/{utils.test.ts → utils.unit.test.ts} +0 -0
  77. /package/src/components/ds-progress-linear/{utils.test.ts → utils.unit.test.ts} +0 -0
  78. /package/src/components/ds-radio-group/{ds-radio-group.types.tsx → ds-radio-group.types.ts} +0 -0
  79. /package/src/components/ds-radio-group/{index.tsx → index.ts} +0 -0
  80. /package/src/components/ds-segment-group/{ds-segment-group.types.tsx → ds-segment-group.types.ts} +0 -0
  81. /package/src/components/ds-segment-group/{index.tsx → index.ts} +0 -0
  82. /package/src/components/ds-select/{index.tsx → index.ts} +0 -0
  83. /package/src/components/ds-skeleton/{ds-skeleton.tsx → ds-skeleton.ts} +0 -0
  84. /package/src/components/ds-smart-tabs/{index.tsx → index.ts} +0 -0
  85. /package/src/components/ds-spinner/{ds-spinner.types.tsx → ds-spinner.types.ts} +0 -0
  86. /package/src/components/ds-spinner/{index.tsx → index.ts} +0 -0
  87. /package/src/components/ds-status-badge/{ds-status-badge.types.tsx → ds-status-badge.types.ts} +0 -0
  88. /package/src/components/ds-status-badge/{index.tsx → index.ts} +0 -0
  89. /package/src/components/ds-stepper/{index.tsx → index.ts} +0 -0
  90. /package/src/components/ds-system-status/{ds-system-status.types.tsx → ds-system-status.types.ts} +0 -0
  91. /package/src/components/ds-system-status/{index.tsx → index.ts} +0 -0
  92. /package/src/components/ds-table/context/{ds-table-context.tsx → ds-table-context.ts} +0 -0
  93. /package/src/components/ds-tag/{ds-tag.types.tsx → ds-tag.types.ts} +0 -0
  94. /package/src/components/ds-tag/{index.tsx → index.ts} +0 -0
  95. /package/src/components/ds-textarea/{ds-textarea.types.tsx → ds-textarea.types.ts} +0 -0
  96. /package/src/components/ds-textarea/{index.tsx → index.ts} +0 -0
  97. /package/src/components/ds-toast/{ds-toast.types.tsx → ds-toast.types.ts} +0 -0
  98. /package/src/components/ds-toggle/{ds-toggle.types.tsx → ds-toggle.types.ts} +0 -0
  99. /package/src/components/ds-toggle/{index.tsx → index.ts} +0 -0
  100. /package/src/components/ds-tooltip/{ds-tooltip.types.tsx → ds-tooltip.types.ts} +0 -0
  101. /package/src/components/ds-tooltip/{index.tsx → index.ts} +0 -0
  102. /package/src/components/ds-typography/{ds-typography.types.tsx → ds-typography.types.ts} +0 -0
  103. /package/src/components/ds-typography/{index.tsx → index.ts} +0 -0
  104. /package/src/utils/{format-relative-time.test.ts → format-relative-time.unit.test.ts} +0 -0
  105. /package/src/{components/ds-table/utils → utils}/merge-refs.ts +0 -0
  106. /package/src/utils/{numbers.test.ts → numbers.unit.test.ts} +0 -0
@@ -9,11 +9,11 @@ import { type DsCheckboxProps, DsCheckbox } from '../ds-checkbox';
9
9
  import { SelectItemsChips } from './select-items-chips';
10
10
  import { DsTypography } from '../ds-typography';
11
11
  import { DsTextInput } from '../ds-text-input';
12
+ import { SELECT_ALL_VALUE, getUserSelectedItems } from './utils';
12
13
 
13
14
  const SEARCH_THRESHOLD = 13;
14
- const SELECT_ALL_VALUE = '__INTERNAL_SELECT_ALL_VALUE__';
15
15
 
16
- const SELECT_ALL: DsSelectOption = {
16
+ const SELECT_ALL_OPTION: DsSelectOption = {
17
17
  label: 'All',
18
18
  value: SELECT_ALL_VALUE,
19
19
  };
@@ -36,7 +36,7 @@ const DsSelect = ({
36
36
  const [searchTerm, setSearchTerm] = useState('');
37
37
  const [showAllItems, setShowAllItems] = useState(false);
38
38
 
39
- const internalOptions = multiselectProps.multiple ? [SELECT_ALL, ...userOptions] : userOptions;
39
+ const internalOptions = multiselectProps.multiple ? [SELECT_ALL_OPTION, ...userOptions] : userOptions;
40
40
 
41
41
  const collection = createListCollection({
42
42
  items: internalOptions,
@@ -91,6 +91,40 @@ const DsSelect = ({
91
91
  },
92
92
  });
93
93
 
94
+ const renderTriggerValue = () => {
95
+ const defaultTrigger = (
96
+ <DsTypography className={styles.valueText} variant="body-sm-reg" asChild>
97
+ <Select.ValueText placeholder={placeholder} />
98
+ </DsTypography>
99
+ );
100
+
101
+ if (!multiselectProps.renderValue || !select.hasSelectedItems) {
102
+ return defaultTrigger;
103
+ }
104
+
105
+ const userItems = getUserSelectedItems(select.selectedItems);
106
+
107
+ if (multiselectProps.multiple) {
108
+ return (
109
+ <DsTypography className={styles.valueText} variant="body-sm-reg">
110
+ {multiselectProps.renderValue(userItems)}
111
+ </DsTypography>
112
+ );
113
+ }
114
+
115
+ const firstItem = userItems[0];
116
+
117
+ if (!firstItem) {
118
+ return defaultTrigger;
119
+ }
120
+
121
+ return (
122
+ <DsTypography className={styles.valueText} variant="body-sm-reg">
123
+ {multiselectProps.renderValue(firstItem)}
124
+ </DsTypography>
125
+ );
126
+ };
127
+
94
128
  return (
95
129
  <Select.RootProvider
96
130
  value={select}
@@ -132,9 +166,7 @@ const DsSelect = ({
132
166
  // automatic labelling here.
133
167
  aria-labelledby={null as never}
134
168
  >
135
- <DsTypography className={styles.valueText} variant="body-sm-reg" asChild>
136
- <Select.ValueText placeholder={placeholder} />
137
- </DsTypography>
169
+ {renderTriggerValue()}
138
170
 
139
171
  <Select.Indicator className={styles.triggerIcon}>
140
172
  <DsIcon icon="keyboard_arrow_down" size={size === 'small' ? 'small' : 'medium'} />
@@ -192,7 +224,9 @@ const DsSelect = ({
192
224
  <Select.Item item={item}>
193
225
  {multiselectProps.multiple && <DsCheckbox checked={checked} />}
194
226
  {item.icon && <DsIcon className={styles.itemIcon} icon={item.icon} aria-hidden="true" />}
195
- <Select.ItemText>{renderOption ? renderOption(item) : item.label}</Select.ItemText>
227
+ <Select.ItemText>
228
+ {renderOption && item.value !== SELECT_ALL_VALUE ? renderOption(item) : item.label}
229
+ </Select.ItemText>
196
230
  </Select.Item>
197
231
  </DsTypography>
198
232
  );
@@ -78,10 +78,22 @@ export type DsSelectProps = {
78
78
  multiple?: undefined | false;
79
79
  value: SelectOptionValue;
80
80
  onValueChange?: (value: SelectOptionValue) => void;
81
+ /**
82
+ * Custom render function for the selected value in the trigger.
83
+ * When provided, replaces the default label text inside the trigger button.
84
+ * Only called when there is a selection; otherwise the placeholder is shown.
85
+ */
86
+ renderValue?: (selectedOption: DsSelectOption) => ReactNode;
81
87
  }
82
88
  | {
83
89
  multiple: true;
84
90
  value: SelectOptionValue[];
85
91
  onValueChange?: (value: SelectOptionValue[]) => void;
92
+ /**
93
+ * Custom render function for the selected value in the trigger.
94
+ * When provided, replaces the default label text inside the trigger button.
95
+ * Only called when there is a selection; otherwise the placeholder is shown.
96
+ */
97
+ renderValue?: (selectedOptions: DsSelectOption[]) => ReactNode;
86
98
  }
87
99
  );
@@ -1,7 +1,8 @@
1
1
  import { useSelectContext, type UseSelectContext } from '@ark-ui/react/select';
2
2
  import styles from './ds-select.module.scss';
3
- import DsButton from '../ds-button/ds-button';
4
- import DsChip from '../ds-chip/ds-chip';
3
+ import { DsButton } from '../ds-button';
4
+ // TODO: Use DsTag instead.
5
+ import { DsChip } from '../ds-chip';
5
6
  import type { DsSelectOption, SelectOptionValue } from './ds-select.types';
6
7
 
7
8
  type SelectItemsChipsProps = {
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import type { DsSelectOption } from './ds-select.types';
3
+ import { SELECT_ALL_VALUE, getUserSelectedItems } from './utils';
4
+
5
+ describe('getUserSelectedItems', () => {
6
+ it('should return all items when none is the internal select-all', () => {
7
+ const items: DsSelectOption[] = [
8
+ { value: 'a', label: 'A' },
9
+ { value: 'b', label: 'B' },
10
+ ];
11
+
12
+ expect(getUserSelectedItems(items)).toEqual(items);
13
+ });
14
+
15
+ it('should filter out the internal select-all item', () => {
16
+ const items: DsSelectOption[] = [
17
+ { value: SELECT_ALL_VALUE, label: 'All' },
18
+ { value: 'a', label: 'A' },
19
+ { value: 'b', label: 'B' },
20
+ ];
21
+
22
+ expect(getUserSelectedItems(items)).toEqual([
23
+ { value: 'a', label: 'A' },
24
+ { value: 'b', label: 'B' },
25
+ ]);
26
+ });
27
+
28
+ it('should return an empty array when only the select-all item is present', () => {
29
+ const items: DsSelectOption[] = [{ value: SELECT_ALL_VALUE, label: 'All' }];
30
+
31
+ expect(getUserSelectedItems(items)).toEqual([]);
32
+ });
33
+
34
+ it('should return an empty array for empty input', () => {
35
+ expect(getUserSelectedItems([])).toEqual([]);
36
+ });
37
+ });
@@ -0,0 +1,7 @@
1
+ import type { DsSelectOption } from './ds-select.types';
2
+
3
+ export const SELECT_ALL_VALUE = '__INTERNAL_SELECT_ALL_VALUE__';
4
+
5
+ export function getUserSelectedItems(selectedItems: DsSelectOption[]): DsSelectOption[] {
6
+ return selectedItems.filter((item) => item.value !== SELECT_ALL_VALUE);
7
+ }
@@ -1,6 +1,6 @@
1
1
  import type { ReactNode } from 'react';
2
2
  import type React from 'react';
3
- import type { IconType } from '../ds-icon/ds-icon.types';
3
+ import { type IconType } from '../ds-icon';
4
4
 
5
5
  export const colors = [
6
6
  'dark-blue',
@@ -1,9 +1,13 @@
1
1
  import { useStepper } from '../hooks/use-stepper';
2
- import DsButton from '../../ds-button/ds-button';
3
- import type { DsButtonProps } from '../../ds-button/versions/ds-button-legacy';
2
+ import { DsButton, type DsButtonUnifiedProps } from '../../ds-button';
3
+
4
+ // TODO: Use the new button props once we deprecate the legacy button.
5
+ type DsLegacyButtonProps = Extract<DsButtonUnifiedProps, { design?: undefined | 'legacy' }> & {
6
+ design?: undefined | 'legacy';
7
+ };
4
8
 
5
9
  export type DsNextStepButtonProps = Pick<
6
- DsButtonProps,
10
+ DsLegacyButtonProps,
7
11
  'variant' | 'size' | 'className' | 'style' | 'children'
8
12
  >;
9
13
 
@@ -2,7 +2,7 @@ import type { MouseEvent, PropsWithChildren, ReactNode } from 'react';
2
2
  import { useStepper } from '../hooks/use-stepper';
3
3
  import classnames from 'classnames';
4
4
  import styles from '../ds-stepper.module.scss';
5
- import DsIcon from '../../ds-icon/ds-icon';
5
+ import { DsIcon } from '../../ds-icon';
6
6
  import { DsStepSeparator } from './ds-step-separator';
7
7
 
8
8
  export type DsStepProps = PropsWithChildren<{
@@ -59,9 +59,12 @@ export const DsTableBodyVirtualized = <TData,>({
59
59
  const scrollOffset = instance.scrollOffset || 0;
60
60
  const totalContentHeight = instance.getTotalSize();
61
61
  const viewportHeight = instance.scrollElement?.clientHeight;
62
+ const scrollDirection = instance.scrollDirection;
62
63
 
63
64
  if (viewportHeight) {
64
- onScroll({ scrollOffset, totalContentHeight, viewportHeight });
65
+ const bottomOffset = totalContentHeight - (scrollOffset + viewportHeight);
66
+
67
+ onScroll({ scrollOffset, totalContentHeight, viewportHeight, bottomOffset, scrollDirection });
65
68
  }
66
69
  }
67
70
  },
@@ -19,4 +19,8 @@ export interface ScrollParams {
19
19
  totalContentHeight: number;
20
20
  /** The visible height of the table container (in pixels) */
21
21
  viewportHeight: number;
22
+ /** The distance from the current scroll position to the bottom of the content (in pixels) */
23
+ bottomOffset: number;
24
+ /** The direction of the scroll movement */
25
+ scrollDirection: 'forward' | 'backward' | null;
22
26
  }
@@ -10,7 +10,7 @@ import { DsTableRowExpandableCell } from '../ds-table-row-expandable-cell';
10
10
  import type { DsTableRowProps } from './ds-table-row.types';
11
11
  import styles from './ds-table-row.module.scss';
12
12
  import { useDsTableContext } from '../../context/ds-table-context';
13
- import { mergeRefs } from '../../utils/merge-refs';
13
+ import { mergeRefs } from '../../../../utils/merge-refs';
14
14
  import { getColumnSizeStyle } from '../../utils/column-size';
15
15
 
16
16
  interface DsRowDragHandleProps {
@@ -1,8 +1,7 @@
1
1
  import { useRef, useState } from 'react';
2
2
  import { Tabs } from '@ark-ui/react/tabs';
3
3
  import classNames from 'classnames';
4
- import DsIcon from '../../../ds-icon/ds-icon';
5
- import type { IconType } from '../../../ds-icon';
4
+ import { DsIcon, type IconType } from '../../../ds-icon';
6
5
  import { DsTooltip } from '../../../ds-tooltip';
7
6
  import { DsDropdownMenu } from '../../../ds-dropdown-menu';
8
7
  import type { DsTabsTabProps } from '../../ds-tabs.types';
@@ -100,29 +100,17 @@ describe('Tag Filter Utils', () => {
100
100
 
101
101
  beforeEach(() => {
102
102
  mockContainer = document.createElement('div');
103
+ document.body.appendChild(mockContainer);
103
104
  });
104
105
 
105
106
  afterEach(() => {
106
107
  vi.restoreAllMocks();
108
+ document.body.removeChild(mockContainer);
107
109
  });
108
110
 
109
111
  it('should calculate available width without padding', () => {
110
- vi.spyOn(mockContainer, 'getBoundingClientRect').mockReturnValue({
111
- width: 500,
112
- height: 100,
113
- top: 0,
114
- left: 0,
115
- bottom: 100,
116
- right: 500,
117
- x: 0,
118
- y: 0,
119
- toJSON: () => ({}),
120
- });
121
-
122
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
123
- paddingLeft: '0px',
124
- paddingRight: '0px',
125
- } as CSSStyleDeclaration);
112
+ mockContainer.style.padding = '0px';
113
+ mockContainer.style.width = '500px';
126
114
 
127
115
  const result = getContainerAvailableWidth(mockContainer);
128
116
 
@@ -130,22 +118,9 @@ describe('Tag Filter Utils', () => {
130
118
  });
131
119
 
132
120
  it('should subtract padding from container width', () => {
133
- vi.spyOn(mockContainer, 'getBoundingClientRect').mockReturnValue({
134
- width: 500,
135
- height: 100,
136
- top: 0,
137
- left: 0,
138
- bottom: 100,
139
- right: 500,
140
- x: 0,
141
- y: 0,
142
- toJSON: () => ({}),
143
- });
144
-
145
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
146
- paddingLeft: '20px',
147
- paddingRight: '30px',
148
- } as CSSStyleDeclaration);
121
+ mockContainer.style.paddingLeft = '20px';
122
+ mockContainer.style.paddingRight = '30px';
123
+ mockContainer.style.width = '500px';
149
124
 
150
125
  const result = getContainerAvailableWidth(mockContainer);
151
126
 
@@ -153,22 +128,9 @@ describe('Tag Filter Utils', () => {
153
128
  });
154
129
 
155
130
  it('should handle string padding values with decimals', () => {
156
- vi.spyOn(mockContainer, 'getBoundingClientRect').mockReturnValue({
157
- width: 500,
158
- height: 100,
159
- top: 0,
160
- left: 0,
161
- bottom: 100,
162
- right: 500,
163
- x: 0,
164
- y: 0,
165
- toJSON: () => ({}),
166
- });
167
-
168
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
169
- paddingLeft: '15.5px',
170
- paddingRight: '24.5px',
171
- } as CSSStyleDeclaration);
131
+ mockContainer.style.paddingLeft = '15.5px';
132
+ mockContainer.style.paddingRight = '24.5px';
133
+ mockContainer.style.width = '500px';
172
134
 
173
135
  const result = getContainerAvailableWidth(mockContainer);
174
136
 
@@ -176,22 +138,9 @@ describe('Tag Filter Utils', () => {
176
138
  });
177
139
 
178
140
  it('should default to 0 for invalid padding values', () => {
179
- vi.spyOn(mockContainer, 'getBoundingClientRect').mockReturnValue({
180
- width: 500,
181
- height: 100,
182
- top: 0,
183
- left: 0,
184
- bottom: 100,
185
- right: 500,
186
- x: 0,
187
- y: 0,
188
- toJSON: () => ({}),
189
- });
190
-
191
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
192
- paddingLeft: '',
193
- paddingRight: '',
194
- } as CSSStyleDeclaration);
141
+ mockContainer.style.paddingLeft = 'invalid';
142
+ mockContainer.style.paddingRight = 'invalid';
143
+ mockContainer.style.width = '500px';
195
144
 
196
145
  const result = getContainerAvailableWidth(mockContainer);
197
146
 
@@ -214,41 +163,17 @@ describe('Tag Filter Utils', () => {
214
163
 
215
164
  it('should measure tag widths and gap', () => {
216
165
  const tag1 = document.createElement('div');
217
- tag1.setAttribute('data-measure-tag', '');
218
166
  const tag2 = document.createElement('div');
167
+
168
+ tag1.setAttribute('data-measure-tag', '');
219
169
  tag2.setAttribute('data-measure-tag', '');
220
170
 
171
+ tag1.style.width = '80px';
172
+ tag2.style.width = '90px';
173
+
221
174
  mockContainer.appendChild(tag1);
222
175
  mockContainer.appendChild(tag2);
223
176
 
224
- vi.spyOn(tag1, 'getBoundingClientRect').mockReturnValue({
225
- width: 80,
226
- height: 30,
227
- top: 0,
228
- left: 0,
229
- bottom: 30,
230
- right: 80,
231
- x: 0,
232
- y: 0,
233
- toJSON: () => ({}),
234
- });
235
-
236
- vi.spyOn(tag2, 'getBoundingClientRect').mockReturnValue({
237
- width: 90,
238
- height: 30,
239
- top: 0,
240
- left: 0,
241
- bottom: 30,
242
- right: 90,
243
- x: 0,
244
- y: 0,
245
- toJSON: () => ({}),
246
- });
247
-
248
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
249
- gap: '8px',
250
- } as CSSStyleDeclaration);
251
-
252
177
  const result = getElementMeasurements(mockContainer);
253
178
 
254
179
  expect(result.tagWidths).toEqual([80, 90]);
@@ -259,22 +184,7 @@ describe('Tag Filter Utils', () => {
259
184
  const tag1 = document.createElement('div');
260
185
  tag1.setAttribute('data-measure-tag', '');
261
186
  mockContainer.appendChild(tag1);
262
-
263
- vi.spyOn(tag1, 'getBoundingClientRect').mockReturnValue({
264
- width: 80,
265
- height: 30,
266
- top: 0,
267
- left: 0,
268
- bottom: 30,
269
- right: 80,
270
- x: 0,
271
- y: 0,
272
- toJSON: () => ({}),
273
- });
274
-
275
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
276
- gap: '',
277
- } as CSSStyleDeclaration);
187
+ mockContainer.style.gap = 'invalid';
278
188
 
279
189
  const result = getElementMeasurements(mockContainer);
280
190
 
@@ -282,10 +192,7 @@ describe('Tag Filter Utils', () => {
282
192
  });
283
193
 
284
194
  it('should handle no tags present', () => {
285
- vi.spyOn(window, 'getComputedStyle').mockReturnValue({
286
- gap: '10px',
287
- } as CSSStyleDeclaration);
288
-
195
+ mockContainer.style.gap = '10px';
289
196
  const result = getElementMeasurements(mockContainer);
290
197
 
291
198
  expect(result.tagWidths).toEqual([]);
@@ -9,6 +9,7 @@ const DsTextInput: FC<DsTextInputProps> = ({
9
9
  name,
10
10
  size = 'default',
11
11
  type = 'text',
12
+ onFocus,
12
13
  onBlur,
13
14
  onKeyDown,
14
15
  onChange,
@@ -23,6 +24,7 @@ const DsTextInput: FC<DsTextInputProps> = ({
23
24
  maxLength,
24
25
  placeholder,
25
26
  disabled = false,
27
+ readOnly = false,
26
28
  tabIndex,
27
29
  slots,
28
30
  }) => {
@@ -60,7 +62,9 @@ const DsTextInput: FC<DsTextInputProps> = ({
60
62
  minLength={minLength}
61
63
  maxLength={maxLength}
62
64
  placeholder={placeholder}
65
+ readOnly={readOnly}
63
66
  disabled={disabled}
67
+ onFocus={onFocus}
64
68
  onBlur={onBlur}
65
69
  onKeyDown={onKeyDown}
66
70
  onChange={handleChange}
@@ -27,6 +27,10 @@ export interface DsTextInputProps {
27
27
  * @default text
28
28
  */
29
29
  type?: string;
30
+ /**
31
+ * Event handler called when the input field receives focus
32
+ */
33
+ onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
30
34
  /**
31
35
  * Event handler called when the input field loses focus
32
36
  *
@@ -86,7 +90,10 @@ export interface DsTextInputProps {
86
90
  * Whether the input is disabled
87
91
  */
88
92
  disabled?: boolean;
89
-
93
+ /**
94
+ * Whether the input is read only
95
+ */
96
+ readOnly?: boolean;
90
97
  /**
91
98
  * The tabIndex of the input
92
99
  */
File without changes
File without changes
File without changes
File without changes