@kubex/zinc 1.1.131 → 1.1.133

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.
@@ -333,4 +333,46 @@ describe('<zn-data-table-filter>', () => {
333
333
  expect(pills(el)).to.deep.equal([]);
334
334
  expect(el.value).to.equal('');
335
335
  });
336
+
337
+ // Setting `value` (the inverse of emitChange) rebuilds the active pills, so a filter restored
338
+ // from a shared URL renders exactly as it was when encoded.
339
+ describe('restore from value', () => {
340
+ const encode = (conditions: {key: string; comparator: string; value: string}[]) =>
341
+ btoa(JSON.stringify(conditions));
342
+
343
+ it('rebuilds the active pills from an encoded value', async () => {
344
+ const value = encode([
345
+ {key: 'status', comparator: 'eq', value: 'active'},
346
+ {key: 'email', comparator: 'contains', value: 'acme'},
347
+ ]);
348
+
349
+ const el = await fixture<ZnDataTableFilter>(html`
350
+ <zn-data-table-filter .filters="${FILTERS}" .value="${value}"></zn-data-table-filter>`);
351
+ await el.updateComplete;
352
+
353
+ expect(pills(el)).to.deep.equal(['Status: Active', 'Email: acme']);
354
+ });
355
+
356
+ it('renders no pills for an absent value', async () => {
357
+ const el = await fixture<ZnDataTableFilter>(html`
358
+ <zn-data-table-filter .filters="${FILTERS}"></zn-data-table-filter>`);
359
+ await el.updateComplete;
360
+
361
+ expect(pills(el)).to.deep.equal([]);
362
+ });
363
+
364
+ // The production flow sets `value` after `filters` (and after default-filters seeds an empty
365
+ // pill), so a later value must still win over the valueless default pill.
366
+ it('lets a value set after default-filters win over the empty default pill', async () => {
367
+ const el = await fixture<ZnDataTableFilter>(html`
368
+ <zn-data-table-filter default-filters="role" .filters="${FILTERS}"></zn-data-table-filter>`);
369
+ await el.updateComplete;
370
+ expect(pills(el)).to.deep.equal(['Role']);
371
+
372
+ el.value = encode([{key: 'status', comparator: 'eq', value: 'active'}]);
373
+ await el.updateComplete;
374
+
375
+ expect(pills(el)).to.deep.equal(['Status: Active']);
376
+ });
377
+ });
336
378
  });
@@ -39,7 +39,7 @@ type AllowedInputElement =
39
39
  * @property {string} placeholder - The placeholder text for the search input (default: "Search...").
40
40
  * @property {string} helpText - Help text, shown from an information icon inside the search input.
41
41
  * @property {string} searchUri - Optional URI to use for search operations.
42
- * @property {number} debounceDelay - The delay in milliseconds before triggering a search (default: 500).
42
+ * @property {number} debounceDelay - The delay in milliseconds before triggering a search (default: 350).
43
43
  */
