@indico-data/design-system 3.18.0 → 3.19.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 (93) hide show
  1. package/lib/components/forms/date/datePicker/types.d.ts +7 -0
  2. package/lib/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.d.ts +3 -0
  3. package/lib/components/forms/date/iconTriggerDatePicker/types.d.ts +5 -0
  4. package/lib/components/forms/date/inputDatePicker/SingleInputDatePicker.d.ts +2 -1
  5. package/lib/components/forms/date/inputDateRangePicker/InputDateRangePicker.d.ts +3 -0
  6. package/lib/components/forms/date/inputDateRangePicker/types.d.ts +7 -0
  7. package/lib/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.d.ts +2 -1
  8. package/lib/components/forms/select/Select.d.ts +3 -1
  9. package/lib/components/forms/select/types.d.ts +10 -0
  10. package/lib/components/forms/subcomponents/Label.d.ts +4 -0
  11. package/lib/components/forms/subcomponents/types.d.ts +5 -0
  12. package/lib/components/forms/timePicker/TimePicker.d.ts +4 -1
  13. package/lib/components/forms/timePicker/types.d.ts +5 -0
  14. package/lib/components/modal/ConfirmationModal.d.ts +1 -1
  15. package/lib/components/modal/Modal.d.ts +1 -1
  16. package/lib/components/modal/Modal.stories.d.ts +4 -0
  17. package/lib/components/modal/types.d.ts +19 -5
  18. package/lib/components/pagination/Pagination.d.ts +1 -1
  19. package/lib/components/pagination/index.d.ts +1 -0
  20. package/lib/components/pagination/types.d.ts +13 -0
  21. package/lib/components/stepper/Stepper.d.ts +1 -1
  22. package/lib/components/stepper/components/BackNavigation.d.ts +2 -1
  23. package/lib/components/stepper/components/NextNavigation.d.ts +3 -1
  24. package/lib/components/stepper/types.d.ts +11 -0
  25. package/lib/components/table/LoadingComponent.d.ts +5 -1
  26. package/lib/components/table/components/HorizontalStickyHeader.d.ts +4 -1
  27. package/lib/components/table/hooks/usePinnedColumnsManager.d.ts +2 -2
  28. package/lib/components/table/types.d.ts +18 -0
  29. package/lib/components/table/utils/processColumns.d.ts +2 -2
  30. package/lib/components/tanstackTable/TankstackTable.types.d.ts +19 -2
  31. package/lib/components/tanstackTable/TanstackTable.d.ts +1 -1
  32. package/lib/components/tanstackTable/components/NoResults/NoResults.d.ts +2 -1
  33. package/lib/components/tanstackTable/components/TablePagination/TablePagination.d.ts +3 -1
  34. package/lib/index.d.ts +143 -23
  35. package/lib/index.esm.js +130 -60
  36. package/lib/index.esm.js.map +1 -1
  37. package/lib/index.js +130 -60
  38. package/lib/index.js.map +1 -1
  39. package/package.json +1 -1
  40. package/src/components/button/Button.tsx +4 -18
  41. package/src/components/button/__tests__/Button.test.tsx +30 -28
  42. package/src/components/forms/date/datePicker/DatePicker.stories.tsx +10 -0
  43. package/src/components/forms/date/datePicker/DatePicker.tsx +9 -2
  44. package/src/components/forms/date/datePicker/types.ts +8 -0
  45. package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.stories.tsx +10 -0
  46. package/src/components/forms/date/iconTriggerDatePicker/IconTriggerDatePicker.tsx +12 -1
  47. package/src/components/forms/date/iconTriggerDatePicker/types.ts +5 -0
  48. package/src/components/forms/date/inputDatePicker/SingleInputDatePicker.tsx +4 -3
  49. package/src/components/forms/date/inputDatePicker/__tests__/SingleInputDatePicker.test.tsx +2 -0
  50. package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.stories.tsx +10 -0
  51. package/src/components/forms/date/inputDateRangePicker/InputDateRangePicker.tsx +14 -2
  52. package/src/components/forms/date/inputDateRangePicker/types.ts +7 -0
  53. package/src/components/forms/date/inputDateTimePicker/SingleInputDateTimePicker.tsx +3 -2
  54. package/src/components/forms/input/Input.tsx +1 -0
  55. package/src/components/forms/numberInput/NumberInput.tsx +1 -0
  56. package/src/components/forms/passwordInput/PasswordInput.tsx +7 -1
  57. package/src/components/forms/select/Select.stories.tsx +6 -5
  58. package/src/components/forms/select/Select.tsx +15 -1
  59. package/src/components/forms/select/types.ts +11 -0
  60. package/src/components/forms/subcomponents/Label.tsx +20 -3
  61. package/src/components/forms/subcomponents/types.ts +5 -0
  62. package/src/components/forms/textarea/Textarea.tsx +1 -0
  63. package/src/components/forms/timePicker/TimePicker.stories.tsx +10 -0
  64. package/src/components/forms/timePicker/TimePicker.tsx +10 -1
  65. package/src/components/forms/timePicker/types.ts +5 -0
  66. package/src/components/modal/ConfirmationModal.tsx +19 -14
  67. package/src/components/modal/Modal.stories.tsx +53 -22
  68. package/src/components/modal/Modal.tsx +8 -2
  69. package/src/components/modal/types.ts +21 -5
  70. package/src/components/pagination/Pagination.stories.tsx +11 -0
  71. package/src/components/pagination/Pagination.tsx +14 -5
  72. package/src/components/pagination/index.ts +1 -0
  73. package/src/components/pagination/types.ts +14 -0
  74. package/src/components/stepper/Stepper.stories.tsx +11 -0
  75. package/src/components/stepper/Stepper.tsx +16 -2
  76. package/src/components/stepper/components/BackNavigation.tsx +5 -3
  77. package/src/components/stepper/components/NextNavigation.tsx +15 -5
  78. package/src/components/stepper/types.ts +12 -0
  79. package/src/components/table/LoadingComponent.tsx +6 -2
  80. package/src/components/table/Table.stories.tsx +10 -0
  81. package/src/components/table/Table.tsx +12 -2
  82. package/src/components/table/components/HorizontalStickyHeader.tsx +12 -1
  83. package/src/components/table/hooks/usePinnedColumnsManager.ts +3 -2
  84. package/src/components/table/types.ts +20 -0
  85. package/src/components/table/utils/processColumns.tsx +3 -1
  86. package/src/components/tanstackTable/TankstackTable.types.ts +20 -2
  87. package/src/components/tanstackTable/TanstackTable.stories.tsx +8 -6
  88. package/src/components/tanstackTable/TanstackTable.tsx +21 -11
  89. package/src/components/tanstackTable/components/NoResults/NoResults.tsx +9 -3
  90. package/src/components/tanstackTable/components/TablePagination/TablePagination.tsx +4 -1
  91. package/src/index.ts +1 -1
  92. package/src/storybook/formArgTypes.ts +10 -0
  93. package/src/storybookDocs/Permafrost.mdx +8 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indico-data/design-system",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "description": "",
