@kubex/zinc 1.1.121 → 1.1.123
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/dist/custom-elements.json +2847 -1816
- package/dist/vscode.html-custom-data.json +189 -167
- package/dist/web-types.json +531 -465
- package/dist/zn.d.ts +401 -296
- package/dist/zn.min.js +1478 -1337
- package/docs/pages/components/button.md +9 -0
- package/docs/pages/components/data-table-filter.md +60 -4
- package/docs/pages/components/data-table-search.md +3 -3
- package/docs/pages/components/data-table.md +78 -9
- package/docs/pages/components/datepicker.md +10 -0
- package/docs/pages/components/menu-item.md +16 -2
- package/package.json +1 -1
- package/src/components/button/button.component.ts +11 -4
- package/src/components/button/button.scss +12 -1
- package/src/components/data-table/data-table.component.ts +341 -139
- package/src/components/data-table/data-table.scss +110 -43
- package/src/components/data-table/data-table.test.ts +110 -0
- package/src/components/data-table-filter/data-table-filter.component.ts +487 -49
- package/src/components/data-table-filter/data-table-filter.scss +163 -9
- package/src/components/data-table-filter/data-table-filter.test.ts +328 -2
- package/src/components/data-table-search/data-table-search.component.ts +1 -1
- package/src/components/data-table-search/data-table-search.scss +5 -0
- package/src/components/datepicker/datepicker.component.ts +23 -2
- package/src/components/datepicker/datepicker.scss +25 -0
- package/src/components/datepicker/datepicker.test.ts +28 -1
- package/src/components/input/input.component.ts +1 -0
- package/src/components/input/input.scss +1 -1
- package/src/components/menu/menu.component.ts +2 -0
- package/src/components/menu/menu.scss +23 -2
- package/src/components/menu-item/menu-item.component.ts +7 -1
- package/src/components/menu-item/menu-item.scss +4 -4
- package/src/components/menu-item/menu-item.test.ts +39 -1
- package/src/components/panel/panel.component.ts +12 -5
- package/src/components/panel/panel.scss +3 -3
- package/src/components/query-builder/query-builder.component.ts +1 -1
- package/src/components/tooltip/tooltip.component.ts +2 -2
- package/src/components/tooltip/tooltip.test.ts +70 -2
|
@@ -5,8 +5,14 @@
|
|
|
5
5
|
@include wc.scrollbars;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
// Separates a top-level filter bar from the table's own panel below it
|
|
9
|
+
slot[name="filter-top"]::slotted(*) {
|
|
10
|
+
margin-bottom: var(--zn-spacing-small);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.table__scroll {
|
|
14
|
+
overflow-x: auto;
|
|
15
|
+
@include wc.scrollbars;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
table {
|
|
@@ -28,17 +34,6 @@ table {
|
|
|
28
34
|
background-color: rgb(var(--zn-shadow));
|
|
29
35
|
}
|
|
30
36
|
|
|
31
|
-
&.table--standalone {
|
|
32
|
-
background-color: rgb(var(--zn-panel));
|
|
33
|
-
border-radius: var(--zn-border-radius);
|
|
34
|
-
border: 1px solid rgb(var(--zn-border-color));
|
|
35
|
-
overflow: hidden;
|
|
36
|
-
|
|
37
|
-
thead tr th {
|
|
38
|
-
border-top: 0;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
37
|
thead {
|
|
43
38
|
color: rgb(var(--zn-text-heading));
|
|
44
39
|
font-weight: var(--zn-font-weight-medium);
|
|
@@ -232,32 +227,69 @@ table {
|
|
|
232
227
|
display: flex;
|
|
233
228
|
flex-direction: row;
|
|
234
229
|
justify-content: space-between;
|
|
235
|
-
align-items:
|
|
236
|
-
padding
|
|
230
|
+
align-items: flex-start;
|
|
231
|
+
padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
|
|
237
232
|
gap: var(--zn-spacing-small);
|
|
238
233
|
|
|
239
234
|
&__actions,
|
|
240
235
|
&__query-builder {
|
|
241
236
|
display: flex;
|
|
242
237
|
flex-direction: row;
|
|
243
|
-
align-items:
|
|
238
|
+
align-items: center;
|
|
244
239
|
gap: var(--zn-spacing-small);
|
|
245
240
|
}
|
|
246
241
|
|
|
242
|
+
&__toggle--active::part(base) {
|
|
243
|
+
background-color: rgb(var(--zn-color-tab));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
&__caption {
|
|
247
|
+
@include wc.text-style(h1);
|
|
248
|
+
|
|
249
|
+
// Sits on the same 36px line as the controls opposite it
|
|
250
|
+
line-height: 36px;
|
|
251
|
+
margin: 0;
|
|
252
|
+
overflow: hidden;
|
|
253
|
+
white-space: nowrap;
|
|
254
|
+
text-overflow: ellipsis;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Controls are a uniform 36px, so topping them out keeps them on one line while
|
|
258
|
+
// a search's help text grows the header downwards
|
|
247
259
|
&__right {
|
|
248
260
|
display: flex;
|
|
249
261
|
flex-direction: row;
|
|
250
|
-
align-items:
|
|
262
|
+
align-items: flex-start;
|
|
251
263
|
gap: var(--zn-spacing-x-small);
|
|
252
264
|
margin-left: auto;
|
|
253
265
|
}
|
|
254
266
|
}
|
|
255
267
|
|
|
268
|
+
&__filters {
|
|
269
|
+
padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
|
|
270
|
+
|
|
271
|
+
&[hidden] {
|
|
272
|
+
display: none;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
256
276
|
&__footer {
|
|
257
277
|
display: flex;
|
|
258
278
|
flex-direction: row;
|
|
259
279
|
justify-content: space-between;
|
|
260
|
-
|
|
280
|
+
flex-wrap: wrap;
|
|
281
|
+
gap: var(--zn-spacing-small);
|
|
282
|
+
padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
|
|
283
|
+
|
|
284
|
+
// The joined control must never break across lines
|
|
285
|
+
zn-button-group::part(base) {
|
|
286
|
+
flex-wrap: nowrap;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// zn-panel's footer supplies its own padding
|
|
290
|
+
&--panel {
|
|
291
|
+
padding: 0;
|
|
292
|
+
}
|
|
261
293
|
|
|
262
294
|
&__right, &__left {
|
|
263
295
|
display: flex;
|
|
@@ -270,40 +302,49 @@ table {
|
|
|
270
302
|
display: flex;
|
|
271
303
|
flex-direction: row;
|
|
272
304
|
align-items: center;
|
|
273
|
-
opacity: 0.8;
|
|
274
305
|
gap: var(--zn-spacing-x-small);
|
|
275
306
|
|
|
307
|
+
p {
|
|
308
|
+
margin: 0;
|
|
309
|
+
color: rgb(var(--zn-text));
|
|
310
|
+
opacity: 0.7;
|
|
311
|
+
white-space: nowrap;
|
|
312
|
+
}
|
|
313
|
+
|
|
276
314
|
zn-select[name="rowPerPage"] {
|
|
277
|
-
|
|
315
|
+
--zn-input-background-color: rgb(var(--zn-panel));
|
|
316
|
+
|
|
317
|
+
width: 66px;
|
|
278
318
|
}
|
|
279
319
|
|
|
320
|
+
// Matches the height of the pagination control it sits beside
|
|
280
321
|
zn-select[name="rowPerPage"]::part(combobox) {
|
|
281
|
-
|
|
322
|
+
min-height: 36px;
|
|
323
|
+
padding-inline: var(--zn-spacing-x-small);
|
|
282
324
|
}
|
|
283
325
|
|
|
284
|
-
|
|
326
|
+
zn-select[name="rowPerPage"]::part(expand-icon) {
|
|
285
327
|
margin-inline-start: 0;
|
|
286
328
|
}
|
|
287
329
|
}
|
|
288
330
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
331
|
+
// Sized to match the icon-button arrows it is joined to, but free to grow for large page numbers
|
|
332
|
+
&__pagination-page::part(base) {
|
|
333
|
+
height: 36px;
|
|
334
|
+
min-height: 36px;
|
|
335
|
+
max-height: 36px;
|
|
336
|
+
min-width: 32px;
|
|
337
|
+
padding: 0 var(--zn-spacing-2x-small);
|
|
338
|
+
justify-content: center;
|
|
339
|
+
border-color: rgb(var(--zn-border-color));
|
|
340
|
+
color: rgb(var(--zn-text));
|
|
341
|
+
font-weight: var(--zn-font-weight-normal);
|
|
342
|
+
white-space: nowrap;
|
|
294
343
|
}
|
|
295
344
|
|
|
296
345
|
&__pagination-page--active::part(base) {
|
|
297
346
|
background-color: var(--zn-color-neutral-300);
|
|
298
347
|
border-color: var(--zn-color-neutral-300);
|
|
299
|
-
color: rgb(var(--zn-text));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
&__pagination-ellipsis {
|
|
303
|
-
display: flex;
|
|
304
|
-
align-items: center;
|
|
305
|
-
padding: 0 var(--zn-spacing-x-small);
|
|
306
|
-
color: rgb(var(--zn-text));
|
|
307
348
|
}
|
|
308
349
|
}
|
|
309
350
|
|
|
@@ -330,6 +371,30 @@ table {
|
|
|
330
371
|
}
|
|
331
372
|
}
|
|
332
373
|
|
|
374
|
+
zn-menu {
|
|
375
|
+
--zn-menu-min-width: var(--zn-size-spanel);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// A table's header and footer are chrome around the rows, so they sit tighter than
|
|
379
|
+
// the panel's own content padding. The header's trailing edge tucks in further still,
|
|
380
|
+
// since the controls there are already inset by their own button padding.
|
|
381
|
+
zn-panel {
|
|
382
|
+
--zn-panel-header-padding: var(--zn-spacing-x-small) var(--zn-spacing-x-small)
|
|
383
|
+
var(--zn-spacing-x-small) var(--zn-spacing-medium);
|
|
384
|
+
--zn-panel-footer-padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Closes the rows off from the footer, which the panel border would otherwise do
|
|
388
|
+
zn-panel::part(footer) {
|
|
389
|
+
border-top: 1px solid rgb(var(--zn-border-color));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Dropdowns sit over dense rows here, so they need a solid surface rather than
|
|
393
|
+
// zn-menu's translucent default
|
|
394
|
+
zn-menu::part(base) {
|
|
395
|
+
background-color: rgb(var(--zn-panel));
|
|
396
|
+
}
|
|
397
|
+
|
|
333
398
|
.reduced-opacity .table tbody {
|
|
334
399
|
opacity: 0.5;
|
|
335
400
|
}
|
|
@@ -417,16 +482,18 @@ table:has(tbody tr:first-of-type.table__row--selected) thead tr th {
|
|
|
417
482
|
border-bottom-color: var(--zn-color-purple-600) !important;
|
|
418
483
|
}
|
|
419
484
|
|
|
420
|
-
.
|
|
485
|
+
tbody tr.table__row--group th {
|
|
486
|
+
padding: var(--zn-spacing-small) var(--zn-spacing-medium);
|
|
487
|
+
text-align: left;
|
|
488
|
+
white-space: nowrap;
|
|
489
|
+
background-color: rgb(var(--zn-panel));
|
|
490
|
+
color: rgba(var(--zn-text-panel-title), 100%);
|
|
421
491
|
font-weight: var(--zn-font-weight-semibold);
|
|
422
492
|
font-size: var(--zn-font-size-large);
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
overflow: hidden;
|
|
428
|
-
white-space: nowrap;
|
|
429
|
-
text-overflow: ellipsis;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
tbody tr.table__row--group:not(:first-of-type) th {
|
|
496
|
+
border-top: 1px solid rgb(var(--zn-border-color));
|
|
430
497
|
}
|
|
431
498
|
|
|
432
499
|
tbody tr.table__row--error td {
|
|
@@ -10,6 +10,116 @@ describe('<zn-data-table>', () => {
|
|
|
10
10
|
expect(el).to.exist;
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
+
// filter-top renders above the panel, so it must not bring an otherwise empty header row with it
|
|
14
|
+
it('renders no header when only filter-top is slotted', async () => {
|
|
15
|
+
const el = await fixture<ZnDataTable>(html`
|
|
16
|
+
<zn-data-table standalone no-initial-load>
|
|
17
|
+
<div slot="filter-top">Filters</div>
|
|
18
|
+
<div slot="empty-state">Nothing yet</div>
|
|
19
|
+
</zn-data-table>`);
|
|
20
|
+
await el.updateComplete;
|
|
21
|
+
|
|
22
|
+
expect(el.shadowRoot!.querySelector('.table__header__right')).to.not.exist;
|
|
23
|
+
expect(el.shadowRoot!.querySelector('zn-header')).to.not.exist;
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('leaves a bare empty state unwrapped by the standalone panel', async () => {
|
|
27
|
+
const el = await fixture<ZnDataTable>(html`
|
|
28
|
+
<zn-data-table standalone no-initial-load>
|
|
29
|
+
<div slot="empty-state">Nothing yet</div>
|
|
30
|
+
</zn-data-table>`);
|
|
31
|
+
await el.updateComplete;
|
|
32
|
+
|
|
33
|
+
expect(el.shadowRoot!.querySelector('zn-panel')).to.not.exist;
|
|
34
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.exist;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('keeps the standalone panel for an empty state that has a caption', async () => {
|
|
38
|
+
const el = await fixture<ZnDataTable>(html`
|
|
39
|
+
<zn-data-table standalone no-initial-load caption="Customers">
|
|
40
|
+
<div slot="empty-state">Nothing yet</div>
|
|
41
|
+
</zn-data-table>`);
|
|
42
|
+
await el.updateComplete;
|
|
43
|
+
|
|
44
|
+
expect(el.shadowRoot!.querySelector('zn-panel')).to.exist;
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Raising the page size past the total used to drop the whole footer, leaving no way back
|
|
48
|
+
it('keeps the rows-per-page selector when the page size covers every row', async () => {
|
|
49
|
+
const originalFetch = window.fetch;
|
|
50
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({
|
|
51
|
+
rows: Array.from({length: 24}, (_, i) => ({id: String(i), cells: [{text: 'Row', column: 'name'}]})),
|
|
52
|
+
page: 1,
|
|
53
|
+
perPage: 50,
|
|
54
|
+
total: 24,
|
|
55
|
+
}), {status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const el = await fixture<ZnDataTable>(html`
|
|
59
|
+
<zn-data-table data-uri="/test-data" headers='{"name": {"key": "name", "label": "Name"}}'></zn-data-table>`);
|
|
60
|
+
await waitUntil(() => el.shadowRoot!.querySelector('tbody tr.table__row--data'));
|
|
61
|
+
|
|
62
|
+
expect(el.shadowRoot!.querySelector('zn-select[name="rowPerPage"]')).to.exist;
|
|
63
|
+
expect(el.shadowRoot!.querySelector('.table__footer__pagination-page')).to.not.exist;
|
|
64
|
+
} finally {
|
|
65
|
+
window.fetch = originalFetch;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('hides the rows-per-page selector when the dataset fits the smallest page size', async () => {
|
|
70
|
+
const originalFetch = window.fetch;
|
|
71
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({
|
|
72
|
+
rows: [{id: '1', cells: [{text: 'Row', column: 'name'}]}],
|
|
73
|
+
page: 1,
|
|
74
|
+
perPage: 10,
|
|
75
|
+
total: 1,
|
|
76
|
+
}), {status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const el = await fixture<ZnDataTable>(html`
|
|
80
|
+
<zn-data-table data-uri="/test-data" headers='{"name": {"key": "name", "label": "Name"}}'></zn-data-table>`);
|
|
81
|
+
await waitUntil(() => el.shadowRoot!.querySelector('tbody tr.table__row--data'));
|
|
82
|
+
|
|
83
|
+
expect(el.shadowRoot!.querySelector('zn-select[name="rowPerPage"]')).to.not.exist;
|
|
84
|
+
expect(el.shadowRoot!.querySelector('.table__footer')).to.not.exist;
|
|
85
|
+
} finally {
|
|
86
|
+
window.fetch = originalFetch;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// One table per group meant two scrollbars and columns that did not line up between groups
|
|
91
|
+
it('renders grouped rows as group header rows inside a single table', async () => {
|
|
92
|
+
const originalFetch = window.fetch;
|
|
93
|
+
const row = (id: string, status: string) => ({
|
|
94
|
+
id,
|
|
95
|
+
cells: [{text: 'Card ' + id, column: 'name'}, {text: status, column: 'status'}],
|
|
96
|
+
});
|
|
97
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({
|
|
98
|
+
rows: [row('1', 'active'), row('2', 'active'), row('3', 'archived')],
|
|
99
|
+
page: 1,
|
|
100
|
+
perPage: 10,
|
|
101
|
+
total: 3,
|
|
102
|
+
}), {status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const el = await fixture<ZnDataTable>(html`
|
|
106
|
+
<zn-data-table data-uri="/test-data"
|
|
107
|
+
group-by="status"
|
|
108
|
+
groups="active,archived"
|
|
109
|
+
headers='{"name": {"key": "name", "label": "Name"}, "status": {"key": "status", "label": "Status", "hideColumn": true}}'></zn-data-table>`);
|
|
110
|
+
await waitUntil(() => el.shadowRoot!.querySelector('tbody tr.table__row--data'));
|
|
111
|
+
|
|
112
|
+
const root = el.shadowRoot!;
|
|
113
|
+
expect(root.querySelectorAll('table')).to.have.length(1);
|
|
114
|
+
expect(root.querySelectorAll('.table__scroll')).to.have.length(1);
|
|
115
|
+
expect([...root.querySelectorAll('tr.table__row--group')].map(r => r.textContent!.trim()))
|
|
116
|
+
.to.deep.equal(['Active', 'Archived']);
|
|
117
|
+
expect(root.querySelectorAll('tbody tr.table__row--data')).to.have.length(3);
|
|
118
|
+
} finally {
|
|
119
|
+
window.fetch = originalFetch;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
13
123
|
it('does not throw when updating selection without a select-all button', async () => {
|
|
14
124
|
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
15
125
|
|