@kubex/zinc 1.1.121 → 1.1.122

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 (36) hide show
  1. package/dist/custom-elements.json +3110 -2079
  2. package/dist/vscode.html-custom-data.json +200 -178
  3. package/dist/web-types.json +528 -462
  4. package/dist/zn.d.ts +401 -296
  5. package/dist/zn.min.js +1478 -1337
  6. package/docs/pages/components/button.md +9 -0
  7. package/docs/pages/components/data-table-filter.md +60 -4
  8. package/docs/pages/components/data-table-search.md +3 -3
  9. package/docs/pages/components/data-table.md +78 -9
  10. package/docs/pages/components/datepicker.md +10 -0
  11. package/docs/pages/components/menu-item.md +16 -2
  12. package/package.json +1 -1
  13. package/src/components/button/button.component.ts +11 -4
  14. package/src/components/button/button.scss +12 -1
  15. package/src/components/data-table/data-table.component.ts +341 -139
  16. package/src/components/data-table/data-table.scss +105 -42
  17. package/src/components/data-table/data-table.test.ts +110 -0
  18. package/src/components/data-table-filter/data-table-filter.component.ts +487 -49
  19. package/src/components/data-table-filter/data-table-filter.scss +163 -9
  20. package/src/components/data-table-filter/data-table-filter.test.ts +328 -2
  21. package/src/components/data-table-search/data-table-search.component.ts +1 -1
  22. package/src/components/datepicker/datepicker.component.ts +23 -2
  23. package/src/components/datepicker/datepicker.scss +25 -0
  24. package/src/components/datepicker/datepicker.test.ts +28 -1
  25. package/src/components/input/input.component.ts +1 -0
  26. package/src/components/input/input.scss +1 -1
  27. package/src/components/menu/menu.component.ts +2 -0
  28. package/src/components/menu/menu.scss +23 -2
  29. package/src/components/menu-item/menu-item.component.ts +7 -1
  30. package/src/components/menu-item/menu-item.scss +4 -4
  31. package/src/components/menu-item/menu-item.test.ts +39 -1
  32. package/src/components/panel/panel.component.ts +12 -5
  33. package/src/components/panel/panel.scss +3 -3
  34. package/src/components/query-builder/query-builder.component.ts +1 -1
  35. package/src/components/tooltip/tooltip.component.ts +2 -2
  36. package/src/components/tooltip/tooltip.test.ts +70 -2
@@ -5,8 +5,14 @@
5
5
  @include wc.scrollbars;
6
6
  }
7
7
 