5
5
  "author": "",
6
6
  "main": "lib/index.js",
@@ -79,35 +79,21 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>((props, ref) =>
79
79
  >
80
80
  {/* Loading Icon on the left (default) */}
81
81
  {isLoading && !iconRight && (
82
- <Icon
83
- name="indico-o"
84
- style={{ animation: 'spin 1s linear infinite' }}
85
- ariaLabel="Loading..."
86
- size={iconSize}
87
- />
82
+ <Icon name="indico-o" style={{ animation: 'spin 1s linear infinite' }} size={iconSize} />
88
83
  )}
89
84
 
90
85
  {/* Left Icon */}
91
- {iconLeft && !isLoading && (
92
- <Icon name={iconLeft} ariaLabel={`${iconLeft} Icon`} size={iconSize} />
93
- )}
86
+ {iconLeft && !isLoading && <Icon name={iconLeft} ariaLabel={iconLeft} size={iconSize} />}
94
87
 
95
88
  {/* Button children */}
96
89
  {children}
97
90
 
98
91
  {/* Right Icon */}
99
- {iconRight && !isLoading && (
100
- <Icon name={iconRight} ariaLabel={`${iconRight} Icon`} size={iconSize} />
101
- )}
92
+ {iconRight && !isLoading && <Icon name={iconRight} ariaLabel={iconRight} size={iconSize} />}
102
93
 
103
94
  {/* Loading Icon on the right */}
104
95
  {isLoading && iconRight && (
105
- <Icon
106
- name="indico-o"
107
- style={{ animation: 'spin 1s linear infinite' }}
108
- ariaLabel="Loading..."
109
- size={iconSize}
110
- />
96
+ <Icon name="indico-o" style={{ animation: 'spin 1s linear infinite' }} size={iconSize} />
111
97
  )}
112
98
  </button>
113
99
  );