44
44
  export default class ZnDataTableSearch extends ZincElement implements ZincFormControl {
45
45
  static styles: CSSResultGroup = unsafeCSS(styles);
@@ -97,10 +97,6 @@ export default class ZnDataTableSearch extends ZincElement implements ZincFormCo
97
97
  */
98
98
  getFormData(): Record<string, any> {
99
99
  const params: Record<string, any> = {};
100
- const slot = this.shadowRoot?.querySelector('slot');
101
- if (!slot) return params;
102
-
103
- const elements = slot.assignedElements({flatten: true});
104
100
  const allowedInputs = [
105
101
  'zn-input',
106
102
  'zn-select',
@@ -115,15 +111,22 @@ export default class ZnDataTableSearch extends ZincElement implements ZincFormCo
115
111
  'zn-input-group',
116
112
  ];
117
113
 
118
- elements.forEach((element) => {
119
- if (allowedInputs.includes(element.tagName.toLowerCase())) {
120
- const input = element as AllowedInputElement;
121
- const value = input.value as string || element.getAttribute('value');
122
- const name = input.name || element.getAttribute('name');
123
- if (name) {
124
- params[name] = value;
125
- }
114
+ const collect = (element: Element): void => {
115
+ if (!allowedInputs.includes(element.tagName.toLowerCase())) return;
116
+ const input = element as AllowedInputElement;
117
+ const value = input.value as string;
118
+ const name = input.name || element.getAttribute('name');
119
+ if (name) {
120
+ params[name] = value;
126
121
  }
122
+ };
123
+
124
+ const slots = this.shadowRoot?.querySelectorAll('slot');
125
+ slots?.forEach((slot) => {
126
+ slot.assignedElements({flatten: true}).forEach((element) => {
127
+ collect(element);
128
+ element.querySelectorAll(allowedInputs.join(',')).forEach((descendant) => collect(descendant));
129
+ });
127
130
  });
128
131
 
129
132
  return params;
@@ -1,5 +1,7 @@
1
+ import '../../../dist/zn.min.js';
1
2
  import {expect, fixture, html} from '@open-wc/testing';
2
3
  import type ZnDataTableSearch from './data-table-search.component';
4
+ import type ZnSelect from '../select/select.component';
3
5
 
4
6
  describe('<zn-data-table-search>', () => {
5
7
  it('should render a component', async () => {
@@ -8,4 +10,26 @@ describe('<zn-data-table-search>', () => {
8
10
 
9
11
  expect(el).to.exist;
10
12
  });
13
+
14
+ // getFormData must read each slotted field's live `.value` property, not a stale `value` attribute.
15
+ // On a zn-select the `value` attribute even binds to a separate default-value property, so reading
16
+ // the attribute would return the wrong (initial) value.
17
+ it('reads a slotted field\'s live value property rather than its stale value attribute', async () => {
18
+ const el = await fixture<ZnDataTableSearch>(html`
19
+ <zn-data-table-search>
20
+ <zn-select name="status" value="open">
21
+ <zn-option value="open">Open</zn-option>
22
+ <zn-option value="closed">Closed</zn-option>
23
+ </zn-select>
24
+ </zn-data-table-search>`);
25
+
26
+ const select = el.querySelector<ZnSelect>('zn-select')!;
27
+ await select.updateComplete;
28
+
29
+ // Diverge the live property from the attribute the element was rendered with.
30
+ select.value = 'closed';
31
+ expect(select.getAttribute('value')).to.equal('open');
32
+
33
+ expect(el.getFormData().status).to.equal('closed');
34
+ });
11
35
  });
@@ -92,27 +92,25 @@ export default class ZnPanel extends ZincElement {
92
92
  this.resizeObserver = null;
93
93
  }
94
94
 
95
- private get bodySlot(): HTMLSlotElement | null {
96
- return this.renderRoot?.querySelector('.panel__body > slot') ?? null;
97
- }
98
-
99
- // Slotted content can grow without the body's own box changing, so the observers go on the
100
- // slotted elements rather than the body.
101
95
  private observeBodyContent() {
102
- const slot = this.bodySlot;
103
- if (!slot) return;
96
+ const body = this.renderRoot?.querySelector('.panel__body');
97
+ if (!body) return;
104
98
 
105
99
  this.resizeObserver ??= new ResizeObserver(() => this.measureBodyContent());
106
100
  this.resizeObserver.disconnect();
107
- slot.assignedElements({flatten: true}).forEach(el => this.resizeObserver!.observe(el));
101
+ this.resizeObserver.observe(body);
108
102
  this.measureBodyContent();
109
103
  }
110
104
 
105
+ // Measured on the body, not on the slotted elements: `display: contents` hosts such as zn-stat
106
+ // render content but have no box of their own to read or observe.
111
107
  private measureBodyContent() {
112
- const nodes = this.bodySlot?.assignedNodes({flatten: true}) ?? [];
113
- this.bodyEmpty = !nodes.some(node => node.nodeType === Node.TEXT_NODE
114
- ? node.textContent!.trim() !== ''
115
- : (node as Element).getBoundingClientRect().height > 0);
108
+ const body = this.renderRoot?.querySelector('.panel__body');
109
+ if (!body) return;
110
+
111
+ const style = getComputedStyle(body);
112
+ const padding = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
113
+ this.bodyEmpty = body.scrollHeight - padding <= 0;
116
114
  }
117
115
 
118
116
  protected render(): unknown {
@@ -1,5 +1,5 @@
1
1
  import '../../../dist/zn.min.js';
2
- import { expect, fixture, html, waitUntil } from '@open-wc/testing';
2
+ import { aTimeout, expect, fixture, html, waitUntil } from '@open-wc/testing';
3
3
 
4
4
  describe('<zn-panel>', () => {
5
5
  it('should render a component', async () => {
@@ -41,6 +41,20 @@ describe('<zn-panel>', () => {
41
41
  expect(el.shadowRoot!.querySelector('.panel')!.classList.contains('panel--empty')).to.be.true;
42
42
  });
43
43
 
44
+ it('keeps the padded body when slotted content has no box of its own', async () => {
45
+ const el = await fixture(html` <zn-panel caption="Example">
46
+ <div style="display: contents">
47
+ <div style="height: 20px"></div>
48
+ </div>
49
+ </zn-panel> `);
50
+ const header = el.shadowRoot!.querySelector('.panel__header')!;
51
+
52
+ await aTimeout(50);
53
+
54
+ expect(header.classList.contains('panel__header--underline')).to.be.true;
55
+ expect(el.shadowRoot!.querySelector('.panel')!.classList.contains('panel--empty')).to.be.false;
56
+ });
57
+
44
58
  it('restores the header underline when slotted content gains height', async () => {
45
59
  const el = await fixture(html` <zn-panel caption="Example">
46
60
  <div id="wrapper"></div>
@@ -169,3 +169,13 @@
169
169
  .switch__wrapper--inline.switch__wrapper--label-right label {
170
170
  flex-direction: row-reverse;
171
171
  }
172
+
173
+ /* Disabled */
174
+ .switch__wrapper--disabled {
175
+ opacity: 0.5;
176
+
177
+ label,
178
+ .switch__input-wrapper {
179
+ cursor: not-allowed;
180
+ }
181
+ }