8
- .table-container {
9
- overflow: auto;
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,18 +227,31 @@ table {
232
227
  display: flex;
233
228
  flex-direction: row;
234
229
  justify-content: space-between;
235
- align-items: baseline;
236
- padding-block: var(--zn-spacing-small);
230
+ align-items: center;
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: baseline;
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
+ margin: 0;
250
+ overflow: hidden;
251
+ white-space: nowrap;
252
+ text-overflow: ellipsis;
253
+ }
254
+
247
255
  &__right {
248
256
  display: flex;
249
257
  flex-direction: row;
@@ -253,11 +261,31 @@ table {
253
261
  }
254
262
  }
255
263
 
264
+ &__filters {
265
+ padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
266
+
267
+ &[hidden] {
268
+ display: none;
269
+ }
270
+ }
271
+
256
272
  &__footer {
257
273
  display: flex;
258
274
  flex-direction: row;
259
275
  justify-content: space-between;
260
- padding: var(--zn-spacing-small) 0;
276
+ flex-wrap: wrap;
277
+ gap: var(--zn-spacing-small);
278
+ padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
279
+
280
+ // The joined control must never break across lines
281
+ zn-button-group::part(base) {
282
+ flex-wrap: nowrap;
283
+ }
284
+
285
+ // zn-panel's footer supplies its own padding
286
+ &--panel {
287
+ padding: 0;
288
+ }
261
289
 
262
290
  &__right, &__left {
263
291
  display: flex;
@@ -270,40 +298,49 @@ table {
270
298
  display: flex;
271
299
  flex-direction: row;
272
300
  align-items: center;
273
- opacity: 0.8;
274
301
  gap: var(--zn-spacing-x-small);
275
302
 
303
+ p {
304
+ margin: 0;
305
+ color: rgb(var(--zn-text));
306
+ opacity: 0.7;
307
+ white-space: nowrap;
308
+ }
309
+
276
310
  zn-select[name="rowPerPage"] {
277
- max-width: 75px;
311
+ --zn-input-background-color: rgb(var(--zn-panel));
312
+
313
+ width: 66px;
278
314
  }
279
315
 
316
+ // Matches the height of the pagination control it sits beside
280
317
  zn-select[name="rowPerPage"]::part(combobox) {
281
- padding-inline-end: 8px;
318
+ min-height: 36px;
319
+ padding-inline: var(--zn-spacing-x-small);
282
320
  }
283
321
 
284
- zn-select[name="rowPerPage"]::part(expand-icon) {
322
+ zn-select[name="rowPerPage"]::part(expand-icon) {
285
323
  margin-inline-start: 0;
286
324
  }
287
325
  }
288
326
 
289
- &__pagination-buttons {
290
- display: flex;
291
- flex-direction: row;
292
- align-items: center;
293
- gap: var(--zn-spacing-x-small);
327
+ // Sized to match the icon-button arrows it is joined to, but free to grow for large page numbers
328
+ &__pagination-page::part(base) {
329
+ height: 36px;
330
+ min-height: 36px;
331
+ max-height: 36px;
332
+ min-width: 32px;
333
+ padding: 0 var(--zn-spacing-2x-small);
334
+ justify-content: center;
335
+ border-color: rgb(var(--zn-border-color));
336
+ color: rgb(var(--zn-text));
337
+ font-weight: var(--zn-font-weight-normal);
338
+ white-space: nowrap;
294
339
  }
295
340
 
296
341
  &__pagination-page--active::part(base) {
297
342
  background-color: var(--zn-color-neutral-300);
298
343
  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
344
  }
308
345
  }
309
346
 
@@ -330,6 +367,30 @@ table {
330
367
  }
331
368
  }
332
369
 
370
+ zn-menu {
371
+ --zn-menu-min-width: var(--zn-size-spanel);
372
+ }
373
+
374
+ // A table's header and footer are chrome around the rows, so they sit tighter than
375
+ // the panel's own content padding. The header's trailing edge tucks in further still,
376
+ // since the controls there are already inset by their own button padding.
377
+ zn-panel {
378
+ --zn-panel-header-padding: var(--zn-spacing-x-small) var(--zn-spacing-x-small)
379
+ var(--zn-spacing-x-small) var(--zn-spacing-medium);
380
+ --zn-panel-footer-padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
381
+ }
382
+
383
+ // Closes the rows off from the footer, which the panel border would otherwise do
384
+ zn-panel::part(footer) {
385
+ border-top: 1px solid rgb(var(--zn-border-color));
386
+ }
387
+
388
+ // Dropdowns sit over dense rows here, so they need a solid surface rather than
389
+ // zn-menu's translucent default
390
+ zn-menu::part(base) {
391
+ background-color: rgb(var(--zn-panel));
392
+ }
393
+
333
394
  .reduced-opacity .table tbody {
334
395
  opacity: 0.5;
335
396
  }
@@ -417,16 +478,18 @@ table:has(tbody tr:first-of-type.table__row--selected) thead tr th {
417
478
  border-bottom-color: var(--zn-color-purple-600) !important;
418
479
  }
419
480
 
420
- .table-group__title {
481
+ tbody tr.table__row--group th {
482
+ padding: var(--zn-spacing-small) var(--zn-spacing-medium);
483
+ text-align: left;
484
+ white-space: nowrap;
485
+ background-color: rgb(var(--zn-panel));
486
+ color: rgba(var(--zn-text-panel-title), 100%);
421
487
  font-weight: var(--zn-font-weight-semibold);
422
488
  font-size: var(--zn-font-size-large);
423
- color: rgba(var(--zn-text-panel-title), 100%);
424
- line-height: var(--zn-line-height-looser);
425
- padding-left: var(--zn-spacing-medium);
426
- margin: 0;
427
- overflow: hidden;
428
- white-space: nowrap;
429
- text-overflow: ellipsis;
489
+ }
490
+
491
+ tbody tr.table__row--group:not(:first-of-type) th {
492
+ border-top: 1px solid rgb(var(--zn-border-color));
430
493
  }
431
494
 
432
495
  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