@@ -52,11 +52,10 @@ describe('Button', () => {
52
52
  </Button>,
53
53
  );
54
54
  const button = screen.getByRole('button');
55
- const loadingIcon = screen.getByLabelText('Loading...');
56
55
 
57
56
  expect(button).toHaveClass('btn--loading');
58
57
  expect(button).toBeDisabled();
59
- expect(loadingIcon).toBeInTheDocument();
58
+ expect(button).toHaveAttribute('aria-busy', 'true');
60
59
  });
61
60
 
62
61
  it('displays the loading icon on the left when isLoading and iconLeft exists and hides the iconLeft', () => {
@@ -65,9 +64,10 @@ describe('Button', () => {
65
64
  Button
66
65
  </Button>,
67
66
  );
68
- const loadingIcon = screen.getByLabelText('Loading...');
69
- const iconLeft = screen.queryByLabelText('check Icon');
67
+ const button = screen.getByRole('button');
68
+ const iconLeft = screen.queryByLabelText('check');
70
69
 
70
+ expect(button).toHaveAttribute('aria-busy', 'true');
71
71
  expect(iconLeft).not.toBeInTheDocument();
72
72
  });
73
73
 
@@ -77,19 +77,22 @@ describe('Button', () => {
77
77
  Button
78
78
  </Button>,
79
79
  );
80
- const loadingIcon = screen.getByLabelText('Loading...');
81
- const iconRight = screen.queryByLabelText('check Icon');
80
+ const button = screen.getByRole('button');
81
+ const iconRight = screen.queryByLabelText('check');
82
82
 
83
+ expect(button).toHaveAttribute('aria-busy', 'true');
83
84
  expect(iconRight).not.toBeInTheDocument();
84
85
  });
85
86
 
