uikit-react-public 0.44.0 → 0.44.3

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 (32) hide show
  1. package/dist/components/Calendar/Calendar.d.ts +1 -1
  2. package/dist/components/Calendar/Calendar.stories.d.ts +6 -1
  3. package/dist/components/Calendar/Calendar.types.d.ts +9 -0
  4. package/dist/components/Calendar/index.d.ts +1 -1
  5. package/dist/components/Calendar/subcomponents/Day.d.ts +4 -2
  6. package/dist/components/Calendar/subcomponents/Day.stories.d.ts +1 -1
  7. package/dist/components/Calendar/subcomponents/EventDot.d.ts +3 -1
  8. package/dist/components/Calendar/subcomponents/Grid.d.ts +3 -2
  9. package/dist/components/Calendar/subcomponents/__tests__/Day.test.d.ts +1 -0
  10. package/dist/components/Calendar/subcomponents/__tests__/EventDot.test.d.ts +1 -0
  11. package/dist/components/Datepicker/Datepicker.stories.d.ts +5 -0
  12. package/dist/components/Datepicker/Datepicker.types.d.ts +3 -1
  13. package/dist/components/Datepicker/subcomponents/CustomDatepicker.d.ts +4 -2
  14. package/dist/components/index.d.ts +1 -1
  15. package/dist/index.js +1805 -1751
  16. package/lib/components/Calendar/Calendar.stories.tsx +60 -12
  17. package/lib/components/Calendar/Calendar.tsx +27 -20
  18. package/lib/components/Calendar/Calendar.types.ts +17 -1
  19. package/lib/components/Calendar/__tests__/Calendar.test.tsx +76 -2
  20. package/lib/components/Calendar/__tests__/__snapshots__/Calendar.test.tsx.snap +462 -231
  21. package/lib/components/Calendar/index.ts +6 -1
  22. package/lib/components/Calendar/subcomponents/Day.tsx +44 -5
  23. package/lib/components/Calendar/subcomponents/EventDot.tsx +42 -6
  24. package/lib/components/Calendar/subcomponents/Grid.tsx +4 -1
  25. package/lib/components/Calendar/subcomponents/__tests__/Day.test.tsx +139 -0
  26. package/lib/components/Calendar/subcomponents/__tests__/EventDot.test.tsx +78 -0
  27. package/lib/components/Datepicker/Datepicker.stories.tsx +55 -24
  28. package/lib/components/Datepicker/Datepicker.types.ts +7 -1
  29. package/lib/components/Datepicker/__tests__/Datepicker.test.tsx +90 -0
  30. package/lib/components/Datepicker/subcomponents/CustomDatepicker.tsx +16 -1
  31. package/lib/components/index.ts +1 -1
  32. package/package.json +1 -1
@@ -107,6 +107,55 @@ describe('Datepicker', () => {
107
107
  );
108
108
  });
109
109
 
