@internetarchive/histogram-date-range 0.1.7 → 0.1.8-alpha.1

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.
@@ -1,431 +1,431 @@
1
- import { html, fixture, expect, oneEvent, aTimeout } from '@open-wc/testing';
2
- import '../src/histogram-date-range';
3
- const SLIDER_WIDTH = 10;
4
- const WIDTH = 200;
5
- const subject = html `
6
- <histogram-date-range
7
- width="${WIDTH}"
8
- tooltipWidth="140"
9
- height="50"
10
- dateFormat="M/D/YYYY"
11
- minDate="1900"
12
- maxDate="12/4/2020"
13
- bins="[33, 1, 100]"
14
- >
15
- </histogram-date-range>
16
- `;
17
- async function createCustomElementInHTMLContainer() {
18
- document.head.insertAdjacentHTML('beforeend', `<style>
19
- html {
20
- font-size:10px;
21
- }
22
- .container {
23
- width: 400px;
24
- height: 400px;
25
- display: flex;
26
- background: #FFF6E1;
27
- justify-content: center;
28
- align-items: center;
29
- }
30
- </style>`);
31
- // https://open-wc.org/docs/testing/helpers/#customize-the-fixture-container
32
- const parentNode = document.createElement('div');
33
- parentNode.setAttribute('class', 'container');
34
- return fixture(subject, { parentNode });
35
- }
36
- describe('HistogramDateRange', () => {
37
- it('shows scaled histogram bars when provided with data', async () => {
38
- var _a;
39
- const el = await createCustomElementInHTMLContainer();
40
- const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
41
- const heights = Array.from(bars).map(b => b.height.baseVal.value);
42
- expect(heights).to.eql([38, 7, 50]);
43
- });
44
- it('changes the position of the sliders and standardizes date format when dates are entered', async () => {
45
- var _a, _b;
46
- const el = await createCustomElementInHTMLContainer();
47
- /* -------------------------- minimum (left) slider ------------------------- */
48
- expect(el.minSliderX).to.eq(SLIDER_WIDTH);
49
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
50
- const pressEnterEvent = new KeyboardEvent('keyup', {
51
- key: 'Enter',
52
- });
53
- // valid min date
54
- minDateInput.value = '1950';
55
- minDateInput.dispatchEvent(pressEnterEvent);
56
- expect(Math.floor(el.minSliderX)).to.eq(84);
57
- expect(el.minSelectedDate).to.eq('1/1/1950'); // set to correct format
58
- // attempt to set date earlier than first item
59
- minDateInput.value = '10/1/1850';
60
- minDateInput.dispatchEvent(new Event('blur'));
61
- expect(Math.floor(el.minSliderX)).to.eq(SLIDER_WIDTH); // leftmost valid position
62
- // allow date value less than slider range
63
- expect(el.minSelectedDate).to.eq('10/1/1850');
64
- /* -------------------------- maximum (right) slider ------------------------- */
65
- expect(el.maxSliderX).to.eq(WIDTH - SLIDER_WIDTH);
66
- const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
67
- // set valid max date
68
- maxDateInput.value = '3/12/1975';
69
- maxDateInput.dispatchEvent(pressEnterEvent);
70
- expect(Math.floor(el.maxSliderX)).to.eq(121);
71
- expect(maxDateInput.value).to.eq('3/12/1975');
72
- // attempt to set date later than last item
73
- maxDateInput.value = '12/31/2199';
74
- maxDateInput.dispatchEvent(new Event('blur'));
75
- await el.updateComplete;
76
- expect(el.maxSliderX).to.eq(WIDTH - SLIDER_WIDTH); // rightmost valid position
77
- // allow date value greater than slider range
78
- expect(maxDateInput.value).to.eq('12/31/2199');
79
- });
80
- it('handles invalid date inputs', async () => {
81
- var _a, _b;
82
- const el = await createCustomElementInHTMLContainer();
83
- /* -------------------------- minimum (left) slider ------------------------- */
84
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
85
- minDateInput.value = '5/17/1961';
86
- minDateInput.dispatchEvent(new Event('blur'));
87
- await el.updateComplete;
88
- expect(Math.floor(el.minSliderX)).to.eq(101);
89
- expect(minDateInput.value).to.eq('5/17/1961');
90
- // enter invalid value
91
- minDateInput.value = 'invalid';
92
- minDateInput.dispatchEvent(new Event('blur'));
93
- await el.updateComplete;
94
- expect(Math.floor(el.minSliderX)).to.eq(101); // does not move
95
- expect(minDateInput.value).to.eq('5/17/1961'); // resets back to previous date
96
- /* -------------------------- maximum (right) slider ------------------------- */
97
- const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
98
- // initial values
99
- expect(el.maxSliderX).to.eq(WIDTH - SLIDER_WIDTH);
100
- expect(maxDateInput.value).to.eq('12/4/2020');
101
- // enter invalid value
102
- maxDateInput.value = 'Abc 12, 1YYY';
103
- maxDateInput.dispatchEvent(new Event('blur'));
104
- await el.updateComplete;
105
- expect(Math.floor(el.maxSliderX)).to.eq(WIDTH - SLIDER_WIDTH); // does not move
106
- expect(maxDateInput.value).to.eq('12/4/2020'); // resets back to previous date
107
- });
108
- it('updates the date inputs when the sliders are moved', async () => {
109
- var _a, _b, _c, _d, _e;
110
- const el = await createCustomElementInHTMLContainer();
111
- /* -------------------------- minimum (left) slider ------------------------- */
112
- const minSlider = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#slider-min');
113
- const container = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#container');
114
- const minDateInput = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('#date-min');
115
- // initial state
116
- expect(minSlider.getBoundingClientRect().x).to.eq(108);
117
- expect(Array.from(minSlider.classList).join(' ')).to.eq('draggable');
118
- // pointer down
119
- minSlider.dispatchEvent(new PointerEvent('pointerdown'));
120
- await el.updateComplete;
121
- // cursor changes to 'grab'
122
- const classList = minSlider.classList;
123
- expect(classList.contains('draggable')).to.be.true;
124
- expect(classList.contains('dragging')).to.be.true;
125
- // slide to right
126
- window.dispatchEvent(new PointerEvent('pointermove', { clientX: 70 }));
127
- await el.updateComplete;
128
- // slider has moved
129
- expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(168);
130
- // min date is updated
131
- expect(minDateInput.value).to.eq('4/23/1940');
132
- // stop dragging
133
- window.dispatchEvent(new PointerEvent('pointerup'));
134
- await el.updateComplete;
135
- // cursor returns to normal
136
- expect(Array.from(container.classList)).not.to.include('dragging');
137
- /* -------------------------- maximum (right) slider ------------------------- */
138
- const maxSlider = (_d = el.shadowRoot) === null || _d === void 0 ? void 0 : _d.querySelector('#slider-max');
139
- const maxDateInput = (_e = el.shadowRoot) === null || _e === void 0 ? void 0 : _e.querySelector('#date-max');
140
- // initial state
141
- expect(maxSlider.getBoundingClientRect().x).to.eq(298);
142
- // slide to left
143
- maxSlider.dispatchEvent(new PointerEvent('pointerdown', { clientX: 195 }));
144
- window.dispatchEvent(new PointerEvent('pointermove', { clientX: 160 }));
145
- await el.updateComplete;
146
- // slider has moved
147
- expect(Math.round(maxSlider.getBoundingClientRect().x)).to.eq(268);
148
- // max date is updated
149
- expect(maxDateInput.value).to.eq('10/8/2000');
150
- await el.updateComplete;
151
- // try to slide min slider past max slider
152
- minSlider.dispatchEvent(new PointerEvent('pointerdown', { clientX: 62 }));
153
- window.dispatchEvent(new PointerEvent('pointermove', { clientX: 190 }));
154
- await el.updateComplete;
155
- // slider moves all the way to meet the right slider
156
- expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(258);
157
- // try to slide max slider past min slider
158
- maxSlider.dispatchEvent(new PointerEvent('pointerdown', { clientX: 120 }));
159
- window.dispatchEvent(new PointerEvent('pointermove', { clientX: 50 }));
160
- await el.updateComplete;
161
- expect(Math.round(maxSlider.getBoundingClientRect().x)).to.eq(268); // max slider didn't move
162
- });
163
- it("emits a custom event when the element's date range changes", async () => {
164
- var _a;
165
- const el = await createCustomElementInHTMLContainer();
166
- el.updateDelay = 30; // set debounce delay of 30ms
167
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
168
- const updateEventPromise = oneEvent(el, 'histogramDateRangeUpdated');
169
- // simulate typing a new value into input
170
- minDateInput.value = '1955';
171
- minDateInput.dispatchEvent(new Event('blur'));
172
- // will wait longer than debounce delay
173
- const { detail } = await updateEventPromise;
174
- // verify that event is emitted
175
- expect(detail.minDate).to.equal('1/1/1955');
176
- expect(detail.maxDate).to.equal('12/4/2020');
177
- let eventCount = 0;
178
- el.addEventListener('histogramDateRangeUpdated', () => (eventCount += 1));
179
- // events are not sent if no change since the last event that was sent
180
- minDateInput.value = '1955';
181
- minDateInput.dispatchEvent(new Event('blur'));
182
- await aTimeout(60); // wait longer than debounce delay
183
- expect(eventCount).to.equal(0);
184
- const updateEventPromise2 = oneEvent(el, 'histogramDateRangeUpdated');
185
- // with the debounce, multiple quick changes only result in one event sent
186
- minDateInput.value = '1965';
187
- minDateInput.dispatchEvent(new Event('blur'));
188
- await aTimeout(10); // wait less than the debounce delay
189
- minDateInput.dispatchEvent(new Event('focus'));
190
- minDateInput.value = '1975';
191
- minDateInput.dispatchEvent(new Event('blur'));
192
- await aTimeout(10);
193
- minDateInput.dispatchEvent(new Event('focus'));
194
- minDateInput.value = '1985';
195
- minDateInput.dispatchEvent(new Event('blur'));
196
- await aTimeout(10);
197
- const event2 = await updateEventPromise2;
198
- expect(event2.detail.minDate).to.equal('1/1/1985');
199
- expect(eventCount).to.equal(1); // only one event was fired
200
- });
201
- it('shows/hides tooltip when hovering over (or pointing at) a bar', async () => {
202
- var _a, _b;
203
- const el = await createCustomElementInHTMLContainer();
204
- // include a number which will require commas (1,000,000)
205
- el.bins = [1000000, 1, 100];
206
- await aTimeout(10);
207
- const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
208
- const tooltip = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#tooltip');
209
- expect(tooltip.innerText).to.eq('');
210
- // hover
211
- bars[0].dispatchEvent(new PointerEvent('pointerenter'));
212
- await el.updateComplete;
213
- expect(tooltip.innerText).to.match(/^1,000,000 items\n1\/1\/1900 - 4\/23\/1940/);
214
- expect(getComputedStyle(tooltip).display).to.eq('block');
215
- // leave
216
- bars[0].dispatchEvent(new PointerEvent('pointerleave'));
217
- await el.updateComplete;
218
- expect(getComputedStyle(tooltip).display).to.eq('none');
219
- expect(tooltip.innerText).to.eq('');
220
- // ensure singular item is not pluralized
221
- bars[1].dispatchEvent(new PointerEvent('pointerenter'));
222
- await el.updateComplete;
223
- expect(tooltip.innerText).to.match(/^1 item\n4\/23\/1940 - 8\/13\/1980/);
224
- });
225
- it('does not show tooltip while dragging', async () => {
226
- var _a, _b, _c;
227
- const el = await createCustomElementInHTMLContainer();
228
- const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
229
- const tooltip = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#tooltip');
230
- expect(tooltip.innerText).to.eq('');
231
- const minSlider = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('#slider-min');
232
- // pointer down and slide right
233
- minSlider.dispatchEvent(new PointerEvent('pointerdown'));
234
- window.dispatchEvent(new PointerEvent('pointermove', { clientX: 100 }));
235
- await el.updateComplete;
236
- // hover over bar
237
- bars[0].dispatchEvent(new PointerEvent('pointerenter'));
238
- await el.updateComplete;
239
- // tooltip display is suppressed while dragging
240
- expect(tooltip.style.display).to.eq('');
241
- });
242
- it('passes the a11y audit', async () => {
243
- await fixture(subject).then(el => expect(el).shadowDom.to.be.accessible());
244
- });
245
- it('allows range to be pre-selected', async () => {
246
- var _a, _b;
247
- const el = await fixture(html `
248
- <histogram-date-range
249
- minDate="1900"
250
- maxDate="Dec 4, 2020"
251
- minSelectedDate="2012"
252
- maxSelectedDate="2019"
253
- bins="[33, 1, 100]"
254
- >
255
- </histogram-date-range>
256
- `);
257
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
258
- expect(minDateInput.value).to.eq('2012');
259
- const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
260
- expect(maxDateInput.value).to.eq('2019');
261
- });
262
- it('extends the selected range when the histogram is clicked outside of the current range', async () => {
263
- var _a, _b;
264
- const el = await fixture(html `
265
- <histogram-date-range
266
- minDate="1900"
267
- maxDate="2020"
268
- minSelectedDate="1950"
269
- maxSelectedDate="1955"
270
- bins="[33, 1, 1, 1, 10, 10, 1, 1, 1, 50, 100]"
271
- >
272
- </histogram-date-range>
273
- `);
274
- const leftBarToClick = Array.from((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar'))[1]; // click on second bar to the left
275
- leftBarToClick.dispatchEvent(new Event('click'));
276
- await el.updateComplete;
277
- expect(el.minSelectedDate).to.eq('1910'); // range was extended to left
278
- const rightBarToClick = Array.from((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.bar'))[8]; // click on second bar from the right
279
- rightBarToClick.dispatchEvent(new Event('click'));
280
- expect(el.maxSelectedDate).to.eq('1998'); // range was extended to right
281
- });
282
- it('narrows the selected range when the histogram is clicked inside of the current range', async () => {
283
- var _a, _b;
284
- const el = await fixture(html `
285
- <histogram-date-range
286
- minDate="1900"
287
- maxDate="2020"
288
- minSelectedDate="1900"
289
- maxSelectedDate="2020"
290
- bins="[33, 1, 1, 1, 10, 10, 1, 1, 1, 50, 100]"
291
- >
292
- </histogram-date-range>
293
- `);
294
- ///////////////////////////////////////////////
295
- // NB: the slider nearest the clicked bar moves
296
- ///////////////////////////////////////////////
297
- const leftBarToClick = Array.from((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar'))[3]; // click on fourth bar to the left
298
- leftBarToClick.dispatchEvent(new Event('click'));
299
- expect(el.minSelectedDate).to.eq('1932'); // range was extended to the right
300
- const rightBarToClick = Array.from((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.bar'))[8]; // click on second bar from the right
301
- rightBarToClick.dispatchEvent(new Event('click'));
302
- expect(el.maxSelectedDate).to.eq('1998'); // range was extended to the left
303
- });
304
- it('handles invalid pre-selected range by defaulting to overall max and min', async () => {
305
- var _a, _b;
306
- const el = await fixture(html `
307
- <histogram-date-range
308
- minDate="1900"
309
- maxDate="2020"
310
- minSelectedDate="2000xyz"
311
- maxSelectedDate="5000"
312
- bins="[33, 1, 100]"
313
- >
314
- </histogram-date-range>
315
- `);
316
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
317
- // malformed min date defaults to overall min
318
- expect(minDateInput.value).to.eq('1900');
319
- const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
320
- // well-formed max date is allowed
321
- expect(maxDateInput.value).to.eq('5000');
322
- });
323
- it('handles year values less than 1000 by overriding date format to just display year', async () => {
324
- var _a, _b;
325
- const el = await fixture(html `
326
- <histogram-date-range
327
- dateFormat="M/D/YYYY"
328
- minDate="-2000"
329
- maxDate="2000"
330
- minSelectedDate="-500"
331
- maxSelectedDate="500"
332
- bins="[33, 1, 100]"
333
- >
334
- </histogram-date-range>
335
- `);
336
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
337
- expect(minDateInput.value).to.eq('-500');
338
- const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
339
- expect(maxDateInput.value).to.eq('500');
340
- });
341
- it('handles missing data', async () => {
342
- var _a, _b;
343
- let el = await fixture(html `<histogram-date-range>
344
- minDate="1900" maxDate="2020" bins=""
345
- </histogram-date-range>`);
346
- expect((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.innerHTML).to.contain('no data');
347
- el = await fixture(html `<histogram-date-range
348
- minDate="1900"
349
- maxDate="2020"
350
- bins="[]"
351
- missingDataMessage="no data available"
352
- ></histogram-date-range>`);
353
- expect((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.innerHTML).to.contain('no data available');
354
- });
355
- it('correctly displays data consisting of a single bin', async () => {
356
- var _a;
357
- const el = await fixture(html `
358
- <histogram-date-range minDate="2020" maxDate="2020" bins="[50]">
359
- </histogram-date-range>
360
- `);
361
- const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
362
- const heights = Array.from(bars).map(b => b.height.baseVal.value);
363
- expect(heights).to.eql([157]);
364
- });
365
- it('correctly displays small diff between max and min values', async () => {
366
- var _a;
367
- const el = await fixture(html `
368
- <histogram-date-range bins="[1519,2643,1880,2041,1638,1441]">
369
- </histogram-date-range>
370
- `);
371
- const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
372
- const heights = Array.from(bars).map(b => b.height.baseVal.value);
373
- expect(heights).to.eql([37, 40, 38, 38, 37, 36]);
374
- });
375
- it('has a disabled state', async () => {
376
- var _a, _b, _c;
377
- const el = await fixture(html `
378
- <histogram-date-range
379
- minDate="1900"
380
- maxDate="2020"
381
- disabled
382
- bins="[33, 1, 100]"
383
- >
384
- </histogram-date-range>
385
- `);
386
- expect((_b = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.inner-container')) === null || _b === void 0 ? void 0 : _b.classList.contains('disabled')).to.eq(true);
387
- const minSlider = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('#slider-min');
388
- expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(8); // initial state
389
- // attempt to slide to right
390
- minSlider.dispatchEvent(new PointerEvent('pointerdown'));
391
- await el.updateComplete;
392
- // cursor is not draggable if disabled
393
- expect(Array.from(minSlider.classList).join(' ')).to.eq('');
394
- // attempt to slide to right
395
- window.dispatchEvent(new PointerEvent('pointermove', { clientX: 70 }));
396
- await el.updateComplete;
397
- // slider does not moved if element disabled
398
- expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(8);
399
- });
400
- it('has a loading state with an activity indicator', async () => {
401
- var _a, _b, _c, _d;
402
- const el = await fixture(html `
403
- <histogram-date-range
404
- minDate="1900"
405
- maxDate="2020"
406
- loading
407
- bins="[33, 1, 100]"
408
- >
409
- </histogram-date-range>
410
- `);
411
- expect((_d = (_c = (_b = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('ia-activity-indicator')) === null || _b === void 0 ? void 0 : _b.attributes) === null || _c === void 0 ? void 0 : _c.getNamedItem('mode')) === null || _d === void 0 ? void 0 : _d.value).to.eq('processing');
412
- });
413
- it('can use LitElement bound properties', async () => {
414
- var _a, _b;
415
- const el = await fixture(html `
416
- <histogram-date-range
417
- .minDate=${1900}
418
- .maxDate=${'Dec 4, 2020'}
419
- .minSelectedDate=${2012}
420
- .maxSelectedDate=${2019}
421
- .bins=${[33, 1, 100]}
422
- >
423
- </histogram-date-range>
424
- `);
425
- const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
426
- expect(minDateInput.value).to.eq('2012');
427
- const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
428
- expect(maxDateInput.value).to.eq('2019');
429
- });
430
- });
1
+ import { html, fixture, expect, oneEvent, aTimeout } from '@open-wc/testing';
2
+ import '../src/histogram-date-range';
3
+ const SLIDER_WIDTH = 10;
4
+ const WIDTH = 200;
5
+ const subject = html `
6
+ <histogram-date-range
7
+ width="${WIDTH}"
8
+ tooltipWidth="140"
9
+ height="50"
10
+ dateFormat="M/D/YYYY"
11
+ minDate="1900"
12
+ maxDate="12/4/2020"
13
+ bins="[33, 1, 100]"
14
+ >
15
+ </histogram-date-range>
16
+ `;
17
+ async function createCustomElementInHTMLContainer() {
18
+ document.head.insertAdjacentHTML('beforeend', `<style>
19
+ html {
20
+ font-size:10px;
21
+ }
22
+ .container {
23
+ width: 400px;
24
+ height: 400px;
25
+ display: flex;
26
+ background: #FFF6E1;
27
+ justify-content: center;
28
+ align-items: center;
29
+ }
30
+ </style>`);
31
+ // https://open-wc.org/docs/testing/helpers/#customize-the-fixture-container
32
+ const parentNode = document.createElement('div');
33
+ parentNode.setAttribute('class', 'container');
34
+ return fixture(subject, { parentNode });
35
+ }
36
+ describe('HistogramDateRange', () => {
37
+ it('shows scaled histogram bars when provided with data', async () => {
38
+ var _a;
39
+ const el = await createCustomElementInHTMLContainer();
40
+ const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
41
+ const heights = Array.from(bars).map(b => b.height.baseVal.value);
42
+ expect(heights).to.eql([38, 7, 50]);
43
+ });
44
+ it('changes the position of the sliders and standardizes date format when dates are entered', async () => {
45
+ var _a, _b;
46
+ const el = await createCustomElementInHTMLContainer();
47
+ /* -------------------------- minimum (left) slider ------------------------- */
48
+ expect(el.minSliderX).to.eq(SLIDER_WIDTH);
49
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
50
+ const pressEnterEvent = new KeyboardEvent('keyup', {
51
+ key: 'Enter',
52
+ });
53
+ // valid min date
54
+ minDateInput.value = '1950';
55
+ minDateInput.dispatchEvent(pressEnterEvent);
56
+ expect(Math.floor(el.minSliderX)).to.eq(84);
57
+ expect(el.minSelectedDate).to.eq('1/1/1950'); // set to correct format
58
+ // attempt to set date earlier than first item
59
+ minDateInput.value = '10/1/1850';
60
+ minDateInput.dispatchEvent(new Event('blur'));
61
+ expect(Math.floor(el.minSliderX)).to.eq(SLIDER_WIDTH); // leftmost valid position
62
+ // allow date value less than slider range
63
+ expect(el.minSelectedDate).to.eq('10/1/1850');
64
+ /* -------------------------- maximum (right) slider ------------------------- */
65
+ expect(el.maxSliderX).to.eq(WIDTH - SLIDER_WIDTH);
66
+ const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
67
+ // set valid max date
68
+ maxDateInput.value = '3/12/1975';
69
+ maxDateInput.dispatchEvent(pressEnterEvent);
70
+ expect(Math.floor(el.maxSliderX)).to.eq(121);
71
+ expect(maxDateInput.value).to.eq('3/12/1975');
72
+ // attempt to set date later than last item
73
+ maxDateInput.value = '12/31/2199';
74
+ maxDateInput.dispatchEvent(new Event('blur'));
75
+ await el.updateComplete;
76
+ expect(el.maxSliderX).to.eq(WIDTH - SLIDER_WIDTH); // rightmost valid position
77
+ // allow date value greater than slider range
78
+ expect(maxDateInput.value).to.eq('12/31/2199');
79
+ });
80
+ it('handles invalid date inputs', async () => {
81
+ var _a, _b;
82
+ const el = await createCustomElementInHTMLContainer();
83
+ /* -------------------------- minimum (left) slider ------------------------- */
84
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
85
+ minDateInput.value = '5/17/1961';
86
+ minDateInput.dispatchEvent(new Event('blur'));
87
+ await el.updateComplete;
88
+ expect(Math.floor(el.minSliderX)).to.eq(101);
89
+ expect(minDateInput.value).to.eq('5/17/1961');
90
+ // enter invalid value
91
+ minDateInput.value = 'invalid';
92
+ minDateInput.dispatchEvent(new Event('blur'));
93
+ await el.updateComplete;
94
+ expect(Math.floor(el.minSliderX)).to.eq(101); // does not move
95
+ expect(minDateInput.value).to.eq('5/17/1961'); // resets back to previous date
96
+ /* -------------------------- maximum (right) slider ------------------------- */
97
+ const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
98
+ // initial values
99
+ expect(el.maxSliderX).to.eq(WIDTH - SLIDER_WIDTH);
100
+ expect(maxDateInput.value).to.eq('12/4/2020');
101
+ // enter invalid value
102
+ maxDateInput.value = 'Abc 12, 1YYY';
103
+ maxDateInput.dispatchEvent(new Event('blur'));
104
+ await el.updateComplete;
105
+ expect(Math.floor(el.maxSliderX)).to.eq(WIDTH - SLIDER_WIDTH); // does not move
106
+ expect(maxDateInput.value).to.eq('12/4/2020'); // resets back to previous date
107
+ });
108
+ it('updates the date inputs when the sliders are moved', async () => {
109
+ var _a, _b, _c, _d, _e;
110
+ const el = await createCustomElementInHTMLContainer();
111
+ /* -------------------------- minimum (left) slider ------------------------- */
112
+ const minSlider = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#slider-min');
113
+ const container = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#container');
114
+ const minDateInput = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('#date-min');
115
+ // initial state
116
+ expect(minSlider.getBoundingClientRect().x).to.eq(108);
117
+ expect(Array.from(minSlider.classList).join(' ')).to.eq('draggable');
118
+ // pointer down
119
+ minSlider.dispatchEvent(new PointerEvent('pointerdown'));
120
+ await el.updateComplete;
121
+ // cursor changes to 'grab'
122
+ const classList = minSlider.classList;
123
+ expect(classList.contains('draggable')).to.be.true;
124
+ expect(classList.contains('dragging')).to.be.true;
125
+ // slide to right
126
+ window.dispatchEvent(new PointerEvent('pointermove', { clientX: 70 }));
127
+ await el.updateComplete;
128
+ // slider has moved
129
+ expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(168);
130
+ // min date is updated
131
+ expect(minDateInput.value).to.eq('4/23/1940');
132
+ // stop dragging
133
+ window.dispatchEvent(new PointerEvent('pointerup'));
134
+ await el.updateComplete;
135
+ // cursor returns to normal
136
+ expect(Array.from(container.classList)).not.to.include('dragging');
137
+ /* -------------------------- maximum (right) slider ------------------------- */
138
+ const maxSlider = (_d = el.shadowRoot) === null || _d === void 0 ? void 0 : _d.querySelector('#slider-max');
139
+ const maxDateInput = (_e = el.shadowRoot) === null || _e === void 0 ? void 0 : _e.querySelector('#date-max');
140
+ // initial state
141
+ expect(maxSlider.getBoundingClientRect().x).to.eq(298);
142
+ // slide to left
143
+ maxSlider.dispatchEvent(new PointerEvent('pointerdown', { clientX: 195 }));
144
+ window.dispatchEvent(new PointerEvent('pointermove', { clientX: 160 }));
145
+ await el.updateComplete;
146
+ // slider has moved
147
+ expect(Math.round(maxSlider.getBoundingClientRect().x)).to.eq(268);
148
+ // max date is updated
149
+ expect(maxDateInput.value).to.eq('10/8/2000');
150
+ await el.updateComplete;
151
+ // try to slide min slider past max slider
152
+ minSlider.dispatchEvent(new PointerEvent('pointerdown', { clientX: 62 }));
153
+ window.dispatchEvent(new PointerEvent('pointermove', { clientX: 190 }));
154
+ await el.updateComplete;
155
+ // slider moves all the way to meet the right slider
156
+ expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(258);
157
+ // try to slide max slider past min slider
158
+ maxSlider.dispatchEvent(new PointerEvent('pointerdown', { clientX: 120 }));
159
+ window.dispatchEvent(new PointerEvent('pointermove', { clientX: 50 }));
160
+ await el.updateComplete;
161
+ expect(Math.round(maxSlider.getBoundingClientRect().x)).to.eq(268); // max slider didn't move
162
+ });
163
+ it("emits a custom event when the element's date range changes", async () => {
164
+ var _a;
165
+ const el = await createCustomElementInHTMLContainer();
166
+ el.updateDelay = 30; // set debounce delay of 30ms
167
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
168
+ const updateEventPromise = oneEvent(el, 'histogramDateRangeUpdated');
169
+ // simulate typing a new value into input
170
+ minDateInput.value = '1955';
171
+ minDateInput.dispatchEvent(new Event('blur'));
172
+ // will wait longer than debounce delay
173
+ const { detail } = await updateEventPromise;
174
+ // verify that event is emitted
175
+ expect(detail.minDate).to.equal('1/1/1955');
176
+ expect(detail.maxDate).to.equal('12/4/2020');
177
+ let eventCount = 0;
178
+ el.addEventListener('histogramDateRangeUpdated', () => (eventCount += 1));
179
+ // events are not sent if no change since the last event that was sent
180
+ minDateInput.value = '1955';
181
+ minDateInput.dispatchEvent(new Event('blur'));
182
+ await aTimeout(60); // wait longer than debounce delay
183
+ expect(eventCount).to.equal(0);
184
+ const updateEventPromise2 = oneEvent(el, 'histogramDateRangeUpdated');
185
+ // with the debounce, multiple quick changes only result in one event sent
186
+ minDateInput.value = '1965';
187
+ minDateInput.dispatchEvent(new Event('blur'));
188
+ await aTimeout(10); // wait less than the debounce delay
189
+ minDateInput.dispatchEvent(new Event('focus'));
190
+ minDateInput.value = '1975';
191
+ minDateInput.dispatchEvent(new Event('blur'));
192
+ await aTimeout(10);
193
+ minDateInput.dispatchEvent(new Event('focus'));
194
+ minDateInput.value = '1985';
195
+ minDateInput.dispatchEvent(new Event('blur'));
196
+ await aTimeout(10);
197
+ const event2 = await updateEventPromise2;
198
+ expect(event2.detail.minDate).to.equal('1/1/1985');
199
+ expect(eventCount).to.equal(1); // only one event was fired
200
+ });
201
+ it('shows/hides tooltip when hovering over (or pointing at) a bar', async () => {
202
+ var _a, _b;
203
+ const el = await createCustomElementInHTMLContainer();
204
+ // include a number which will require commas (1,000,000)
205
+ el.bins = [1000000, 1, 100];
206
+ await aTimeout(10);
207
+ const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
208
+ const tooltip = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#tooltip');
209
+ expect(tooltip.innerText).to.eq('');
210
+ // hover
211
+ bars[0].dispatchEvent(new PointerEvent('pointerenter'));
212
+ await el.updateComplete;
213
+ expect(tooltip.innerText).to.match(/^1,000,000 items\n1\/1\/1900 - 4\/23\/1940/);
214
+ expect(getComputedStyle(tooltip).display).to.eq('block');
215
+ // leave
216
+ bars[0].dispatchEvent(new PointerEvent('pointerleave'));
217
+ await el.updateComplete;
218
+ expect(getComputedStyle(tooltip).display).to.eq('none');
219
+ expect(tooltip.innerText).to.eq('');
220
+ // ensure singular item is not pluralized
221
+ bars[1].dispatchEvent(new PointerEvent('pointerenter'));
222
+ await el.updateComplete;
223
+ expect(tooltip.innerText).to.match(/^1 item\n4\/23\/1940 - 8\/13\/1980/);
224
+ });
225
+ it('does not show tooltip while dragging', async () => {
226
+ var _a, _b, _c;
227
+ const el = await createCustomElementInHTMLContainer();
228
+ const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
229
+ const tooltip = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#tooltip');
230
+ expect(tooltip.innerText).to.eq('');
231
+ const minSlider = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('#slider-min');
232
+ // pointer down and slide right
233
+ minSlider.dispatchEvent(new PointerEvent('pointerdown'));
234
+ window.dispatchEvent(new PointerEvent('pointermove', { clientX: 100 }));
235
+ await el.updateComplete;
236
+ // hover over bar
237
+ bars[0].dispatchEvent(new PointerEvent('pointerenter'));
238
+ await el.updateComplete;
239
+ // tooltip display is suppressed while dragging
240
+ expect(tooltip.style.display).to.eq('');
241
+ });
242
+ it('passes the a11y audit', async () => {
243
+ await fixture(subject).then(el => expect(el).shadowDom.to.be.accessible());
244
+ });
245
+ it('allows range to be pre-selected', async () => {
246
+ var _a, _b;
247
+ const el = await fixture(html `
248
+ <histogram-date-range
249
+ minDate="1900"
250
+ maxDate="Dec 4, 2020"
251
+ minSelectedDate="2012"
252
+ maxSelectedDate="2019"
253
+ bins="[33, 1, 100]"
254
+ >
255
+ </histogram-date-range>
256
+ `);
257
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
258
+ expect(minDateInput.value).to.eq('2012');
259
+ const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
260
+ expect(maxDateInput.value).to.eq('2019');
261
+ });
262
+ it('extends the selected range when the histogram is clicked outside of the current range', async () => {
263
+ var _a, _b;
264
+ const el = await fixture(html `
265
+ <histogram-date-range
266
+ minDate="1900"
267
+ maxDate="2020"
268
+ minSelectedDate="1950"
269
+ maxSelectedDate="1955"
270
+ bins="[33, 1, 1, 1, 10, 10, 1, 1, 1, 50, 100]"
271
+ >
272
+ </histogram-date-range>
273
+ `);
274
+ const leftBarToClick = Array.from((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar'))[1]; // click on second bar to the left
275
+ leftBarToClick.dispatchEvent(new Event('click'));
276
+ await el.updateComplete;
277
+ expect(el.minSelectedDate).to.eq('1910'); // range was extended to left
278
+ const rightBarToClick = Array.from((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.bar'))[8]; // click on second bar from the right
279
+ rightBarToClick.dispatchEvent(new Event('click'));
280
+ expect(el.maxSelectedDate).to.eq('1998'); // range was extended to right
281
+ });
282
+ it('narrows the selected range when the histogram is clicked inside of the current range', async () => {
283
+ var _a, _b;
284
+ const el = await fixture(html `
285
+ <histogram-date-range
286
+ minDate="1900"
287
+ maxDate="2020"
288
+ minSelectedDate="1900"
289
+ maxSelectedDate="2020"
290
+ bins="[33, 1, 1, 1, 10, 10, 1, 1, 1, 50, 100]"
291
+ >
292
+ </histogram-date-range>
293
+ `);
294
+ ///////////////////////////////////////////////
295
+ // NB: the slider nearest the clicked bar moves
296
+ ///////////////////////////////////////////////
297
+ const leftBarToClick = Array.from((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar'))[3]; // click on fourth bar to the left
298
+ leftBarToClick.dispatchEvent(new Event('click'));
299
+ expect(el.minSelectedDate).to.eq('1932'); // range was extended to the right
300
+ const rightBarToClick = Array.from((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelectorAll('.bar'))[8]; // click on second bar from the right
301
+ rightBarToClick.dispatchEvent(new Event('click'));
302
+ expect(el.maxSelectedDate).to.eq('1998'); // range was extended to the left
303
+ });
304
+ it('handles invalid pre-selected range by defaulting to overall max and min', async () => {
305
+ var _a, _b;
306
+ const el = await fixture(html `
307
+ <histogram-date-range
308
+ minDate="1900"
309
+ maxDate="2020"
310
+ minSelectedDate="2000xyz"
311
+ maxSelectedDate="5000"
312
+ bins="[33, 1, 100]"
313
+ >
314
+ </histogram-date-range>
315
+ `);
316
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
317
+ // malformed min date defaults to overall min
318
+ expect(minDateInput.value).to.eq('1900');
319
+ const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
320
+ // well-formed max date is allowed
321
+ expect(maxDateInput.value).to.eq('5000');
322
+ });
323
+ it('handles year values less than 1000 by overriding date format to just display year', async () => {
324
+ var _a, _b;
325
+ const el = await fixture(html `
326
+ <histogram-date-range
327
+ dateFormat="M/D/YYYY"
328
+ minDate="-2000"
329
+ maxDate="2000"
330
+ minSelectedDate="-500"
331
+ maxSelectedDate="500"
332
+ bins="[33, 1, 100]"
333
+ >
334
+ </histogram-date-range>
335
+ `);
336
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
337
+ expect(minDateInput.value).to.eq('-500');
338
+ const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
339
+ expect(maxDateInput.value).to.eq('500');
340
+ });
341
+ it('handles missing data', async () => {
342
+ var _a, _b;
343
+ let el = await fixture(html `<histogram-date-range>
344
+ minDate="1900" maxDate="2020" bins=""
345
+ </histogram-date-range>`);
346
+ expect((_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.innerHTML).to.contain('no data');
347
+ el = await fixture(html `<histogram-date-range
348
+ minDate="1900"
349
+ maxDate="2020"
350
+ bins="[]"
351
+ missingDataMessage="no data available"
352
+ ></histogram-date-range>`);
353
+ expect((_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.innerHTML).to.contain('no data available');
354
+ });
355
+ it('correctly displays data consisting of a single bin', async () => {
356
+ var _a;
357
+ const el = await fixture(html `
358
+ <histogram-date-range minDate="2020" maxDate="2020" bins="[50]">
359
+ </histogram-date-range>
360
+ `);
361
+ const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
362
+ const heights = Array.from(bars).map(b => b.height.baseVal.value);
363
+ expect(heights).to.eql([157]);
364
+ });
365
+ it('correctly displays small diff between max and min values', async () => {
366
+ var _a;
367
+ const el = await fixture(html `
368
+ <histogram-date-range bins="[1519,2643,1880,2041,1638,1441]">
369
+ </histogram-date-range>
370
+ `);
371
+ const bars = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.bar');
372
+ const heights = Array.from(bars).map(b => b.height.baseVal.value);
373
+ expect(heights).to.eql([37, 40, 38, 38, 37, 36]);
374
+ });
375
+ it('has a disabled state', async () => {
376
+ var _a, _b, _c;
377
+ const el = await fixture(html `
378
+ <histogram-date-range
379
+ minDate="1900"
380
+ maxDate="2020"
381
+ disabled
382
+ bins="[33, 1, 100]"
383
+ >
384
+ </histogram-date-range>
385
+ `);
386
+ expect((_b = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('.inner-container')) === null || _b === void 0 ? void 0 : _b.classList.contains('disabled')).to.eq(true);
387
+ const minSlider = (_c = el.shadowRoot) === null || _c === void 0 ? void 0 : _c.querySelector('#slider-min');
388
+ expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(8); // initial state
389
+ // attempt to slide to right
390
+ minSlider.dispatchEvent(new PointerEvent('pointerdown'));
391
+ await el.updateComplete;
392
+ // cursor is not draggable if disabled
393
+ expect(Array.from(minSlider.classList).join(' ')).to.eq('');
394
+ // attempt to slide to right
395
+ window.dispatchEvent(new PointerEvent('pointermove', { clientX: 70 }));
396
+ await el.updateComplete;
397
+ // slider does not moved if element disabled
398
+ expect(Math.round(minSlider.getBoundingClientRect().x)).to.eq(8);
399
+ });
400
+ it('has a loading state with an activity indicator', async () => {
401
+ var _a, _b, _c, _d;
402
+ const el = await fixture(html `
403
+ <histogram-date-range
404
+ minDate="1900"
405
+ maxDate="2020"
406
+ loading
407
+ bins="[33, 1, 100]"
408
+ >
409
+ </histogram-date-range>
410
+ `);
411
+ expect((_d = (_c = (_b = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('ia-activity-indicator')) === null || _b === void 0 ? void 0 : _b.attributes) === null || _c === void 0 ? void 0 : _c.getNamedItem('mode')) === null || _d === void 0 ? void 0 : _d.value).to.eq('processing');
412
+ });
413
+ it('can use LitElement bound properties', async () => {
414
+ var _a, _b;
415
+ const el = await fixture(html `
416
+ <histogram-date-range
417
+ .minDate=${1900}
418
+ .maxDate=${'Dec 4, 2020'}
419
+ .minSelectedDate=${2012}
420
+ .maxSelectedDate=${2019}
421
+ .bins=${[33, 1, 100]}
422
+ >
423
+ </histogram-date-range>
424
+ `);
425
+ const minDateInput = (_a = el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#date-min');
426
+ expect(minDateInput.value).to.eq('2012');
427
+ const maxDateInput = (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector('#date-max');
428
+ expect(maxDateInput.value).to.eq('2019');
429
+ });
430
+ });
431
431
  //# sourceMappingURL=histogram-date-range.test.js.map