@dashforge/tw 0.9.0-beta → 0.10.0-beta
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.
- package/CHANGELOG.md +60 -0
- package/dist/index.esm.js +1634 -256
- package/dist/src/components/Calendar/Calendar.d.ts +14 -0
- package/dist/src/components/Calendar/Calendar.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.types.d.ts +72 -0
- package/dist/src/components/Calendar/calendar.types.d.ts.map +1 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts +99 -0
- package/dist/src/components/Calendar/calendar.variants.d.ts.map +1 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts +14 -0
- package/dist/src/components/DatePicker/DatePicker.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts +88 -0
- package/dist/src/components/DatePicker/datePicker.types.d.ts.map +1 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts +97 -0
- package/dist/src/components/DatePicker/datePicker.variants.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts +13 -0
- package/dist/src/components/DateRangePicker/DateRangePicker.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts +85 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.types.d.ts.map +1 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts +183 -0
- package/dist/src/components/DateRangePicker/dateRangePicker.variants.d.ts.map +1 -0
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts +7 -43
- package/dist/src/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts +50 -55
- package/dist/src/components/DateTimePicker/dateTimePicker.types.d.ts.map +1 -1
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts +36 -94
- package/dist/src/components/DateTimePicker/dateTimePicker.variants.d.ts.map +1 -1
- package/dist/src/components/TimePicker/TimePicker.d.ts +12 -0
- package/dist/src/components/TimePicker/TimePicker.d.ts.map +1 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts +80 -0
- package/dist/src/components/TimePicker/timePicker.types.d.ts.map +1 -0
- package/dist/src/components/TimePicker/timePicker.variants.d.ts +103 -0
- package/dist/src/components/TimePicker/timePicker.variants.d.ts.map +1 -0
- package/dist/src/index.d.ts +15 -3
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/components/Calendar/Calendar.test.tsx +75 -0
- package/src/components/Calendar/Calendar.tsx +264 -0
- package/src/components/Calendar/calendar.types.ts +65 -0
- package/src/components/Calendar/calendar.variants.ts +64 -0
- package/src/components/DatePicker/DatePicker.test.tsx +76 -0
- package/src/components/DatePicker/DatePicker.tsx +305 -0
- package/src/components/DatePicker/datePicker.types.ts +77 -0
- package/src/components/DatePicker/datePicker.variants.ts +50 -0
- package/src/components/DateRangePicker/DateRangePicker.test.tsx +92 -0
- package/src/components/DateRangePicker/DateRangePicker.tsx +510 -0
- package/src/components/DateRangePicker/dateRangePicker.types.ts +74 -0
- package/src/components/DateRangePicker/dateRangePicker.variants.ts +101 -0
- package/src/components/DateTimePicker/DateTimePicker.test.tsx +60 -186
- package/src/components/DateTimePicker/DateTimePicker.tsx +209 -181
- package/src/components/DateTimePicker/dateTimePicker.types.ts +50 -54
- package/src/components/DateTimePicker/dateTimePicker.variants.ts +22 -51
- package/src/components/TimePicker/TimePicker.test.tsx +65 -0
- package/src/components/TimePicker/TimePicker.tsx +274 -0
- package/src/components/TimePicker/timePicker.types.ts +69 -0
- package/src/components/TimePicker/timePicker.variants.ts +53 -0
- package/src/index.ts +38 -6
- package/tsconfig.lib.json +7 -4
- package/src/components/DateTimePicker/DateTimePicker.perf.test.tsx +0 -153
|
@@ -1,204 +1,78 @@
|
|
|
1
1
|
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { describe, it, expect, vi, beforeAll } from 'vitest';
|
|
3
3
|
import { render, screen, fireEvent } from '@testing-library/react';
|
|
4
|
-
import { DashFormProvider } from '@dashforge/forms';
|
|
5
|
-
import { DateTimePicker, isoToInputValue } from './DateTimePicker.js';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
5
|
+
import { DateTimePicker } from './DateTimePicker.js';
|
|
6
|
+
|
|
7
|
+
beforeAll(() => {
|
|
8
|
+
// Radix Popover (Floating UI) needs ResizeObserver — jsdom lacks it.
|
|
9
|
+
if (!('ResizeObserver' in globalThis)) {
|
|
10
|
+
class ResizeObserverStub {
|
|
11
|
+
observe(): void {
|
|
12
|
+
/* no-op */
|
|
13
|
+
}
|
|
14
|
+
unobserve(): void {
|
|
15
|
+
/* no-op */
|
|
16
|
+
}
|
|
17
|
+
disconnect(): void {
|
|
18
|
+
/* no-op */
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
(globalThis as { ResizeObserver?: unknown }).ResizeObserver =
|
|
22
|
+
ResizeObserverStub;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
describe('DateTimePicker', () => {
|
|
27
|
+
it('renders a labelled trigger with the placeholder', () => {
|
|
28
|
+
render(
|
|
29
|
+
<DateTimePicker name="appt" label="Appointment" placeholder="Pick one" />,
|
|
29
30
|
);
|
|
31
|
+
expect(screen.queryByText('Appointment')).not.toBeNull();
|
|
32
|
+
expect(screen.getByRole('button').textContent).toContain('Pick one');
|
|
30
33
|
});
|
|
31
34
|
|
|
32
|
-
it('
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
it('shows a formatted controlled value', () => {
|
|
36
|
+
render(<DateTimePicker name="d" value="2026-05-20T14:30" />);
|
|
37
|
+
expect(screen.getByRole('button').textContent).toContain(
|
|
38
|
+
'May 20, 2026, 14:30',
|
|
35
39
|
);
|
|
36
40
|
});
|
|
37
41
|
|
|
38
|
-
it('
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
it('renders a native date input by default', () => {
|
|
46
|
-
render(<DateTimePicker name="dob" label="DOB" />);
|
|
47
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
48
|
-
expect(input.type).toBe('date');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('renders a time input when mode="time"', () => {
|
|
52
|
-
render(<DateTimePicker name="t" label="Time" mode="time" />);
|
|
53
|
-
const input = screen.getByLabelText('Time') as HTMLInputElement;
|
|
54
|
-
expect(input.type).toBe('time');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('renders a datetime-local input when mode="datetime"', () => {
|
|
58
|
-
render(<DateTimePicker name="dt" label="When" mode="datetime" />);
|
|
59
|
-
const input = screen.getByLabelText('When') as HTMLInputElement;
|
|
60
|
-
expect(input.type).toBe('datetime-local');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('seeds `defaultValue` (uncontrolled)', () => {
|
|
64
|
-
render(
|
|
65
|
-
<DateTimePicker name="dob" label="DOB" defaultValue="2026-05-16" />
|
|
66
|
-
);
|
|
67
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
68
|
-
expect(input.defaultValue).toBe('2026-05-16');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('reflects controlled `value`', () => {
|
|
72
|
-
render(
|
|
73
|
-
<DateTimePicker name="dob" label="DOB" value="2026-05-16" />
|
|
74
|
-
);
|
|
75
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
76
|
-
expect(input.value).toBe('2026-05-16');
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('truncates a controlled rich ISO to the date mode prefix', () => {
|
|
80
|
-
render(
|
|
81
|
-
<DateTimePicker
|
|
82
|
-
name="dob"
|
|
83
|
-
label="DOB"
|
|
84
|
-
value="2026-05-16T09:42:15.000Z"
|
|
85
|
-
/>
|
|
86
|
-
);
|
|
87
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
88
|
-
expect(input.value).toBe('2026-05-16');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('forwards min/max/step to the native input', () => {
|
|
92
|
-
render(
|
|
93
|
-
<DateTimePicker
|
|
94
|
-
name="t"
|
|
95
|
-
label="Time"
|
|
96
|
-
mode="time"
|
|
97
|
-
min="08:00"
|
|
98
|
-
max="18:00"
|
|
99
|
-
step={300}
|
|
100
|
-
/>
|
|
101
|
-
);
|
|
102
|
-
const input = screen.getByLabelText('Time') as HTMLInputElement;
|
|
103
|
-
expect(input.min).toBe('08:00');
|
|
104
|
-
expect(input.max).toBe('18:00');
|
|
105
|
-
expect(input.step).toBe('300');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('renders a `*` for required fields and sets the native attr', () => {
|
|
109
|
-
render(<DateTimePicker name="dob" label="DOB" required />);
|
|
110
|
-
const input = screen.getByLabelText(/DOB/) as HTMLInputElement;
|
|
111
|
-
expect(input.required).toBe(true);
|
|
112
|
-
expect(screen.getByText('*')).toBeTruthy();
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('renders helperText when not in error', () => {
|
|
116
|
-
render(
|
|
117
|
-
<DateTimePicker
|
|
118
|
-
name="dob"
|
|
119
|
-
label="DOB"
|
|
120
|
-
helperText="Pick a date in the past."
|
|
121
|
-
/>
|
|
122
|
-
);
|
|
123
|
-
expect(screen.getByText('Pick a date in the past.')).toBeTruthy();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('shows the helperText in the error slot when `error` is true', () => {
|
|
127
|
-
render(
|
|
128
|
-
<DateTimePicker
|
|
129
|
-
name="dob"
|
|
130
|
-
label="DOB"
|
|
131
|
-
error
|
|
132
|
-
helperText="Required"
|
|
133
|
-
/>
|
|
134
|
-
);
|
|
135
|
-
const p = screen.getByText('Required');
|
|
136
|
-
// The error slot has the `text-danger-600` class per the variants.
|
|
137
|
-
expect(p.className).toMatch(/danger/);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('disables the input', () => {
|
|
141
|
-
render(<DateTimePicker name="dob" label="DOB" disabled />);
|
|
142
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
143
|
-
expect(input.disabled).toBe(true);
|
|
144
|
-
});
|
|
42
|
+
it('opens a popover with a calendar and a time list', () => {
|
|
43
|
+
render(<DateTimePicker name="d" defaultValue="2026-05-20T10:00" />);
|
|
44
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
45
|
+
fireEvent.click(screen.getByRole('button'));
|
|
46
|
+
expect(screen.queryByRole('dialog')).not.toBeNull();
|
|
47
|
+
expect(screen.getByRole('grid')).not.toBeNull();
|
|
48
|
+
expect(screen.getByRole('listbox', { name: 'Time options' })).not.toBeNull();
|
|
145
49
|
});
|
|
146
50
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
);
|
|
157
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
158
|
-
fireEvent.change(input, { target: { value: '2026-12-25' } });
|
|
159
|
-
expect(onValueChange).toHaveBeenCalledWith('2026-12-25');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('fires onValueChange with `null` when the input is cleared', () => {
|
|
163
|
-
const onValueChange = vi.fn();
|
|
164
|
-
render(
|
|
165
|
-
<DateTimePicker
|
|
166
|
-
name="dob"
|
|
167
|
-
label="DOB"
|
|
168
|
-
value="2026-05-16"
|
|
169
|
-
onValueChange={onValueChange}
|
|
170
|
-
/>
|
|
171
|
-
);
|
|
172
|
-
const input = screen.getByLabelText('DOB') as HTMLInputElement;
|
|
173
|
-
fireEvent.change(input, { target: { value: '' } });
|
|
174
|
-
expect(onValueChange).toHaveBeenCalledWith(null);
|
|
175
|
-
});
|
|
51
|
+
it('picking a date updates the value and keeps the popover open', () => {
|
|
52
|
+
const onChange = vi.fn();
|
|
53
|
+
render(
|
|
54
|
+
<DateTimePicker name="d" defaultValue="2026-05-20T10:00" onChange={onChange} />,
|
|
55
|
+
);
|
|
56
|
+
fireEvent.click(screen.getByRole('button'));
|
|
57
|
+
fireEvent.click(screen.getByRole('button', { name: /May 15, 2026/ }));
|
|
58
|
+
expect(onChange).toHaveBeenCalledWith('2026-05-15T10:00');
|
|
59
|
+
expect(screen.queryByRole('dialog')).not.toBeNull();
|
|
176
60
|
});
|
|
177
61
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// are tested), so we don't assert input.value here.
|
|
188
|
-
expect(screen.getByLabelText('DOB')).toBeTruthy();
|
|
189
|
-
});
|
|
62
|
+
it('picking a time completes the value and closes the popover', () => {
|
|
63
|
+
const onChange = vi.fn();
|
|
64
|
+
render(
|
|
65
|
+
<DateTimePicker name="d" defaultValue="2026-05-20T10:00" onChange={onChange} />,
|
|
66
|
+
);
|
|
67
|
+
fireEvent.click(screen.getByRole('button'));
|
|
68
|
+
fireEvent.click(screen.getByRole('option', { name: '14:30' }));
|
|
69
|
+
expect(onChange).toHaveBeenCalledWith('2026-05-20T14:30');
|
|
70
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
190
71
|
});
|
|
191
72
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
name="dob"
|
|
197
|
-
label="DOB"
|
|
198
|
-
visibleWhen={() => false}
|
|
199
|
-
/>
|
|
200
|
-
);
|
|
201
|
-
expect(container.querySelector('input')).toBeNull();
|
|
202
|
-
});
|
|
73
|
+
it('does not open the popover when disabled', () => {
|
|
74
|
+
render(<DateTimePicker name="d" disabled />);
|
|
75
|
+
fireEvent.click(screen.getByRole('button'));
|
|
76
|
+
expect(screen.queryByRole('dialog')).toBeNull();
|
|
203
77
|
});
|
|
204
78
|
});
|