uikit-react-public 0.11.16 → 0.11.24

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 (142) hide show
  1. package/dist/components/Calendar/Calendar.d.ts +3 -0
  2. package/dist/components/Calendar/Calendar.stories.d.ts +42 -0
  3. package/dist/components/Calendar/Calendar.types.d.ts +18 -0
  4. package/dist/components/Calendar/index.d.ts +2 -0
  5. package/dist/components/Calendar/subcomponents/AcademicWeek.d.ts +5 -0
  6. package/dist/components/Calendar/subcomponents/AcademicWeeks.d.ts +7 -0
  7. package/dist/components/Calendar/subcomponents/ColumnHeading.d.ts +7 -0
  8. package/dist/components/Calendar/subcomponents/Controls.d.ts +6 -0
  9. package/dist/components/Calendar/subcomponents/Day.d.ts +12 -0
  10. package/dist/components/{Datepicker/subcomponents/Day → Calendar/subcomponents}/Day.stories.d.ts +9 -1
  11. package/dist/components/Calendar/subcomponents/EventDot.d.ts +6 -0
  12. package/dist/components/Calendar/subcomponents/Grid.d.ts +11 -0
  13. package/dist/components/Calendar/subcomponents/index.d.ts +7 -0
  14. package/dist/components/Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.d.ts +24 -0
  15. package/dist/components/Calendar/utils/index.d.ts +4 -0
  16. package/dist/components/Calendar/utils/normaliseMonth/normaliseMonth.d.ts +9 -0
  17. package/dist/components/Calendar/utils/normaliseMonth/normaliseMonth.test.d.ts +1 -0
  18. package/dist/components/Calendar/utils/parseDateFromString/parseDateFromString.d.ts +9 -0
  19. package/dist/components/Calendar/utils/parseDateFromString/parseDateFromString.test.d.ts +1 -0
  20. package/dist/components/Datepicker/Datepicker.d.ts +3 -12
  21. package/dist/components/Datepicker/Datepicker.stories.d.ts +16 -3
  22. package/dist/components/Datepicker/Datepicker.types.d.ts +23 -0
  23. package/dist/components/Datepicker/index.d.ts +1 -1
  24. package/dist/components/Datepicker/subcomponents/CustomDatepicker.d.ts +17 -0
  25. package/dist/components/Datepicker/subcomponents/DatepickerInput.d.ts +10 -0
  26. package/dist/components/Datepicker/subcomponents/NativeDatepicker.d.ts +6 -0
  27. package/dist/components/Datepicker/subcomponents/Panel.d.ts +6 -0
  28. package/dist/components/Datepicker/subcomponents/VisibleField.d.ts +12 -0
  29. package/dist/components/Datepicker/subcomponents/index.d.ts +5 -7
  30. package/dist/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.d.ts +17 -0
  31. package/dist/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.d.ts +1 -0
  32. package/dist/components/Datepicker/utils/index.d.ts +2 -2
  33. package/dist/components/Datepicker/utils/parseInputValue/parseInputValue.d.ts +11 -0
  34. package/dist/components/Datepicker/utils/parseInputValue/parseInputValue.test.d.ts +1 -0
  35. package/dist/components/Footer/Footer.d.ts +1 -1
  36. package/dist/components/Header/Header.d.ts +5 -4
  37. package/dist/components/Header/index.d.ts +1 -1
  38. package/dist/components/Menu/Menu.d.ts +2 -1
  39. package/dist/components/Menu/MenuContent.d.ts +1 -0
  40. package/dist/components/Select/Select.stories.d.ts +1 -1
  41. package/dist/components/Select/Select.types.d.ts +10 -50
  42. package/dist/components/Select/index.d.ts +1 -1
  43. package/dist/components/Select/subcomponents/CustomSelect.d.ts +2 -1
  44. package/dist/components/index.d.ts +2 -0
  45. package/dist/index.js +3332 -3063
  46. package/lib/components/Calendar/Calendar.stories.tsx +209 -0
  47. package/lib/components/Calendar/Calendar.tsx +121 -0
  48. package/lib/components/Calendar/Calendar.types.ts +21 -0
  49. package/lib/components/Calendar/__tests__/Calendar.test.tsx +71 -0
  50. package/lib/components/Calendar/__tests__/__snapshots__/Calendar.test.tsx.snap +1218 -0
  51. package/lib/components/Calendar/index.ts +6 -0
  52. package/lib/components/Calendar/subcomponents/AcademicWeek.tsx +36 -0
  53. package/lib/components/Calendar/subcomponents/AcademicWeeks.tsx +46 -0
  54. package/lib/components/Calendar/subcomponents/ColumnHeading.tsx +40 -0
  55. package/lib/components/{Datepicker/subcomponents/MonthSelector/MonthSelector.tsx → Calendar/subcomponents/Controls.tsx} +17 -17
  56. package/lib/components/{Datepicker/subcomponents/Day → Calendar/subcomponents}/Day.stories.tsx +30 -7
  57. package/lib/components/Calendar/subcomponents/Day.tsx +130 -0
  58. package/lib/components/Calendar/subcomponents/EventDot.tsx +40 -0
  59. package/lib/components/Calendar/subcomponents/Grid.tsx +117 -0
  60. package/lib/components/Calendar/subcomponents/index.ts +7 -0
  61. package/lib/components/Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.test.ts +104 -0
  62. package/lib/components/Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.ts +85 -0
  63. package/lib/components/{Datepicker → Calendar}/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.test.ts +29 -65
  64. package/lib/components/{Datepicker → Calendar}/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.ts +11 -43
  65. package/lib/components/Calendar/utils/index.ts +4 -0
  66. package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.test.ts +40 -0
  67. package/lib/components/Calendar/utils/normaliseMonth/normaliseMonth.ts +16 -0
  68. package/lib/components/Calendar/utils/parseDateFromString/parseDateFromString.test.ts +15 -0
  69. package/lib/components/Calendar/utils/parseDateFromString/parseDateFromString.ts +19 -0
  70. package/lib/components/Datepicker/Datepicker.stories.tsx +220 -23
  71. package/lib/components/Datepicker/Datepicker.tsx +34 -137
  72. package/lib/components/Datepicker/Datepicker.types.ts +38 -0
  73. package/lib/components/Datepicker/__tests__/Datepicker.test.tsx +53 -112
  74. package/lib/components/Datepicker/__tests__/__snapshots__/Datepicker.test.tsx.snap +92 -638
  75. package/lib/components/Datepicker/index.ts +1 -1
  76. package/lib/components/Datepicker/subcomponents/CustomDatepicker.tsx +209 -0
  77. package/lib/components/Datepicker/subcomponents/DatepickerInput.tsx +74 -0
  78. package/lib/components/Datepicker/subcomponents/NativeDatepicker.tsx +70 -0
  79. package/lib/components/Datepicker/subcomponents/Panel.tsx +32 -0
  80. package/lib/components/Datepicker/subcomponents/VisibleField.tsx +104 -0
  81. package/lib/components/Datepicker/subcomponents/index.ts +5 -7
  82. package/lib/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.test.ts +32 -0
  83. package/lib/components/Datepicker/utils/dateToLocaleISOString/dateToLocaleISOString.ts +23 -0
  84. package/lib/components/Datepicker/utils/index.ts +2 -2
  85. package/lib/components/Datepicker/utils/parseInputValue/parseInputValue.test.ts +110 -0
  86. package/lib/components/Datepicker/utils/parseInputValue/parseInputValue.ts +57 -0
  87. package/lib/components/Footer/Footer.tsx +3 -3
  88. package/lib/components/Footer/__tests__/__snapshots__/Footer.test.tsx.snap +6 -6
  89. package/lib/components/Header/Header.tsx +32 -33
  90. package/lib/components/Header/HeaderMenu.tsx +9 -2
  91. package/lib/components/Header/__tests__/__snapshots__/Header.test.tsx.snap +40 -48
  92. package/lib/components/Header/index.ts +5 -1
  93. package/lib/components/Menu/Menu.tsx +3 -0
  94. package/lib/components/Menu/MenuContent.tsx +4 -1
  95. package/lib/components/Select/Select.stories.tsx +38 -39
  96. package/lib/components/Select/Select.tsx +4 -18
  97. package/lib/components/Select/Select.types.ts +30 -69
  98. package/lib/components/Select/__tests__/Select.test.tsx +6 -6
  99. package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +1 -1
  100. package/lib/components/Select/index.ts +1 -1
  101. package/lib/components/Select/subcomponents/CustomSelect.tsx +22 -12
  102. package/lib/components/Select/subcomponents/NativeSelect.tsx +7 -3
  103. package/lib/components/Select/subcomponents/Panel.tsx +4 -4
  104. package/lib/components/Select/subcomponents/VisibleField.tsx +1 -1
  105. package/lib/components/index.ts +3 -0
  106. package/package.json +4 -4
  107. package/LICENSE +0 -9
  108. package/dist/components/Datepicker/subcomponents/CalendarGrid/CalendarGrid.d.ts +0 -6
  109. package/dist/components/Datepicker/subcomponents/CalendarGrid/index.d.ts +0 -1
  110. package/dist/components/Datepicker/subcomponents/CalendarMenu/CalendarMenu.d.ts +0 -8
  111. package/dist/components/Datepicker/subcomponents/CalendarMenu/index.d.ts +0 -1
  112. package/dist/components/Datepicker/subcomponents/ColumnHeadings/ColumnHeadings.d.ts +0 -2
  113. package/dist/components/Datepicker/subcomponents/ColumnHeadings/index.d.ts +0 -1
  114. package/dist/components/Datepicker/subcomponents/DateField/DateField.d.ts +0 -7
  115. package/dist/components/Datepicker/subcomponents/DateField/index.d.ts +0 -1
  116. package/dist/components/Datepicker/subcomponents/Day/Day.d.ts +0 -10
  117. package/dist/components/Datepicker/subcomponents/Day/index.d.ts +0 -1
  118. package/dist/components/Datepicker/subcomponents/MonthSelector/MonthSelector.d.ts +0 -6
  119. package/dist/components/Datepicker/subcomponents/MonthSelector/index.d.ts +0 -1
  120. package/dist/components/Datepicker/subcomponents/Native/Native.d.ts +0 -9
  121. package/dist/components/Datepicker/subcomponents/Native/index.d.ts +0 -1
  122. package/dist/components/Datepicker/utils/parseDateForDateField/parseDateForDateField.d.ts +0 -20
  123. package/lib/components/Datepicker/subcomponents/CalendarGrid/CalendarGrid.tsx +0 -59
  124. package/lib/components/Datepicker/subcomponents/CalendarGrid/index.ts +0 -1
  125. package/lib/components/Datepicker/subcomponents/CalendarMenu/CalendarMenu.tsx +0 -64
  126. package/lib/components/Datepicker/subcomponents/CalendarMenu/index.ts +0 -1
  127. package/lib/components/Datepicker/subcomponents/ColumnHeadings/ColumnHeadings.tsx +0 -35
  128. package/lib/components/Datepicker/subcomponents/ColumnHeadings/index.ts +0 -1
  129. package/lib/components/Datepicker/subcomponents/DateField/DateField.tsx +0 -155
  130. package/lib/components/Datepicker/subcomponents/DateField/__tests__/DateField.test.tsx +0 -191
  131. package/lib/components/Datepicker/subcomponents/DateField/index.ts +0 -1
  132. package/lib/components/Datepicker/subcomponents/Day/Day.tsx +0 -94
  133. package/lib/components/Datepicker/subcomponents/Day/index.ts +0 -1
  134. package/lib/components/Datepicker/subcomponents/MonthSelector/index.ts +0 -1
  135. package/lib/components/Datepicker/subcomponents/Native/Native.tsx +0 -59
  136. package/lib/components/Datepicker/subcomponents/Native/index.ts +0 -1
  137. package/lib/components/Datepicker/utils/parseDateForDateField/parseDateForDateField.test.ts +0 -41
  138. package/lib/components/Datepicker/utils/parseDateForDateField/parseDateForDateField.ts +0 -48
  139. /package/dist/components/{Datepicker/subcomponents/DateField/__tests__/DateField.test.d.ts → Calendar/__tests__/Calendar.test.d.ts} +0 -0
  140. /package/dist/components/{Datepicker/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.test.d.ts → Calendar/utils/getAcademicWeekNumbers/getAcademicWeekNumbers.test.d.ts} +0 -0
  141. /package/dist/components/{Datepicker → Calendar}/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.d.ts +0 -0
  142. /package/dist/components/{Datepicker/utils/parseDateForDateField/parseDateForDateField.test.d.ts → Calendar/utils/getDatesForCalendarGrid/getDatesForCalendarGrid.test.d.ts} +0 -0