110
+ test('Does not submit an enclosing form on ENTER key press', async () => {
111
+ const onValueChange = vi.fn();
112
+ const onSubmit = vi.fn((event: React.FormEvent) => event.preventDefault());
113
+ const user = userEvent.setup();
114
+ customRender(
115
+ <form onSubmit={onSubmit}>
116
+ <Datepicker onValueChange={onValueChange} />
117
+ </form>
118
+ );
119
+
120
+ // Internal <input> (the user types into)
121
+ const datepickerInput = screen.getByRole('textbox');
122
+ await user.type(datepickerInput, '10/03/2025');
123
+ expect(datepickerInput).toHaveValue('10/03/2025');
124
+
125
+ // Trigger user-entered date parsing
126
+ await user.keyboard('{Enter}');
127
+
128
+ // The date is still parsed as normal...
129
+ expect(onValueChange).toHaveBeenCalledWith(
130
+ new Date('2025-03-10'),
131
+ expect.anything() // 2nd parameter is the event object (which we're not testing here)
132
+ );
133
+ // ...but the enclosing form is not submitted
134
+ expect(onSubmit).not.toHaveBeenCalled();
135
+ });
136
+
137
+ test('Does not bubble {Enter} key press to a parent element', async () => {
138
+ const onValueChange = vi.fn();
139
+ const parentKeyDown = vi.fn();
140
+ const user = userEvent.setup();
141
+ customRender(
142
+ <div onKeyDown={parentKeyDown}>
143
+ <Datepicker onValueChange={onValueChange} />
144
+ </div>
145
+ );
146
+
147
+ const datepickerInput = screen.getByRole('textbox');
148
+ await user.type(datepickerInput, '10/03/2025');
149
+ // Typing the digits/slashes above also fires keydown events that
150
+ // correctly bubble (only Enter is stopped, to prevent it submitting an
151
+ // enclosing form) - clear those out so only Enter is captured below.
152
+ parentKeyDown.mockClear();
153
+
154
+ await user.keyboard('{Enter}');
155
+
156
+ expect(parentKeyDown).not.toHaveBeenCalled();
157
+ });
158
+
110
159
  test('Input value reflects the date', () => {
111
160
  // 10th March 2025 (2 is March, because month is 0-indexed in Date constructor)
112
161
  customRender(<Datepicker value={new Date(2025, 2, 10)} />);
@@ -153,6 +202,21 @@ describe('Datepicker', () => {
153
202
  expect(panel.parentElement).toHaveClass('ucl-overlay');
154
203
  });
155
204
 
205
+ test('Forwards pickedMonth to the Calendar panel without affecting the input value', () => {
206
+ customRender(<Datepicker pickedMonth={new Date('2026-07-01')} />);
207
+
208
+ const input = screen.getByTestId('ucl-uikit-datepicker__input');
209
+ fireEvent.focus(input);
210
+
211
+ expect(screen.getByRole('combobox', { name: 'Month' })).toHaveTextContent(
212
+ 'July'
213
+ );
214
+ expect(screen.getByRole('combobox', { name: 'Year' })).toHaveTextContent(
215
+ '2026'
216
+ );
217
+ expect(input).toHaveValue('');
218
+ });
219
+
156
220
  test('Panel escapes a containing dialog via fixed positioning', () => {
157
221
  customRender(
158
222
  <dialog open>
@@ -383,6 +447,32 @@ describe('Datepicker', () => {
383
447
  expect(screen.getByRole('button', { name: 'After' })).toHaveFocus();
384
448
  });
385
449
 
450
+ test('Forwards the statuses prop to the underlying Calendar', () => {
451
+ customRender(
452
+ <Datepicker
453
+ value={new Date(2026, 2, 10)} // 10th March 2026 (2 is March, 0-indexed)
454
+ events={[{ date: '2026-03-10', status: 'pending' }]}
455
+ statuses={{
456
+ pending: { variant: 'warning', ariaLabel: 'Pending' },
457
+ }}
458
+ />
459
+ );
460
+
461
+ const openCalendarButton = screen.getByTestId(
462
+ 'ucl-uikit-datepicker__visible-field-icon-button'
463
+ );
464
+ fireEvent.click(openCalendarButton);
465
+
466
+ const days = screen.getAllByTestId('ucl-uikit-calendar__day');
467
+ // Pick 10th March 2026: 6 days of previous month + 10 days of current month - 1 for 0-indexing
468
+ const dateArrayIndex = 6 + 10 - 1;
469
+
470
+ expect(days[dateArrayIndex]).toHaveAttribute(
471
+ 'aria-label',
472
+ expect.stringContaining('Pending')
473
+ );
474
+ });
475
+
386
476
  test('Disabled Datepicker icon control is not tabbable and cannot open', async () => {
387
477
  const user = userEvent.setup();
388
478
 
@@ -7,7 +7,11 @@ import { parseInputValue } from '../utils';
7
7
  import announce from '../../../utils/announce';
8
8
  import { useTheme } from '../../../theme';
9
9
  import type { DatepickerValue } from '../Datepicker.types';
10
- import type { CalendarEvent, AcademicWeek } from '../../Calendar';
10
+ import type {
11
+ CalendarEvent,
12
+ CalendarStatuses,
13
+ AcademicWeek,
14
+ } from '../../Calendar';
11
15
  import type { InputProps } from './DatepickerInput';
12
16
 
13
17
  interface CustomDatepickerProps extends React.HTMLAttributes<HTMLDivElement> {
@@ -20,9 +24,11 @@ interface CustomDatepickerProps extends React.HTMLAttributes<HTMLDivElement> {
20
24
  maxDate?: string | null; // ISO date string: YYYY-MM-DD
21
25
  minYear?: number;
22
26
  maxYear?: number;
27
+ pickedMonth?: Date | null; // Pre-selects a month to display when no value is set
23
28
  disabled?: boolean;
24
29
  clearable?: boolean;
25
30
  events?: CalendarEvent[];
31
+ statuses?: CalendarStatuses;
26
32
  showAcademicWeeks?: boolean;
27
33
  academicWeeks?: AcademicWeek[];
28
34
  testId?: string;
@@ -40,9 +46,11 @@ const CustomDatepicker = ({
40
46
  maxDate,
41
47
  minYear,
42
48
  maxYear,
49
+ pickedMonth = null,
43
50
  disabled = false,
44
51
  clearable = false,
45
52
  events,
53
+ statuses,
46
54
  showAcademicWeeks,
47
55
  academicWeeks = [],
48
56
  testId = NAME,
@@ -116,6 +124,11 @@ const CustomDatepicker = ({
116
124
 
117
125
  const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
118
126
  if (event.key === 'Enter') {
127
+ // Stop the native "Enter submits the enclosing form" behaviour.
128
+ event.preventDefault();
129
+ // Key events must not bubble out of the datepicker (e.g. so a parent
130
+ // form's own key handlers don't also see this Enter).
131
+ event.stopPropagation();
119
132
  handleParseInput(event);
120
133
  } else if (event.key === 'Escape') {
121
134
  resetField();
@@ -260,12 +273,14 @@ const CustomDatepicker = ({
260
273
  <Panel>
261
274
  <Calendar
262
275
  pickedDate={value}
276
+ pickedMonth={pickedMonth}
263
277
  onDatePick={handlePickCalendarDate}
264
278
  minDate={minDate}
265
279
  maxDate={maxDate}
266
280
  minYear={minYear}
267
281
  maxYear={maxYear}
268
282
  events={events}
283
+ statuses={statuses}
269
284
  showAcademicWeeks={showAcademicWeeks}
270
285
  academicWeeks={academicWeeks}
271
286
  />
@@ -211,7 +211,7 @@ export { default as NativeDatepicker } from './NativeDatepicker';
211
211
  export type { NativeDatepickerProps } from './NativeDatepicker';
212
212
 
213
213
  export { default as Calendar } from './Calendar';
214
- export type { CalendarProps } from './Calendar';
214
+ export type { CalendarProps, CalendarStatuses } from './Calendar';
215
215
 
216
216
  export { default as WeekPicker } from './WeekPicker';
217
217
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uikit-react-public",
3
3
  "private": false,
4
4
  "license": "UNLICENSED",
5
- "version": "0.44.0",
5
+ "version": "0.44.3",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",