86
- it('does not apply a margin to the loading icon when no children are present', () => {
87
- render(<Button isLoading iconLeft="check" onClick={onClick} ariaLabel="btn" />);
88
- const loadingIcon = screen.getByLabelText('Loading...');
89
- expect(loadingIcon).toBeInTheDocument();
87
+ it('sets aria-busy when loading with no children', () => {
88
+ const { rerender } = render(
89
+ <Button isLoading iconLeft="check" onClick={onClick} ariaLabel="btn" />,
90
+ );
91
+ const button = screen.getByRole('button');
92
+ expect(button).toHaveAttribute('aria-busy', 'true');
90
93
 
91
- render(<Button isLoading iconRight="check" onClick={onClick} ariaLabel="btn" />);
92
- expect(loadingIcon).toBeInTheDocument();
94
+ rerender(<Button isLoading iconRight="check" onClick={onClick} ariaLabel="btn" />);
95
+ expect(button).toHaveAttribute('aria-busy', 'true');
93
96
  });
94
97
 
95
98
  it('does not apply the loading class when not loading', () => {
@@ -99,11 +102,10 @@ describe('Button', () => {
99
102
  </Button>,
100
103
  );
101
104
  const button = screen.getByRole('button');
102
- const loadingIcon = screen.queryByLabelText('Loading...');
103
105
 
104
106
  expect(button).not.toHaveClass('btn--loading');
105
107
  expect(button).toBeEnabled();
106
- expect(loadingIcon).not.toBeInTheDocument();
108
+ expect(button).toHaveAttribute('aria-busy', 'false');
107
109
  });
108
110
 
109
111
  it('disables the button when disabled prop is true', () => {
@@ -123,7 +125,7 @@ describe('Button', () => {
123
125
  describe('button with icons', () => {
124
126
  it('renders an icon-only button when only iconLeft is provided', () => {
125
127
  render(<Button iconLeft="check" ariaLabel="icon button" />);
126
- const icon = screen.getByLabelText('check Icon');
128
+ const icon = screen.getByLabelText('check');
127
129
  const button = screen.getByRole('button');
128
130
 
129
131
  expect(icon).toBeInTheDocument();
@@ -133,7 +135,7 @@ describe('Button', () => {
133
135
 
134
136
  it('renders an icon-only button when only iconRight is provided', () => {
135
137
  render(<Button iconRight="check" ariaLabel="icon button" />);
136
- const icon = screen.getByLabelText('check Icon');
138
+ const icon = screen.getByLabelText('check');
137
139
  const button = screen.getByRole('button');
138
140
 
139
141
  expect(icon).toBeInTheDocument();
@@ -147,7 +149,7 @@ describe('Button', () => {
147
149
  Button
148
150
  </Button>,
149
151
  );
150
- const icon = screen.getByLabelText('check Icon');
152
+ const icon = screen.getByLabelText('check');
151
153
  const button = screen.getByRole('button');
152
154
 
153
155
  expect(button).toContainElement(icon);
@@ -159,7 +161,7 @@ describe('Button', () => {
159
161
  Button
160
162
  </Button>,
161
163
  );
162
- const icon = screen.getByLabelText('check Icon');
164
+ const icon = screen.getByLabelText('check');
163
165
  const button = screen.getByRole('button');
164
166
 
165
167
  expect(button).toContainElement(icon);
@@ -171,8 +173,8 @@ describe('Button', () => {
171
173
  Button
172
174
  </Button>,
173
175
  );
174
- const leftIcon = screen.getByLabelText('check Icon');
175
- const rightIcon = screen.getByLabelText('time Icon');
176
+ const leftIcon = screen.getByLabelText('check');
177
+ const rightIcon = screen.getByLabelText('time');
176
178
  const button = screen.getByRole('button');
177
179
 
178
180
  expect(button).toContainElement(leftIcon);
@@ -187,7 +189,7 @@ describe('Button', () => {
187
189
  Button
188
190
  </Button>,
189
191
  );
190
- const icon = screen.getByLabelText('check Icon');
192
+ const icon = screen.getByLabelText('check');
191
193
  expect(icon).toHaveClass('icon--xs');
192
194
  });
193
195
 
@@ -197,7 +199,7 @@ describe('Button', () => {
197
199
  Button
198
200
  </Button>,
199
201
  );
200
- const icon = screen.getByLabelText('check Icon');
202
+ const icon = screen.getByLabelText('check');
201
203
  expect(icon).toHaveClass('icon--sm');
202
204
  });
203
205
 
@@ -207,7 +209,7 @@ describe('Button', () => {
207
209
  Button
208
210
  </Button>,
209
211
  );
210
- const icon = screen.getByLabelText('check Icon');
212
+ const icon = screen.getByLabelText('check');
211
213
  expect(icon).toHaveClass('icon--md');
212
214
  });
213
215
 
@@ -217,7 +219,7 @@ describe('Button', () => {
217
219
  Button
218
220
  </Button>,
219
221
  );
220
- const icon = screen.getByLabelText('check Icon');
222
+ const icon = screen.getByLabelText('check');
221
223
  expect(icon).toHaveClass('icon--md');
222
224
  });
223
225
 
@@ -227,18 +229,18 @@ describe('Button', () => {
227
229
  Button
228
230
  </Button>,
229
231
  );
230
- const icon = screen.getByLabelText('check Icon');
232
+ const icon = screen.getByLabelText('check');
231
233
  expect(icon).toHaveClass('icon--lg');
232
234
  });
233
235
 
234
- it('applies correct icon size to loading icon based on button size', () => {
236
+ it('sets aria-busy when loading regardless of button size', () => {
235
237
  render(
236
238
  <Button size="sm" isLoading ariaLabel="loading button">
237
239
  Button
238
240
  </Button>,
239
241
  );
240
- const loadingIcon = screen.getByLabelText('Loading...');
241
- expect(loadingIcon).toHaveClass('icon--sm');
242
+ const button = screen.getByRole('button');
243
+ expect(button).toHaveAttribute('aria-busy', 'true');
242
244
  });
243
245
  });
244
246
  });
@@ -240,6 +240,16 @@ const meta: Meta = {
240
240
  },
241
241
  },
242
242
  },
243
+ text: {
244
+ control: 'object',
245
+ description: 'Customizable text.',
246
+ table: {
247
+ category: 'i18n Text',
248
+ type: {
249
+ summary: '{ selectTime?: string }',
250
+ },
251
+ },
252
+ },
243
253
  },
244
254
  };
245
255
 
@@ -1,11 +1,15 @@
1
1
  import { DateRange, DayPicker, Mode, OnSelectHandler } from 'react-day-picker';
2
2
  import 'react-day-picker/style.css';
3
- import { DatePickerProps } from './types';
3
+ import { DatePickerProps, DatePickerText } from './types';
4
4
  import { getCommonProps } from './contants';
5
5
  import { TimePicker } from '../../timePicker/TimePicker';
6
6
  import { Col, Row } from '../../../grid';
7
7
  import { useEffect, useRef } from 'react';
8
8
 
9
+ const DEFAULT_TEXT: Required<DatePickerText> = {
10
+ selectTime: 'Select Time',
11
+ };
12
+
9
13
  export const DatePicker = (props: DatePickerProps) => {
10
14
  const {
11
15
  mode = 'single',
@@ -39,9 +43,12 @@ export const DatePicker = (props: DatePickerProps) => {
39
43
  ref,
40
44
  timeTabIndex,
41
45
  dateTabIndex,
46
+ text: textProp,
42
47
  ...rest
43
48
  } = props;
44
49
 
50
+ const text = { ...DEFAULT_TEXT, ...textProp };
51
+
45
52
  const futureDateByYear = (year: number) => new Date(new Date().getFullYear() + year, 11, 31);
46
53
  const endMonthDefault = endMonth ?? futureDateByYear(5);
47
54
 
@@ -116,7 +123,7 @@ export const DatePicker = (props: DatePickerProps) => {
116
123
  <div className="time-picker-wrapper">
117
124
  <Row align="center">
118
125
  <Col xs="content">
119
- <p className="ma-0">Select Time</p>
126
+ <p className="ma-0">{text.selectTime}</p>
120
127
  </Col>
121
128
  <Col>
122
129
  <TimePicker
@@ -9,6 +9,12 @@ import {
9
9
  OnSelectHandler,
10
10
  } from 'react-day-picker';
11
11
 
12
+ /** Customizable text for DatePicker. */
13
+ export interface DatePickerText {
14
+ /** Label for "Select Time" text. Default: "Select Time" */
15
+ selectTime?: string;
16
+ }
17
+
12
18
  export interface DatePickerProps {
13
19
  ref?: React.LegacyRef<HTMLInputElement>;
14
20
  /** The selected day(s). */
@@ -51,6 +57,8 @@ export interface DatePickerProps {
51
57
  isReadOnly?: boolean;
52
58
  timeTabIndex?: number;
53
59
  dateTabIndex?: number;
60
+ /** Customizable display text. */
61
+ text?: DatePickerText;
54
62
  }
55
63
 
56
64
  export interface CommonProps {
@@ -175,6 +175,16 @@ const meta: Meta<typeof IconTriggerDatePicker> = {
175
175
  },
176
176
  action: 'onSelect',
177
177
  },
178
+ text: {
179
+ control: 'object',
180
+ description: 'Customizable text.',
181
+ table: {
182
+ category: 'i18n Text',
183
+ type: {
184
+ summary: '{ triggerIcon?: string }',
185
+ },
186
+ },
187
+ },
178
188
  },
179
189
  };
180
190
 
@@ -6,6 +6,12 @@ import { DatePicker } from '../datePicker/DatePicker';
6
6
  import { Icon } from '../../../icons/Icon';
7
7
  import { UseFloatingOptions } from '@floating-ui/react-dom';
8
8
  import { PortalOptions } from '../datePicker/types';
9
+ import { IconTriggerDatePickerText } from './types';
10
+
11
+ const DEFAULT_TEXT: Required<IconTriggerDatePickerText> = {
12
+ triggerIcon: 'Open date picker',
13
+ };
14
+
9
15
  interface Props {
10
16
  /** Allows you to select a single day, a range of days, or multiple days. */
11
17
  mode?: Mode;
@@ -42,6 +48,8 @@ interface Props {
42
48
  isPortal?: boolean;
43
49
  /** The floating options for the date picker. Follows floating-ui options. */
44
50
  floatingOptions?: UseFloatingOptions;
51
+ /** Customizable display text. */
52
+ text?: IconTriggerDatePickerText;
45
53
  }
46
54
 
47
55
  export const IconTriggerDatePicker = (props: Props) => {
@@ -64,9 +72,12 @@ export const IconTriggerDatePicker = (props: Props) => {
64
72
  portalOptions,
65
73
  floatingOptions,
66
74
  isPortal,
75
+ text: textProp,
67
76
  ...rest
68
77
  } = props;
69
78
 
79
+ const text = { ...DEFAULT_TEXT, ...textProp };
80
+
70
81
  const [localMonth, setLocalMonth] = useState<Date>(initialMonth ?? new Date());
71
82
 
72
83
  const handleSelect: OnSelectHandler<Date> = (date) => {
@@ -93,7 +104,7 @@ export const IconTriggerDatePicker = (props: Props) => {
93
104
  floatingOptions={floatingOptions}
94
105
  >
95
106
  <Icon
96
- aria-label="Open date picker"
107
+ aria-label={text.triggerIcon}
97
108
  name={triggerIcon}
98
109
  size={triggerIconSize}
99
110
  className="date__picker__trigger"
@@ -0,0 +1,5 @@
1
+ /** Customizable text for IconTriggerDatePicker. */
2
+ export interface IconTriggerDatePickerText {
3
+ /** Aria label for the trigger icon. Default: "Open date picker" */
4
+ triggerIcon?: string;
5
+ }
@@ -16,7 +16,8 @@ export interface SingleInputDatePickerProps {
16
16
  /** The layout of the caption. Enables you to add or remove the dropdown navigation for months/years */
17
17
  captionLayout?: 'dropdown' | 'dropdown-months' | 'dropdown-years' | 'label';
18
18
  id?: string;
19
- label?: string;
19
+ /** The label for the input field. */
20
+ label: string;
20
21
  onSelect: (selected: Date | undefined) => void;
21
22
  initialMonth?: Date;
22
23
  selected?: Date;
@@ -131,9 +132,9 @@ export function SingleInputDatePicker(props: SingleInputDatePickerProps) {
131
132
  isClearable={isClearable}
132
133
  onChange={handleInputChange}
133
134
  errorMessage={errorMessage}
134
- label={'Single Date Picker'}
135
+ label={label}
135
136
  tabIndex={tabIndex}
136
- name={'Date Picker'}
137
+ name={label}
137
138
  ref={ref}
138
139
  readonly={isReadOnly}
139
140
  />
@@ -30,6 +30,7 @@ describe('SingleInputDatePicker', () => {
30
30
  render(
31
31
  <SingleInputDatePicker
32
32
  ariaLabel="Single Input Date Picker"
33
+ label="Date Picker"
33
34
  data-testid="datepicker-testid"
34
35
  captionLayout="label"
35
36
  onSelect={mockOnSelect}
@@ -70,6 +71,7 @@ describe('SingleInputDatePicker', () => {
70
71
  render(
71
72
  <SingleInputDatePicker
72
73
  ariaLabel="Single Input Date Picker"
74
+ label="Date Picker"
73
75
  captionLayout="label"
74
76
  data-testid="datepicker-testid"
75
77
  onSelect={mockOnSelect}
@@ -261,6 +261,16 @@ const meta: Meta<typeof InputDateRangePicker> = {
261
261
  disable: true,
262
262
  },
263
263
  },
264
+ text: {
265
+ control: 'object',
266
+ description: 'Customizable text.',
267
+ table: {
268
+ category: 'i18n Text',
269
+ type: {
270
+ summary: '{ fromDate?: string; toDate?: string }',
271
+ },
272
+ },
273
+ },
264
274
  },
265
275
  };
266
276
 
@@ -10,6 +10,13 @@ import { formatDateAsString } from '../inputDatePicker/helpers';
10
10
  import { Button } from '../../../button';
11
11
  import { UseFloatingOptions } from '@floating-ui/react-dom';
12
12
  import { PortalOptions } from '../datePicker/types';
13
+ import { InputDateRangePickerText } from './types';
14
+
15
+ const DEFAULT_TEXT: Required<InputDateRangePickerText> = {
16
+ fromDate: 'From Date',
17
+ toDate: 'To Date',
18
+ };
19
+
13
20
  interface InputDateRangePickerProps {
14
21
  /** A label for assistive technologies. */
15
22
  ariaLabel: string;
@@ -51,6 +58,8 @@ interface InputDateRangePickerProps {
51
58
  floatingOptions?: UseFloatingOptions;
52
59
  /** Whether the date picker is a portal. */
53
60
  isPortal?: boolean;
61
+ /** Customizable display text. */
62
+ text?: InputDateRangePickerText;
54
63
  }
55
64
 
56
65
  export function InputDateRangePicker(props: InputDateRangePickerProps) {
@@ -85,9 +94,12 @@ export function InputDateRangePicker(props: InputDateRangePickerProps) {
85
94
  portalOptions,
86
95
  floatingOptions,
87
96
  isPortal,
97
+ text: textProp,
88
98
  ...rest
89
99
  } = props;
90
100
 
101
+ const text = { ...DEFAULT_TEXT, ...textProp };
102
+
91
103
  const inputId = useId();
92
104
 
93
105
  // Hold the input values in state
@@ -183,7 +195,7 @@ export function InputDateRangePicker(props: InputDateRangePickerProps) {
183
195
  onChange={(e) => handleInputChange(e, 'from')}
184
196
  errorMessage={fromErrorMessage}
185
197
  label={fromLabel}
186
- name={'From Date'}
198
+ name={text.fromDate}
187
199
  data-testid="date-picker-from"
188
200
  hasHiddenLabel={hasHiddenLabel}
189
201
  ref={ref}
@@ -201,7 +213,7 @@ export function InputDateRangePicker(props: InputDateRangePickerProps) {
201
213
  onChange={(e) => handleInputChange(e, 'to')}
202
214
  errorMessage={toErrorMessage}
203
215
  label={toLabel}
204
- name={'To Date'}
216
+ name={text.toDate}
205
217
  data-testid="date-picker-to"
206
218
  hasHiddenLabel={hasHiddenLabel}
207
219
  ref={ref}
@@ -0,0 +1,7 @@
1
+ /** Customizable text for InputDateRangePicker. */
2
+ export interface InputDateRangePickerText {
3
+ /** Label for the "from" date input. Default: "From Date" */
4
+ fromDate?: string;
5
+ /** Label for the "to" date input. Default: "To Date" */
6
+ toDate?: string;
7
+ }
@@ -18,7 +18,8 @@ export interface SingleInputDateTimePickerProps {
18
18
  isDisabled?: boolean;
19
19
  captionLayout?: 'dropdown' | 'dropdown-months' | 'dropdown-years' | 'label';
20
20
  id?: string;
21
- label?: string;
21
+ /** The label for the date input field. */
22
+ label: string;
22
23
  onSelect: (selected: Date | undefined) => void;
23
24
  initialMonth?: Date;
24
25
  selected?: Date;
@@ -153,7 +154,7 @@ export function SingleInputDateTimePicker(props: SingleInputDateTimePickerProps)
153
154
  onChange={handleInputChange}
154
155
  errorMessage={errorMessage}
155
156
  hasHiddenLabel={hasHiddenLabel}
156
- label={'Single Date Picker'}
157
+ label={label}
157
158
  name={`${id}-date-picker`}
158
159
  tabIndex={dateTabIndex}
159
160
  />
@@ -46,6 +46,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
46
46
  onBlur,
47
47
  onKeyDown,
48
48
  className,
49
+ text: _text,
49
50
  ...rest
50
51
  },
51
52
  ref,
@@ -31,6 +31,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
31
31
  helpText,
32
32
  iconName,
33
33
  className,
34
+ text: _text,
34
35
  ...rest
35
36
  },
36
37
  ref,
@@ -32,6 +32,7 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
32
32
  errorMessage,
33
33
  helpText,
34
34
  hasShowPassword = true,
35
+ text: _text,
35
36
  ...rest
36
37
  },
37
38
  ref,
@@ -55,7 +56,12 @@ const PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(
55
56
  return (
56
57
  <>
57
58
  <div className="password-input-wrapper">
58
- <Icon name="lock" data-testid={`${name}-embedded-icon`} className="embedded-icon" size="sm" />
59
+ <Icon
60
+ name="lock"
61
+ data-testid={`${name}-embedded-icon`}
62
+ className="embedded-icon"
63
+ size="sm"
64
+ />
59
65
  <input
60
66
  ref={ref}
61
67
  data-testid={`form-password-input-${name}`}
@@ -87,15 +87,16 @@ const meta: Meta<SelectProps<SelectOption> & LabelProps> = {
87
87
  },
88
88
  defaultValue: { summary: 'true' },
89
89
  },
90
- placeholder: {
91
- control: 'text',
90
+ text: {
91
+ control: 'object',
92
+ description: 'Customizable text.',
92
93
  table: {
93
- category: 'Props',
94
+ category: 'i18n Text',
94
95
  type: {
95
- summary: 'string',
96
+ summary:
97
+ '{ placeholder?: string; noOptions?: string; loading?: string; required?: string }',
96
98
  },
97
99
  },
98
- defaultValue: { summary: 'Select an option...' },
99
100
  },
100
101
  className: {
101
102
  control: 'text',
@@ -1,9 +1,16 @@
1
1
  import React from 'react';
2
2
  import classNames from 'classnames';
3
3
  import ReactSelect, { Props as ReactSelectProps, components, OptionProps } from 'react-select';
4
- import { SelectOption } from './types';
4
+ import { SelectOption, SelectText } from './types';
5
5
  import { withLabel } from '../subcomponents/Label';
6
6
 
7
+ const DEFAULT_TEXT: Required<SelectText> = {
8
+ placeholder: 'Select...',
9
+ noOptions: 'No options',
10
+ loading: 'Loading...',
11
+ required: '(required)',
12
+ };
13
+
7
14
  export interface SelectProps<OptionType extends SelectOption> extends ReactSelectProps<OptionType> {
8
15
  /** Options for the select component */
9
16
  options: OptionType[];
@@ -19,6 +26,8 @@ export interface SelectProps<OptionType extends SelectOption> extends ReactSelec
19
26
  classNamePrefix?: string;
20
27
  /** Event handler for when the selected value changes */
21
28
  onChange?: (newValue: any, actionMeta: any) => void;
29
+ /** Customizable display text. */
30
+ text?: SelectText;
22
31
  }
23
32
 
24
33
  const OptionComponent = <OptionType extends SelectOption>({
@@ -43,10 +52,12 @@ const Select = React.forwardRef(
43
52
  label,
44
53
  hasHiddenLabel,
45
54
  name,
55
+ text: textProp,
46
56
  ...props
47
57
  }: SelectProps<OptionType>,
48
58
  ref: React.Ref<any>,
49
59
  ) => {
60
+ const text = { ...DEFAULT_TEXT, ...textProp };
50
61
  const defaultComponents = {
51
62
  Option: OptionComponent as React.ComponentType<OptionProps<OptionType>>,
52
63
  };
@@ -59,6 +70,9 @@ const Select = React.forwardRef(
59
70
  classNamePrefix={classNamePrefix}
60
71
  className={classNames('select-wrapper', className)}
61
72
  components={mergedComponents}
73
+ placeholder={text.placeholder}
74
+ noOptionsMessage={() => text.noOptions}
75
+ loadingMessage={() => text.loading}
62
76
  {...props}
63
77
  />
64
78
  );
@@ -1,4 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
+ import { FormLabelText } from '../subcomponents/Label';
2
3
 
3
4
  export type SelectOption = {
4
5
  label: string;
@@ -6,3 +7,13 @@ export type SelectOption = {
6
7
  detail?: ReactNode;
7
8
  [key: string]: any; // Allow for additional properties
8
9
  };
10
+
11
+ /** Customizable text for Select. Extends FormLabelText for label-related text. */
12
+ export interface SelectText extends FormLabelText {
13
+ /** Placeholder text when no option is selected. Default: "Select..." */
14
+ placeholder?: string;
15
+ /** Text when there are no options. Default: "No options" */
16
+ noOptions?: string;
17
+ /** Text while loading options. Default: "Loading..." */
18
+ loading?: string;
19
+ }