@dashforge/tw 0.9.1-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 +31 -0
- package/dist/index.esm.js +1152 -336
- 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 +10 -4
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +4 -4
- 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 +28 -16
- package/src/components/DateTimePicker/DateTimePicker.perf.test.tsx +0 -153
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
/**
|
|
3
|
-
* Performance + re-render budget tests for `<DateTimePicker>`.
|
|
4
|
-
*
|
|
5
|
-
* Guardrails — not absolute benchmarks. They protect against:
|
|
6
|
-
*
|
|
7
|
-
* 1. Pathological mount cost (an accidental quadratic in a future
|
|
8
|
-
* refactor — much less likely here than in `<Autocomplete>` because
|
|
9
|
-
* there's no option list, but the test still pins the budget).
|
|
10
|
-
*
|
|
11
|
-
* 2. Sibling re-renders on every keystroke. The bridge subscription
|
|
12
|
-
* model (`useDashFieldMeta(name)`) must remain GRANULAR — only the
|
|
13
|
-
* field whose value changed re-renders, never a sibling.
|
|
14
|
-
*
|
|
15
|
-
* 3. Linear render budget per keystroke for THIS field. Aim for 1
|
|
16
|
-
* render per `onChange`; tolerate 2 (RHF / engine reactive snapshot
|
|
17
|
-
* batching).
|
|
18
|
-
*
|
|
19
|
-
* 4. Mode switch isn't accidentally expensive (cycling between
|
|
20
|
-
* `date` / `time` / `datetime` mounts a NEW native input each time
|
|
21
|
-
* — but the component itself must not allocate per-key arrays etc.).
|
|
22
|
-
*/
|
|
23
|
-
import { describe, it, expect } from 'vitest';
|
|
24
|
-
import { render, fireEvent, screen } from '@testing-library/react';
|
|
25
|
-
import { DashFormProvider } from '@dashforge/forms';
|
|
26
|
-
import { DateTimePicker, TextField } from '../../index.js';
|
|
27
|
-
|
|
28
|
-
function withRenderCounter<P>(
|
|
29
|
-
Component: React.ComponentType<P>,
|
|
30
|
-
counterRef: { count: number }
|
|
31
|
-
): React.FC<P> {
|
|
32
|
-
return function Counted(props: P) {
|
|
33
|
-
counterRef.count++;
|
|
34
|
-
return <Component {...(props as P & object)} />;
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
describe('<DateTimePicker> performance', () => {
|
|
39
|
-
it('mounts well under 50ms (standalone, no bridge)', () => {
|
|
40
|
-
const t0 = performance.now();
|
|
41
|
-
render(<DateTimePicker name="d" label="Date" />);
|
|
42
|
-
const t1 = performance.now();
|
|
43
|
-
expect(t1 - t0).toBeLessThan(50);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('mounts under 100ms inside DashFormProvider (with bridge wiring)', () => {
|
|
47
|
-
const t0 = performance.now();
|
|
48
|
-
render(
|
|
49
|
-
<DashFormProvider defaultValues={{ d: '2026-05-16' }}>
|
|
50
|
-
<DateTimePicker name="d" label="Date" />
|
|
51
|
-
</DashFormProvider>
|
|
52
|
-
);
|
|
53
|
-
const t1 = performance.now();
|
|
54
|
-
expect(t1 - t0).toBeLessThan(100);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('processes a value change in under 20ms', () => {
|
|
58
|
-
render(<DateTimePicker name="d" label="Date" />);
|
|
59
|
-
const input = screen.getByLabelText('Date') as HTMLInputElement;
|
|
60
|
-
const t0 = performance.now();
|
|
61
|
-
fireEvent.change(input, { target: { value: '2026-12-25' } });
|
|
62
|
-
const t1 = performance.now();
|
|
63
|
-
expect(t1 - t0).toBeLessThan(20);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe('<DateTimePicker> re-render budget', () => {
|
|
68
|
-
it('renders at most 2 times on mount', () => {
|
|
69
|
-
const counter = { count: 0 };
|
|
70
|
-
const Counted = withRenderCounter(DateTimePicker, counter);
|
|
71
|
-
render(<Counted name="d" label="Date" />);
|
|
72
|
-
// 1 initial + (tolerate 1 follow-up commit for any setState
|
|
73
|
-
// scheduled in a useEffect; today we don't schedule any, but the
|
|
74
|
-
// budget allows headroom).
|
|
75
|
-
expect(counter.count).toBeLessThanOrEqual(2);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('keeps controlled re-renders ≤ 1 per value prop change', () => {
|
|
79
|
-
const counter = { count: 0 };
|
|
80
|
-
const Counted = withRenderCounter(DateTimePicker, counter);
|
|
81
|
-
const { rerender } = render(
|
|
82
|
-
<Counted name="d" label="Date" value="2026-01-01" />
|
|
83
|
-
);
|
|
84
|
-
const baseline = counter.count;
|
|
85
|
-
rerender(<Counted name="d" label="Date" value="2026-01-02" />);
|
|
86
|
-
rerender(<Counted name="d" label="Date" value="2026-01-03" />);
|
|
87
|
-
rerender(<Counted name="d" label="Date" value="2026-01-04" />);
|
|
88
|
-
// 3 prop changes ⇒ ≤ 4 renders (one per change + tolerance).
|
|
89
|
-
expect(counter.count - baseline).toBeLessThanOrEqual(4);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('does NOT re-render an unrelated sibling on each value change', () => {
|
|
93
|
-
// Critical perf property: typing into one DateTimePicker must not
|
|
94
|
-
// cause sibling components to re-render. Granular per-field
|
|
95
|
-
// subscription via `useDashFieldMeta(name)` is what enforces this.
|
|
96
|
-
const sibCount = { count: 0 };
|
|
97
|
-
const Sibling = () => {
|
|
98
|
-
sibCount.count++;
|
|
99
|
-
return <div data-testid="sib" />;
|
|
100
|
-
};
|
|
101
|
-
render(
|
|
102
|
-
<DashFormProvider defaultValues={{ d: '2026-01-01', other: 'x' }}>
|
|
103
|
-
<DateTimePicker name="d" label="Date" />
|
|
104
|
-
<Sibling />
|
|
105
|
-
</DashFormProvider>
|
|
106
|
-
);
|
|
107
|
-
const before = sibCount.count;
|
|
108
|
-
const input = screen.getByLabelText('Date') as HTMLInputElement;
|
|
109
|
-
for (let i = 1; i <= 5; i++) {
|
|
110
|
-
fireEvent.change(input, {
|
|
111
|
-
target: { value: `2026-01-0${i + 1}` },
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
expect(sibCount.count).toBe(before);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('does NOT re-render when a sibling field changes (per-field subscription)', () => {
|
|
118
|
-
// Inverse of the above: changing the OTHER field in the bridge
|
|
119
|
-
// must not re-render the DateTimePicker. Verifies the granular
|
|
120
|
-
// `useDashFieldMeta` subscription is filtering by name.
|
|
121
|
-
const dtCount = { count: 0 };
|
|
122
|
-
const Counted = withRenderCounter(DateTimePicker, dtCount);
|
|
123
|
-
render(
|
|
124
|
-
<DashFormProvider defaultValues={{ d: '2026-01-01', other: '' }}>
|
|
125
|
-
<Counted name="d" label="Date" />
|
|
126
|
-
<TextField name="other" label="Other" />
|
|
127
|
-
</DashFormProvider>
|
|
128
|
-
);
|
|
129
|
-
const baseline = dtCount.count;
|
|
130
|
-
const other = screen.getByLabelText('Other') as HTMLInputElement;
|
|
131
|
-
for (let i = 0; i < 5; i++) {
|
|
132
|
-
fireEvent.change(other, { target: { value: 'a'.repeat(i + 1) } });
|
|
133
|
-
}
|
|
134
|
-
// The DateTimePicker may re-render once on initial mount of the
|
|
135
|
-
// sibling (parent re-render propagation) but NOT on each sibling
|
|
136
|
-
// keystroke. Budget: 0 additional renders.
|
|
137
|
-
expect(dtCount.count - baseline).toBeLessThanOrEqual(1);
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('mode switch (date ⇄ datetime) does not leak the previous native input', () => {
|
|
141
|
-
// Switching `mode` changes the native element's `type` attribute —
|
|
142
|
-
// React reuses the same DOM node (no remount). Assert that the
|
|
143
|
-
// input element identity is preserved.
|
|
144
|
-
const { rerender, container } = render(
|
|
145
|
-
<DateTimePicker name="d" label="X" mode="date" />
|
|
146
|
-
);
|
|
147
|
-
const inputBefore = container.querySelector('input');
|
|
148
|
-
rerender(<DateTimePicker name="d" label="X" mode="datetime" />);
|
|
149
|
-
const inputAfter = container.querySelector('input');
|
|
150
|
-
expect(inputBefore).toBe(inputAfter);
|
|
151
|
-
expect((inputAfter as HTMLInputElement).type).toBe('datetime-local');
|
|
152
|
-
});
|
|
153
|
-
});
|