@kubex/zinc 1.1.128 → 1.1.130
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 +30 -1
- package/dist/web-types.json +1 -1
- package/dist/zn.d.ts +7 -0
- package/dist/zn.min.js +70 -72
- package/package.json +1 -1
- package/src/components/data-table/data-table.component.ts +22 -26
- package/src/components/data-table/data-table.test.ts +46 -0
- package/src/components/page-builder/page-builder.component.ts +15 -2
- package/src/components/page-builder/page-builder.test.ts +32 -0
- package/src/components/page-builder/page.types.ts +2 -1
package/package.json
CHANGED
|
@@ -641,36 +641,32 @@ export default class ZnDataTable extends ZincElement {
|
|
|
641
641
|
}
|
|
642
642
|
}
|
|
643
643
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
<slot name="no-results"></slot>
|
|
650
|
-
</div>`;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
|
-
return html`
|
|
654
|
-
<div class="table--empty">
|
|
655
|
-
<zn-empty-state caption="No Results Found" icon="search">
|
|
656
|
-
<p>Nothing matched your search. Try adjusting your filters.</p>
|
|
657
|
-
</zn-empty-state>
|
|
658
|
-
</div>`;
|
|
659
|
-
}
|
|
644
|
+
// A table narrowed by a search or filter has rows, just none matching them, so
|
|
645
|
+
// its empty state must not claim there is nothing to create
|
|
646
|
+
private get isNarrowed(): boolean {
|
|
647
|
+
return this.search.length > 0 || this.filter.length > 0;
|
|
648
|
+
}
|
|
660
649
|
|
|
661
|
-
|
|
650
|
+
emptyState() {
|
|
651
|
+
if (this.isNarrowed || (this.noInitialLoad && !this._initialLoad)) {
|
|
662
652
|
return html`
|
|
663
653
|
<div class="table--empty">
|
|
664
|
-
<slot name="
|
|
654
|
+
<slot name="no-results">
|
|
655
|
+
<zn-empty-state caption="No Results Found" icon="search">
|
|
656
|
+
<p>Nothing matched your search. Try adjusting your filters.</p>
|
|
657
|
+
</zn-empty-state>
|
|
658
|
+
</slot>
|
|
665
659
|
</div>`;
|
|
666
660
|
}
|
|
667
661
|
|
|
668
662
|
return html`
|
|
669
663
|
<div class="table--empty">
|
|
670
|
-
<
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
664
|
+
<slot name="empty-state">
|
|
665
|
+
<zn-empty-state
|
|
666
|
+
caption="${this.emptyStateCaption ? this.emptyStateCaption : (this.caption ? "No " + this.caption.toLowerCase() + " found" : "No data found")}"
|
|
667
|
+
icon="${this.emptyStateIcon}">
|
|
668
|
+
</zn-empty-state>
|
|
669
|
+
</slot>
|
|
674
670
|
</div>`;
|
|
675
671
|
}
|
|
676
672
|
|
|
@@ -1323,9 +1319,9 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1323
1319
|
size="${ifDefined(size)}"
|
|
1324
1320
|
color="${ifDefined(color)}"
|
|
1325
1321
|
${ref(el => {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1322
|
+
if (!el) return;
|
|
1323
|
+
tokens.forEach(t => el.setAttribute(t, ''));
|
|
1324
|
+
})}
|
|
1329
1325
|
title="${ifDefined(data.title)}"
|
|
1330
1326
|
></zn-icon> ${content}`;
|
|
1331
1327
|
}
|
|
@@ -1345,7 +1341,7 @@ export default class ZnDataTable extends ZincElement {
|
|
|
1345
1341
|
<zn-hover-container placement="${placement}" flip>
|
|
1346
1342
|
${content}
|
|
1347
1343
|
<div slot="content">
|
|
1348
|
-
|
|
1344
|
+
${unsafeHTML(data.hoverContent)}
|
|
1349
1345
|
</div>
|
|
1350
1346
|
</zn-hover-container>`;
|
|
1351
1347
|
}
|
|
@@ -230,6 +230,52 @@ describe('<zn-data-table>', () => {
|
|
|
230
230
|
}
|
|
231
231
|
});
|
|
232
232
|
|
|
233
|
+
it('shows a no-results state when a filter empties a normally loading table', async () => {
|
|
234
|
+
const originalFetch = window.fetch;
|
|
235
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
236
|
+
{status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
const el = await fixture<ZnDataTable>(html`
|
|
240
|
+
<zn-data-table data-uri="/test-data" headers='{"name": {"key": "name", "label": "Name"}}'>
|
|
241
|
+
<div slot="empty-state" id="nothing-created">Nothing created yet</div>
|
|
242
|
+
<div slot="no-results" id="nothing-matched">Nothing matched</div>
|
|
243
|
+
</zn-data-table>`);
|
|
244
|
+
|
|
245
|
+
await waitUntil(() => el.shadowRoot?.querySelector('slot[name="empty-state"]'));
|
|
246
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.exist;
|
|
247
|
+
|
|
248
|
+
el.filter = 'eyJrZXkiOiJ0eXBlIn0=';
|
|
249
|
+
el.refresh();
|
|
250
|
+
await waitUntil(() => el.shadowRoot?.querySelector('slot[name="no-results"]'));
|
|
251
|
+
|
|
252
|
+
expect(el.shadowRoot!.querySelector('slot[name="no-results"]')).to.exist;
|
|
253
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.not.exist;
|
|
254
|
+
} finally {
|
|
255
|
+
window.fetch = originalFetch;
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('shows a no-results state when a search empties a normally loading table', async () => {
|
|
260
|
+
const originalFetch = window.fetch;
|
|
261
|
+
window.fetch = () => Promise.resolve(new Response(JSON.stringify({rows: [], page: 1, perPage: 10, total: 0}),
|
|
262
|
+
{status: 200, headers: {'Content-Type': 'application/json'}}));
|
|
263
|
+
|
|
264
|
+
try {
|
|
265
|
+
const el = await fixture<ZnDataTable>(html`
|
|
266
|
+
<zn-data-table data-uri="/test-data" search="nothing" headers='{"name": {"key": "name", "label": "Name"}}'>
|
|
267
|
+
<div slot="empty-state">Nothing created yet</div>
|
|
268
|
+
</zn-data-table>`);
|
|
269
|
+
|
|
270
|
+
await waitUntil(() => el.shadowRoot?.querySelector('zn-empty-state'));
|
|
271
|
+
|
|
272
|
+
expect(el.shadowRoot!.querySelector('slot[name="empty-state"]')).to.not.exist;
|
|
273
|
+
expect(el.shadowRoot!.querySelector('zn-empty-state')!.getAttribute('caption')).to.equal('No Results Found');
|
|
274
|
+
} finally {
|
|
275
|
+
window.fetch = originalFetch;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
233
279
|
it('exposes displayTemplates as an object property', async () => {
|
|
234
280
|
const el = await fixture<ZnDataTable>(html` <zn-data-table></zn-data-table> `);
|
|
235
281
|
const templates = (el as unknown as {displayTemplates: Record<string, unknown>}).displayTemplates;
|
|
@@ -666,6 +666,19 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
666
666
|
this._commit({ sections: this._patchSection(id, s => ({ ...s, columns: next })) });
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
+
/**
|
|
670
|
+
* Snaps the field back to the width the container will use. A re-render alone
|
|
671
|
+
* would not: once the state already sits at a bound, an out-of-range entry
|
|
672
|
+
* changes nothing to re-render from.
|
|
673
|
+
*/
|
|
674
|
+
private _onColumnsEntered(section: PageSection, input: ZnInput) {
|
|
675
|
+
const type = this.registry.get(section.type);
|
|
676
|
+
if (type?.slotsMax === undefined) return;
|
|
677
|
+
const next = slotColumns({...section, columns: Number(input.value)}, type);
|
|
678
|
+
input.value = String(next);
|
|
679
|
+
this.setSectionColumns(section.id, next);
|
|
680
|
+
}
|
|
681
|
+
|
|
669
682
|
private _removeSection(id: string) {
|
|
670
683
|
if (this._isPinned(id)) return;
|
|
671
684
|
const [removed, sections] = this._extract(id);
|
|
@@ -1229,9 +1242,9 @@ export default class ZnPageBuilder extends ZincElement {
|
|
|
1229
1242
|
label="Slots per row"
|
|
1230
1243
|
min="${type.slotsMin ?? 1}"
|
|
1231
1244
|
max="${type.slotsMax}"
|
|
1232
|
-
help-text="A new row is added as the last one fills."
|
|
1245
|
+
help-text="Between ${type.slotsMin ?? 1} and ${type.slotsMax}. A new row is added as the last one fills."
|
|
1233
1246
|
.value="${String(slotColumns(section, type))}"
|
|
1234
|
-
@zn-change="${(e: Event) => this.
|
|
1247
|
+
@zn-change="${(e: Event) => this._onColumnsEntered(section, e.target as ZnInput)}"></zn-input>`}
|
|
1235
1248
|
${type?.renderConfig
|
|
1236
1249
|
? type.renderConfig(section, data => this._updateSectionData(section.id, data))
|
|
1237
1250
|
: this._form}
|
|
@@ -476,6 +476,38 @@ describe('<zn-page-builder>', () => {
|
|
|
476
476
|
.to.have.length(6);
|
|
477
477
|
});
|
|
478
478
|
|
|
479
|
+
it('should snap an out-of-range width back on every entry and state the range', async () => {
|
|
480
|
+
const el = await fixture<ZnPageBuilder>(html`
|
|
481
|
+
<zn-page-builder config='{"sections":[{"id":"row","type":"category-row","data":{}}]}'>
|
|
482
|
+
<template type="category-row" slot="config" label="Category Row" slots="3" slots-max="4"></template>
|
|
483
|
+
</zn-page-builder>`);
|
|
484
|
+
await el.updateComplete;
|
|
485
|
+
|
|
486
|
+
el.shadowRoot?.querySelector('.canvas zn-page-section-card')?.dispatchEvent(new Event('click'));
|
|
487
|
+
await el.updateComplete;
|
|
488
|
+
|
|
489
|
+
const width = el.shadowRoot!.querySelector<HTMLInputElement>('.inspector__slots')!;
|
|
490
|
+
expect(width.getAttribute('help-text'), 'the control names its range').to.contain('1 and 4');
|
|
491
|
+
|
|
492
|
+
const enter = async (value: string) => {
|
|
493
|
+
width.value = value;
|
|
494
|
+
width.dispatchEvent(new Event('zn-change', {bubbles: true}));
|
|
495
|
+
await el.updateComplete;
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
await enter('9');
|
|
499
|
+
expect(el.state.sections[0].columns, 'clamped to the max').to.equal(4);
|
|
500
|
+
expect(width.value, 'and shown clamped').to.equal('4');
|
|
501
|
+
|
|
502
|
+
await enter('9');
|
|
503
|
+
expect(width.value, 'a second over-max entry snaps back too').to.equal('4');
|
|
504
|
+
expect(el.state.sections[0].columns).to.equal(4);
|
|
505
|
+
|
|
506
|
+
await enter('0');
|
|
507
|
+
expect(el.state.sections[0].columns, 'clamped to the min').to.equal(1);
|
|
508
|
+
expect(width.value).to.equal('1');
|
|
509
|
+
});
|
|
510
|
+
|
|
479
511
|
it('should clamp a container width to the declared bounds', async () => {
|
|
480
512
|
const el = await fixture<ZnPageBuilder>(html`
|
|
481
513
|
<zn-page-builder config='{"sections":[{"id":"row","type":"category-row","data":{},"columns":99}]}'>
|
|
@@ -64,7 +64,8 @@ export const MAX_SLOTS = 24;
|
|
|
64
64
|
export function slotColumns(section: PageSection, type: PageSectionType): number {
|
|
65
65
|
const columns = type.slots ?? 0;
|
|
66
66
|
if (type.slotsMax === undefined) return columns;
|
|
67
|
-
|
|
67
|
+
const chosen = Math.round(Number(section.columns ?? NaN));
|
|
68
|
+
return Math.min(Math.max(Number.isFinite(chosen) ? chosen : columns, type.slotsMin ?? 1), type.slotsMax);
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
/**
|