@@ -3,14 +3,16 @@ import { useArgs } from '@storybook/preview-api';
3
3
  import Datepicker from './Datepicker';
4
4
 
5
5
  const meta = {
6
- title: 'Components/Work in progress/Datepicker/Datepicker',
6
+ title: 'Components/Ready to use/Datepicker',
7
7
  component: Datepicker,
8
8
  parameters: { layout: 'padded' },
9
9
  argTypes: {
10
- date: { control: { type: 'date' } },
10
+ value: { control: { type: 'date' } },
11
11
  minDate: { control: { type: 'date' } },
12
12
  maxDate: { control: { type: 'date' } },
13
+ disabled: { control: { type: 'boolean' } },
13
14
  native: { control: { type: 'boolean' } },
15
+ showAcademicWeeks: { control: { type: 'boolean' } },
14
16
  },
15
17
  tags: ['autodocs'],
16
18
  } satisfies Meta<typeof Datepicker>;
@@ -18,52 +20,247 @@ const meta = {
18
20
  export default meta;
19
21
  type Story = StoryObj<typeof meta>;
20
22
 
23
+ const dateToISOString = (date: Date) => {
24
+ return date.toISOString().split('T')[0];
25
+ };
26
+
21
27
  export const Default: Story = {
28
+ render: () => {
29
+ const [args, updateArgs] = useArgs();
30
+
31
+ // Storybook controls provide UNIX timestamps for dates, need to convert
32
+ // https://storybook.js.org/docs/essentials/controls#annotation
33
+ args.value = args.value ? new Date(args.value) : null;
34
+ args.minDate = args.minDate
35
+ ? new Date(args.minDate).toLocaleDateString('sv-SE')
36
+ : null;
37
+ args.maxDate = args.maxDate
38
+ ? new Date(args.maxDate).toLocaleDateString('sv-SE')
39
+ : null;
40
+
41
+ const onValueChange = (value: Date | null | undefined) =>
42
+ updateArgs({ value: value });
43
+ return (
44
+ <Datepicker
45
+ {...args}
46
+ onValueChange={onValueChange}
47
+ />
48
+ );
49
+ },
50
+ };
51
+
52
+ // Story repeated in Calendar.stories.tsx
53
+ export const WithEvents: Story = {
54
+ name: 'With events',
22
55
  args: {
23
- date: null,
24
- onDateChange: () => {},
56
+ // IIFE gives us event dots for the current month
57
+ events: (() => {
58
+ const currentDate = new Date();
59
+ const currentYear = currentDate.getFullYear();
60
+ const currentMonth = currentDate.getMonth();
61
+ return [
62
+ // Grey event dots
63
+ { date: dateToISOString(new Date(currentYear, currentMonth, -2)) },
64
+ { date: dateToISOString(new Date(currentYear, currentMonth, -1)) },
65
+ { date: dateToISOString(new Date(currentYear, currentMonth, -1)) },
66
+ { date: dateToISOString(new Date(currentYear, currentMonth, 0)) },
67
+ { date: dateToISOString(new Date(currentYear, currentMonth, 0)) },
68
+ { date: dateToISOString(new Date(currentYear, currentMonth, 0)) },
69
+ // Blue event dots
70
+ { date: dateToISOString(new Date(currentYear, currentMonth, 1)) },
71
+ { date: dateToISOString(new Date(currentYear, currentMonth, 2)) },
72
+ { date: dateToISOString(new Date(currentYear, currentMonth, 2)) },
73
+ { date: dateToISOString(new Date(currentYear, currentMonth, 3)) },
74
+ { date: dateToISOString(new Date(currentYear, currentMonth, 3)) },
75
+ { date: dateToISOString(new Date(currentYear, currentMonth, 3)) },
76
+ ];
77
+ })(),
25
78
  },
26
79
  render: () => {
27
80
  const [args, updateArgs] = useArgs();
28
81
 
29
- // Convert UNIX timestamp from Storybook controls to `Date` object
82
+ // Storybook controls provide UNIX timestamps for dates, need to convert
30
83
  // https://storybook.js.org/docs/essentials/controls#annotation
31
- args.date = args.date ? new Date(args.date) : null;
32
- args.minDate = args.minDate ? new Date(args.minDate) : null;
33
- args.maxDate = args.maxDate ? new Date(args.maxDate) : null;
84
+ args.value = args.value ? new Date(args.value) : null;
85
+ args.minDate = args.minDate
86
+ ? new Date(args.minDate).toLocaleDateString('sv-SE')
87
+ : null;
88
+ args.maxDate = args.maxDate
89
+ ? new Date(args.maxDate).toLocaleDateString('sv-SE')
90
+ : null;
34
91
 
35
- // const onChange = (event: React.ChangeEvent<HTMLInputElement>) => updateArgs({ date: event.target.valueAsDate });
36
- const onDateChange = (date: Date | null | undefined) =>
37
- updateArgs({ date: date });
92
+ const onValueChange = (value: Date | null | undefined) =>
93
+ updateArgs({ value: value });
38
94
  return (
39
95
  <Datepicker
40
96
  {...args}
41
- date={args.date}
42
- onDateChange={onDateChange}
97
+ onValueChange={onValueChange}
43
98
  />
44
99
  );
45
100
  },
46
101
  };
47
102
 
48
- export const AsNative: Story = {
103
+ const academicWeeks = [
104
+ { start: '2025-08-25', number: 1 },
105
+ { start: '2025-09-01', number: 2 },
106
+ { start: '2025-09-08', number: 3 },
107
+ { start: '2025-09-15', number: 4 },
108
+ { start: '2025-09-22', number: 5 },
109
+ { start: '2025-09-29', number: 6 },
110
+ { start: '2025-10-06', number: 7 },
111
+ { start: '2025-10-13', number: 8 },
112
+ { start: '2025-10-20', number: 9 },
113
+ { start: '2025-10-27', number: 10 },
114
+ { start: '2025-11-03', number: 11 },
115
+ { start: '2025-11-10', number: 12 },
116
+ { start: '2025-11-17', number: 13 },
117
+ { start: '2025-11-24', number: 14 },
118
+ { start: '2025-12-01', number: 15 },
119
+ { start: '2025-12-08', number: 16 },
120
+ { start: '2025-12-15', number: 17 },
121
+ { start: '2025-12-22', number: 18 },
122
+ { start: '2025-12-29', number: 19 },
123
+ { start: '2026-01-05', number: 20 },
124
+ { start: '2026-01-12', number: 21 },
125
+ { start: '2026-01-19', number: 22 },
126
+ { start: '2026-01-26', number: 23 },
127
+ { start: '2026-02-02', number: 24 },
128
+ { start: '2026-02-09', number: 25 },
129
+ { start: '2026-02-16', number: 26 },
130
+ { start: '2026-02-23', number: 27 },
131
+ { start: '2026-03-02', number: 28 },
132
+ { start: '2026-03-09', number: 29 },
133
+ { start: '2026-03-16', number: 30 },
134
+ { start: '2026-03-23', number: 31 },
135
+ { start: '2026-03-30', number: 32 },
136
+ { start: '2026-04-06', number: 33 },
137
+ { start: '2026-04-13', number: 34 },
138
+ { start: '2026-04-20', number: 35 },
139
+ { start: '2026-04-27', number: 36 },
140
+ { start: '2026-05-04', number: 37 },
141
+ { start: '2026-05-11', number: 38 },
142
+ { start: '2026-05-18', number: 39 },
143
+ { start: '2026-05-25', number: 40 },
144
+ { start: '2026-06-01', number: 41 },
145
+ { start: '2026-06-08', number: 42 },
146
+ { start: '2026-06-15', number: 43 },
147
+ { start: '2026-06-22', number: 44 },
148
+ { start: '2026-06-29', number: 45 },
149
+ { start: '2026-07-06', number: 46 },
150
+ { start: '2026-07-13', number: 47 },
151
+ { start: '2026-07-20', number: 48 },
152
+ { start: '2026-07-27', number: 49 },
153
+ { start: '2026-08-03', number: 50 },
154
+ { start: '2026-08-10', number: 51 },
155
+ { start: '2026-08-17', number: 52 },
156
+ { start: '2026-08-24', number: 53 },
157
+ ];
158
+
159
+ // Story repeated in Calendar.stories.tsx
160
+ export const WithAcademicWeeks: Story = {
161
+ name: 'With academic weeks',
162
+ args: {
163
+ showAcademicWeeks: true,
164
+ academicWeeks: academicWeeks,
165
+ value: new Date(academicWeeks[0].start), // So the week numbers appear on story mount
166
+ },
167
+ render: () => {
168
+ const [args, updateArgs] = useArgs();
169
+
170
+ // Storybook controls provide UNIX timestamps for dates, need to convert
171
+ // https://storybook.js.org/docs/essentials/controls#annotation
172
+ args.value = args.value ? new Date(args.value) : null;
173
+ args.minDate = args.minDate
174
+ ? new Date(args.minDate).toLocaleDateString('sv-SE')
175
+ : null;
176
+ args.maxDate = args.maxDate
177
+ ? new Date(args.maxDate).toLocaleDateString('sv-SE')
178
+ : null;
179
+
180
+ const onValueChange = (value: Date | null | undefined) =>
181
+ updateArgs({ value: value });
182
+ return (
183
+ <Datepicker
184
+ {...args}
185
+ onValueChange={onValueChange}
186
+ />
187
+ );
188
+ },
189
+ };
190
+
191
+ // Story repeated in Datepicker.stories.tsx
192
+ export const MinMaxDates: Story = {
193
+ name: 'With min and max dates',
194
+ args: {
195
+ // Initialise min date as 5 days into the current month
196
+ minDate: (() => {
197
+ const now = new Date();
198
+ return new Date(now.getFullYear(), now.getMonth(), 5)
199
+ .toISOString()
200
+ .split('T')[0];
201
+ })(),
202
+ // Initialise max date as 5 days before the last day of the current month
203
+ maxDate: (() => {
204
+ const now = new Date();
205
+ // Get last day of current month
206
+ const lastDay = new Date(
207
+ now.getFullYear(),
208
+ now.getMonth() + 1,
209
+ 0
210
+ ).getDate();
211
+ return new Date(now.getFullYear(), now.getMonth(), lastDay - 5)
212
+ .toISOString()
213
+ .split('T')[0];
214
+ })(),
215
+ },
216
+ render: () => {
217
+ const [args, updateArgs] = useArgs();
218
+
219
+ // Storybook controls provide UNIX timestamps for dates, need to convert
220
+ // https://storybook.js.org/docs/essentials/controls#annotation
221
+ args.value = args.value ? new Date(args.value) : null;
222
+ args.minDate = args.minDate
223
+ ? new Date(args.minDate).toLocaleDateString('sv-SE')
224
+ : null;
225
+ args.maxDate = args.maxDate
226
+ ? new Date(args.maxDate).toLocaleDateString('sv-SE')
227
+ : null;
228
+
229
+ const onValueChange = (value: Date | null | undefined) =>
230
+ updateArgs({ value: value });
231
+ return (
232
+ <Datepicker
233
+ {...args}
234
+ onValueChange={onValueChange}
235
+ />
236
+ );
237
+ },
238
+ };
239
+
240
+ export const Native: Story = {
49
241
  name: 'As native fallback',
50
242
  args: {
51
243
  native: true,
52
- date: null,
53
- onDateChange: () => {},
54
244
  },
55
245
  render: () => {
56
246
  const [args, updateArgs] = useArgs();
57
- args.date = args.date ? new Date(args.date) : null;
58
- args.minDate = args.minDate ? new Date(args.minDate) : null;
59
- args.maxDate = args.maxDate ? new Date(args.maxDate) : null;
60
- const onDateChange = (date: Date | null | undefined) =>
61
- updateArgs({ date: date });
247
+
248
+ // Storybook controls provide UNIX timestamps for dates, need to convert
249
+ // https://storybook.js.org/docs/essentials/controls#annotation
250
+ args.value = args.value ? new Date(args.value) : null;
251
+ args.minDate = args.minDate
252
+ ? new Date(args.minDate).toLocaleDateString('sv-SE')
253
+ : null;
254
+ args.maxDate = args.maxDate
255
+ ? new Date(args.maxDate).toLocaleDateString('sv-SE')
256
+ : null;
257
+
258
+ const onValueChange = (value: Date | null | undefined) =>
259
+ updateArgs({ value: value });
62
260
  return (
63
261
  <Datepicker
64
262
  {...args}
65
- date={args.date}
66
- onDateChange={onDateChange}
263
+ onValueChange={onValueChange}
67
264
  />
68
265
  );
69
266
  },
@@ -1,153 +1,50 @@
1
- import {
2
- useState,
3
- useRef,
4
- memo,
5
- InputHTMLAttributes,
6
- } from 'react';
7
- import { css, cx } from '@emotion/css';
8
- import {
9
- Native,
10
- DateField,
11
- CalendarMenu,
12
- } from './subcomponents';
13
- import { useTheme } from '../../theme';
14
-
15
- export const NAME = 'ucl-datepicker';
16
-
17
- export interface DatepickerProps
18
- extends InputHTMLAttributes<HTMLInputElement> {
19
- date: Date | null | undefined;
20
- minDate?: Date | null | undefined;
21
- maxDate?: Date | null | undefined;
22
- onDateChange: (date: Date | null | undefined) => void;
23
- native?: boolean;
24
- testId?: string;
25
- }
1
+ import { NativeDatepicker, CustomDatepicker } from './subcomponents';
2
+ import { dateToLocaleISOString } from './utils';
3
+ import type { DatepickerProps } from './Datepicker.types';
26
4
 
27
5
  const Datepicker = ({
28
- date,
6
+ value,
7
+ onValueChange,
29
8
  minDate,
30
9
  maxDate,
31
- onDateChange,
32
- native,
33
- testId = NAME,
10
+ disabled,
34
11
  className,
12
+ native,
13
+ nativeRef,
14
+ nativeHTMLAttributes,
35
15
  ...props
36
16
  }: DatepickerProps) => {
37
- const [theme] = useTheme();
38
- const [isOpen, setIsOpen] = useState(false);
39
- const datepickerRef = useRef<HTMLDivElement>(null);
17
+ if (native) {
18
+ const nativeValue = dateToLocaleISOString(value);
40
19
 
41
- if (native)
42
20
  return (
43
- <Native
44
- date={date}
21
+ <NativeDatepicker
22
+ value={nativeValue || ''}
23
+ onChange={(event) => {
24
+ const dateString = event.target.value;
25
+ onValueChange?.(dateString ? new Date(dateString) : null, event);
26
+ }}
27
+ min={minDate || undefined}
28
+ max={maxDate || undefined}
29
+ disabled={disabled}
30
+ className={className}
31
+ ref={nativeRef}
32
+ {...nativeHTMLAttributes}
33
+ />
34
+ );
35
+ } else {
36
+ return (
37
+ <CustomDatepicker
38
+ value={value}
39
+ onValueChange={onValueChange}
45
40
  minDate={minDate}
46
41
  maxDate={maxDate}
47
- onDateChange={onDateChange}
48
- testId={testId}
42
+ className={className}
43
+ disabled={disabled}
49
44
  {...props}
50
45
  />
51
46
  );
52
-
53
- const blur = () =>
54
- (document.activeElement as HTMLElement)?.blur();
55
-
56
- const handleKeyDown = (
57
- event: React.KeyboardEvent<HTMLDivElement>
58
- ) => {
59
- // Keyboard mappings for interacting with the date `<CalendarMenu>`
60
- // Escape & Enter both close the `<CalendarMenu>`
61
- if (event.key === 'Escape' || event.key === 'Enter')
62
- blur();
63
- // Move the currently selected date in the `<CalendarGrid />`
64
- else if (event.key === 'ArrowLeft' && date)
65
- onDateChange(
66
- new Date(
67
- date.getFullYear(),
68
- date.getMonth(),
69
- date.getDate() - 1
70
- )
71
- );
72
- else if (event.key === 'ArrowRight' && date)
73
- onDateChange(
74
- new Date(
75
- date.getFullYear(),
76
- date.getMonth(),
77
- date.getDate() + 1
78
- )
79
- );
80
- else if (event.key === 'ArrowUp' && date)
81
- onDateChange(
82
- new Date(
83
- date.getFullYear(),
84
- date.getMonth(),
85
- date.getDate() - 7
86
- )
87
- );
88
- else if (event.key === 'ArrowDown' && date)
89
- onDateChange(
90
- new Date(
91
- date.getFullYear(),
92
- date.getMonth(),
93
- date.getDate() + 7
94
- )
95
- );
96
- };
97
-
98
- const handleOnFocus = () => setIsOpen(true);
99
-
100
- const handleDatePick = () => blur();
101
-
102
- const handleBlurCapture = (event: React.FocusEvent) => {
103
- // Only close if focus moves outside `<Datepicker>` and its subcomponents
104
- if (
105
- !datepickerRef.current?.contains(
106
- event.relatedTarget as Node
107
- )
108
- )
109
- setIsOpen(false);
110
- };
111
-
112
- const baseStyle = css`
113
- position: relative;
114
- width: max-content;
115
- user-select: none;
116
-
117
- &:focus-visible,
118
- &:focus-within {
119
- box-shadow: ${theme.boxShadow.focus};
120
- outline: none;
121
- }
122
- `;
123
-
124
- const style = cx(baseStyle, className);
125
-
126
- return (
127
- <div
128
- className={style}
129
- ref={datepickerRef}
130
- onFocus={handleOnFocus}
131
- onBlurCapture={handleBlurCapture}
132
- onKeyDown={handleKeyDown}
133
- tabIndex={0}
134
- data-testid={testId}
135
- >
136
- <DateField
137
- date={date}
138
- onChange={onDateChange}
139
- testId={testId}
140
- />
141
- {isOpen && (
142
- <CalendarMenu
143
- date={date}
144
- setDate={onDateChange}
145
- onDatePick={handleDatePick}
146
- testId={testId}
147
- />
148
- )}
149
- </div>
150
- );
47
+ }
151
48
  };
152
49
 
153
- export default memo(Datepicker);
50
+ export default Datepicker;
@@ -0,0 +1,38 @@
1
+ import type { InputHTMLAttributes, HTMLAttributes, RefObject } from 'react';
2
+ import type { CalendarEvent, AcademicWeek } from '../Calendar';
3
+
4
+ export type DatepickerValue = Date | null;
5
+
6
+ interface BaseDatepickerProps {
7
+ value?: DatepickerValue;
8
+ onValueChange?: (
9
+ value: DatepickerValue,
10
+ event?: React.SyntheticEvent
11
+ ) => void;
12
+ minDate?: string | null; // ISO date string: YYYY-MM-DD
13
+ maxDate?: string | null; // ISO date string: YYYY-MM-DD
14
+ events?: CalendarEvent[];
15
+ showAcademicWeeks?: boolean;
16
+ academicWeeks?: AcademicWeek[];
17
+ testId?: string;
18
+ disabled?: boolean;
19
+ ref?: RefObject<HTMLDivElement>;
20
+ inputRef?: RefObject<HTMLInputElement>;
21
+ }
22
+
23
+ // Valid HTML attributes for <input type="date">
24
+ type NativeDatepickerAttributeProps = Omit<
25
+ InputHTMLAttributes<HTMLInputElement>,
26
+ // Remove shared 'top-level' & handled props
27
+ 'value' | 'onChange' | 'min' | 'max' | 'className' | 'disabled'
28
+ >;
29
+
30
+ // `className?: string` is received automatically from `HTMLAttributes<HTMLDivElement>`
31
+ export interface DatepickerProps
32
+ extends BaseDatepickerProps,
33
+ HTMLAttributes<HTMLDivElement> {
34
+ native?: boolean;
35
+ // Attached as a nested object
36
+ nativeHTMLAttributes?: NativeDatepickerAttributeProps;
37
+ nativeRef?: RefObject<HTMLInputElement>;
38
+ }