@kubex/zinc 1.0.113 → 1.0.115

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 (101) hide show
  1. package/.github/workflows/eleventy_build.yml +5 -5
  2. package/.github/workflows/js_build_and_deploy.yaml +1 -1
  3. package/dist/custom-elements.json +2422 -190
  4. package/dist/vscode.html-custom-data.json +86 -5
  5. package/dist/web-types.json +251 -9
  6. package/dist/zn.d.ts +518 -340
  7. package/dist/zn.min.css +1 -1
  8. package/dist/zn.min.js +960 -882
  9. package/docs/eleventy.config.cjs +4 -2
  10. package/docs/pages/components/header.md +13 -0
  11. package/docs/pages/components/inline-edit.md +20 -0
  12. package/docs/pages/components/item.md +12 -1
  13. package/docs/pages/components/page.md +149 -0
  14. package/docs/pages/components/progress-bar.md +16 -0
  15. package/docs/pages/components/select.md +25 -0
  16. package/docs/pages/components/split-pane.md +6 -7
  17. package/docs/pages/components/tabs.md +59 -9
  18. package/package.json +8 -5
  19. package/scripts/build.js +40 -19
  20. package/scss/_global-spacing.scss +12 -1
  21. package/scss/_root.scss +34 -2
  22. package/scss/mixins/_spacing-mixins.scss +1 -1
  23. package/scss/shared/layout.scss +15 -2
  24. package/scss/themes/_light.scss +1 -1
  25. package/src/components/absolute-container/absolute-container.component.ts +9 -25
  26. package/src/components/animated-button/animated-button.test.ts +8 -8
  27. package/src/components/button/button.component.ts +3 -1
  28. package/src/components/button/button.scss +5 -0
  29. package/src/components/button-menu/button-menu.component.ts +36 -21
  30. package/src/components/button-menu/button-menu.test.ts +55 -1
  31. package/src/components/chart/chart.component.ts +7 -7
  32. package/src/components/collapsible/collapsible.component.ts +7 -14
  33. package/src/components/cols/cols.scss +1 -1
  34. package/src/components/content-block/content-block.component.ts +11 -33
  35. package/src/components/data-select/data-select.component.ts +7 -11
  36. package/src/components/data-select/data-select.scss +1 -0
  37. package/src/components/data-table/data-table.component.ts +10 -14
  38. package/src/components/dialog/dialog.scss +4 -0
  39. package/src/components/editor/modules/toolbar/toolbar.component.ts +5 -9
  40. package/src/components/expanding-action/expanding-action.component.ts +37 -43
  41. package/src/components/form-group/form-group.component.ts +32 -27
  42. package/src/components/form-group/form-group.scss +1 -0
  43. package/src/components/header/header.component.ts +6 -5
  44. package/src/components/header/header.scss +28 -26
  45. package/src/components/inline-edit/inline-edit.component.ts +13 -1
  46. package/src/components/inline-edit/inline-edit.test.ts +50 -0
  47. package/src/components/input/input.test.ts +77 -0
  48. package/src/components/input-group/input-group.test.ts +2 -10
  49. package/src/components/item/item.component.ts +12 -6
  50. package/src/components/item/item.scss +1 -1
  51. package/src/components/item/item.test.ts +8 -0
  52. package/src/components/navbar/navbar.component.ts +222 -48
  53. package/src/components/navbar/navbar.scss +134 -21
  54. package/src/components/navbar/navbar.test.ts +114 -9
  55. package/src/components/option/option.scss +5 -7
  56. package/src/components/option/option.test.ts +30 -0
  57. package/src/components/page/index.ts +13 -0
  58. package/src/components/page/page.component.ts +406 -0
  59. package/src/components/page/page.scss +325 -0
  60. package/src/components/page/page.test.ts +397 -0
  61. package/src/components/page-nav/page-nav.scss +7 -5
  62. package/src/components/pane/pane.component.ts +2 -2
  63. package/src/components/pane/pane.scss +0 -1
  64. package/src/components/pane/pane.test.ts +31 -0
  65. package/src/components/panel/panel.scss +5 -16
  66. package/src/components/progress-bar/progress-bar.component.ts +5 -2
  67. package/src/components/progress-bar/progress-bar.scss +17 -1
  68. package/src/components/progress-bar/progress-bar.test.ts +12 -0
  69. package/src/components/scroll-container/scroll-container.component.ts +22 -21
  70. package/src/components/select/select.component.ts +243 -39
  71. package/src/components/select/select.scss +36 -5
  72. package/src/components/select/select.test.ts +533 -0
  73. package/src/components/settings-container/settings-container.component.ts +15 -19
  74. package/src/components/settings-container/settings-container.scss +8 -8
  75. package/src/components/sidebar/sidebar.component.ts +11 -11
  76. package/src/components/simple-chart/simple-chart.component.ts +6 -7
  77. package/src/components/sp/sp.scss +46 -15
  78. package/src/components/sp/sp.test.ts +15 -0
  79. package/src/components/split-pane/split-pane.component.ts +153 -26
  80. package/src/components/split-pane/split-pane.scss +89 -13
  81. package/src/components/split-pane/split-pane.test.ts +187 -0
  82. package/src/components/style/style.component.ts +8 -0
  83. package/src/components/tab/index.ts +13 -0
  84. package/src/components/tab/tab.component.ts +25 -0
  85. package/src/components/tab/tab.scss +7 -0
  86. package/src/components/table/table.component.ts +7 -2
  87. package/src/components/table/table.scss +1 -0
  88. package/src/components/tabs/tabs.component.ts +75 -70
  89. package/src/components/tabs/tabs.scss +1 -1
  90. package/src/components/textarea/textarea.component.ts +5 -8
  91. package/src/components/tile/tile.scss +2 -2
  92. package/src/components/translation-group/translation-group.component.ts +15 -14
  93. package/src/components/translations/translations.component.ts +18 -17
  94. package/src/components/translations/translations.scss +1 -0
  95. package/src/components/translations/translations.test.ts +3 -2
  96. package/src/internal/form.ts +234 -0
  97. package/src/internal/theme.ts +26 -46
  98. package/src/internal/zinc-element.ts +12 -7
  99. package/src/utilities/form.ts +1 -0
  100. package/src/wc.scss +1 -1
  101. package/src/zinc.ts +3 -0
@@ -0,0 +1,397 @@
1
+ import '../../../dist/zn.min.js';
2
+ import {aTimeout, expect, fixture, html} from '@open-wc/testing';
3
+ import type ZnPage from './page.component';
4
+
5
+ describe('<zn-page>', () => {
6
+ it('renders header actions and generated tabs', async () => {
7
+ const el = await fixture<ZnPage>(html`
8
+ <zn-page caption="Page Title" summary="Page Summary">
9
+ <zn-button slot="actions" type="primary" size="small" href="/ui">UI Examples</zn-button>
10
+
11
+ <zn-tab caption="Overview">Overview Content</zn-tab>
12
+ <zn-tab caption="Tab One">Tab One Content</zn-tab>
13
+ <zn-tab caption="Tab Two" id="tab-2">Tab Two Content</zn-tab>
14
+ <zn-tab caption="Dynamic Tab" uri="/tab-three"></zn-tab>
15
+ </zn-page>
16
+ `);
17
+ await aTimeout(20);
18
+
19
+ const header = el.shadowRoot!.querySelector('[part="header"]')!;
20
+ const caption = el.shadowRoot!.querySelector('[part="header-caption"]')!;
21
+ const description = el.shadowRoot!.querySelector('[part="header-description"]')!;
22
+ const headerRight = el.shadowRoot!.querySelector('[part="header-right"]')!;
23
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
24
+ const navItems = navbar.shadowRoot!.querySelectorAll('li:not(.more)');
25
+ const actionsSlot = el.shadowRoot!.querySelector<HTMLSlotElement>('slot[name="actions"]')!;
26
+ const overviewTab = el.querySelector('zn-tab[caption="Overview"]')!;
27
+ const overviewSlot = el.shadowRoot!.querySelector<HTMLSlotElement>('slot[name="page-tab-0"]')!;
28
+
29
+ expect(header).to.exist;
30
+ expect(headerRight).to.exist;
31
+ expect(caption.textContent?.trim()).to.equal('Page Title');
32
+ expect(description.textContent?.trim()).to.equal('Page Summary');
33
+ expect(actionsSlot.assignedElements()[0].tagName).to.equal('ZN-BUTTON');
34
+ expect(overviewTab.parentElement).to.equal(el);
35
+ expect(overviewSlot.assignedElements()[0]).to.equal(overviewTab);
36
+ expect(navbar.hasAttribute('hide-one')).to.equal(true);
37
+ expect(navItems[0].getAttribute('tab')).to.equal('');
38
+ expect(navItems[1].getAttribute('tab')).to.equal('tab-one');
39
+ expect(navItems[2].getAttribute('tab')).to.equal('tab-2');
40
+ expect(navItems[3].getAttribute('tab-uri')).to.equal('/tab-three');
41
+ });
42
+
43
+ it('pushes nested expanding actions into the generated navbar', async () => {
44
+ const el = await fixture<ZnPage>(html`
45
+ <zn-page caption="Page Title">
46
+ <zn-tab caption="Overview">
47
+ <div>
48
+ <zn-expanding-action icon="notifications" method="drop">
49
+ <p>Notification content</p>
50
+ </zn-expanding-action>
51
+ </div>
52
+ </zn-tab>
53
+ </zn-page>
54
+ `);
55
+ await aTimeout(40);
56
+
57
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
58
+ const expandingAction = navbar.shadowRoot!.querySelector('zn-expanding-action')!;
59
+
60
+ expect(navbar).to.exist;
61
+ expect(expandingAction).to.exist;
62
+ expect(getComputedStyle(navbar).display).to.not.equal('none');
63
+ });
64
+
65
+ it('selects page tab panels when navigation items are clicked', async () => {
66
+ const el = await fixture<ZnPage>(html`
67
+ <zn-page caption="Page Title">
68
+ <zn-tab caption="Overview">Overview Content</zn-tab>
69
+ <zn-tab caption="Details">Details Content</zn-tab>
70
+ </zn-page>
71
+ `);
72
+ await aTimeout(40);
73
+
74
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
75
+ const navItems = Array.from(navbar.shadowRoot!.querySelectorAll<HTMLElement>('li:not(.more)'));
76
+
77
+ navItems[1].click();
78
+ await aTimeout(40);
79
+
80
+ const selectedPanel = el.shadowRoot!.querySelector<HTMLElement>('#content > div[selected]')!;
81
+ const selectedSlot = selectedPanel.querySelector<HTMLSlotElement>('slot')!;
82
+ const selectedTab = selectedSlot.assignedElements()[0] as HTMLElement;
83
+ expect(selectedPanel.id).to.equal('details');
84
+ expect(selectedTab.textContent?.trim()).to.equal('Details Content');
85
+ expect(getComputedStyle(selectedTab).display).to.not.equal('none');
86
+ });
87
+
88
+ it('creates uri tab panels from page navigation items', async () => {
89
+ const el = await fixture<ZnPage>(html`
90
+ <zn-page caption="Page Title">
91
+ <zn-tab caption="Overview">Overview Content</zn-tab>
92
+ <zn-tab caption="Dynamic Tab" uri="/tab-three"></zn-tab>
93
+ </zn-page>
94
+ `);
95
+ await aTimeout(40);
96
+
97
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
98
+ const dynamicItem = navbar.shadowRoot!.querySelector<HTMLElement>('li[tab-uri="/tab-three"]')!;
99
+
100
+ dynamicItem.click();
101
+ await aTimeout(40);
102
+
103
+ const dynamicPanel = el.shadowRoot!.querySelector<HTMLElement>('#content > div[data-self-uri="/tab-three"]')!;
104
+ expect(dynamicPanel).to.exist;
105
+ expect(dynamicPanel.hasAttribute('selected')).to.equal(true);
106
+ });
107
+
108
+ it('loads uri tabs into the innermost page when pages are nested three layers deep', async () => {
109
+ const el = await fixture<ZnPage>(html`
110
+ <zn-page caption="Outer Page">
111
+ <zn-tab caption="Outer One">
112
+ <zn-page caption="Middle Page" nested>
113
+ <zn-tab caption="Middle One">
114
+ <zn-page caption="Inner Page" nested>
115
+ <zn-tab caption="Inner Static">Inner Static Content</zn-tab>
116
+ <zn-tab caption="Inner Dynamic" uri="/inner-dynamic"></zn-tab>
117
+ </zn-page>
118
+ </zn-tab>
119
+ <zn-tab caption="Middle Two">Middle Two Content</zn-tab>
120
+ </zn-page>
121
+ </zn-tab>
122
+ <zn-tab caption="Outer Two">Outer Two Content</zn-tab>
123
+ </zn-page>
124
+ `);
125
+ await aTimeout(80);
126
+
127
+ const middlePage = el.querySelector<ZnPage>('zn-page')!;
128
+ const innerPage = middlePage.querySelector<ZnPage>('zn-page')!;
129
+ const innerNavbar = innerPage.shadowRoot!.querySelector('zn-navbar')!;
130
+ const innerDynamicItem = innerNavbar.shadowRoot!.querySelector<HTMLElement>('li[tab-uri="/inner-dynamic"]')!;
131
+
132
+ innerDynamicItem.click();
133
+ await aTimeout(60);
134
+
135
+ const outerDynamicPanel = el.shadowRoot!.querySelector('#content > div[data-self-uri="/inner-dynamic"]');
136
+ const middleDynamicPanel = middlePage.shadowRoot!.querySelector('#content > div[data-self-uri="/inner-dynamic"]');
137
+ const innerDynamicPanel = innerPage.shadowRoot!.querySelector<HTMLElement>('#content > div[data-self-uri="/inner-dynamic"]')!;
138
+
139
+ expect(outerDynamicPanel).to.equal(null);
140
+ expect(middleDynamicPanel).to.equal(null);
141
+ expect(innerDynamicPanel).to.exist;
142
+ expect(innerDynamicPanel.hasAttribute('selected')).to.equal(true);
143
+ });
144
+
145
+ it('uses an explicit zn-tab for overview content', async () => {
146
+ const el = await fixture<ZnPage>(html`
147
+ <zn-page caption="Page Title">
148
+ <zn-tab caption="Overview">Overview Content</zn-tab>
149
+ <zn-tab caption="Details">Details Content</zn-tab>
150
+ </zn-page>
151
+ `);
152
+ await aTimeout(20);
153
+
154
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
155
+ const navItems = navbar.shadowRoot!.querySelectorAll('li:not(.more)');
156
+ const overview = el.querySelector('zn-tab')!;
157
+
158
+ expect(navItems[0].textContent?.trim()).to.equal('Overview');
159
+ expect(navItems[0].getAttribute('tab')).to.equal('');
160
+ expect(overview.getAttribute('id')).to.equal('');
161
+ expect(overview.getAttribute('slot')).to.equal('page-tab-0');
162
+ expect(overview.textContent?.trim()).to.equal('Overview Content');
163
+ });
164
+
165
+ it('supports zn-header location, entity, and previous path attributes', async () => {
166
+ const el = await fixture<ZnPage>(html`
167
+ <zn-page
168
+ caption="Page Title"
169
+ full-location="/full/path"
170
+ entity-id="entity-123"
171
+ entity-id-show
172
+ previous-path="/previous"
173
+ previous-target="main">
174
+ <zn-tab caption="Overview">Overview Content</zn-tab>
175
+ </zn-page>
176
+ `);
177
+ await aTimeout(20);
178
+
179
+ const overlay = el.shadowRoot!.querySelector<HTMLElement>('.alt-overlay')!;
180
+ const fullLocationLink = overlay.querySelector<HTMLAnchorElement>('a')!;
181
+ const copyButtons = overlay.querySelectorAll('zn-copy-button');
182
+ const previousLink = el.shadowRoot!.querySelector<HTMLAnchorElement>('.caption__back')!;
183
+
184
+ expect(overlay.classList.contains('alt-overlay--visible')).to.equal(true);
185
+ expect(fullLocationLink.getAttribute('href')).to.equal('/full/path');
186
+ expect(copyButtons[0].getAttribute('value')).to.equal('entity-123');
187
+ expect(copyButtons[1].getAttribute('value')).to.equal('/full/path');
188
+ expect(previousLink.getAttribute('href')).to.equal('/previous');
189
+ expect(previousLink.getAttribute('data-target')).to.equal('main');
190
+ });
191
+
192
+ it('orders the empty tab id first and then by priority', async () => {
193
+ const el = await fixture<ZnPage>(html`
194
+ <zn-page caption="Page Title">
195
+ <zn-tab caption="Later">Later Content</zn-tab>
196
+ <zn-tab caption="Second" priority="20">Second Content</zn-tab>
197
+ <zn-tab caption="Overview">Overview Content</zn-tab>
198
+ <zn-tab caption="First" priority="10">First Content</zn-tab>
199
+ <zn-tab caption="Dynamic" priority="15" uri="/dynamic"></zn-tab>
200
+ </zn-page>
201
+ `);
202
+ await aTimeout(20);
203
+
204
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
205
+ const navItems = Array.from(navbar.shadowRoot!.querySelectorAll('li:not(.more)'));
206
+ expect(navItems.map(item => item.textContent?.trim())).to.deep.equal([
207
+ 'Overview',
208
+ 'First',
209
+ 'Dynamic',
210
+ 'Second',
211
+ 'Later'
212
+ ]);
213
+ expect(navItems[0].getAttribute('tab')).to.equal('');
214
+ expect(navItems[2].getAttribute('tab-uri')).to.equal('/dynamic');
215
+ expect(el.querySelector<HTMLElement>('zn-tab[caption="Dynamic"]')!.getAttribute('id')).to.equal('dynamic');
216
+ });
217
+
218
+ it('preserves source order for tabs without priority', async () => {
219
+ const el = await fixture<ZnPage>(html`
220
+ <zn-page caption="Page Title">
221
+ <zn-tab caption="Zoo">Zoo Content</zn-tab>
222
+ <zn-tab caption="Accounts">Accounts Content</zn-tab>
223
+ <zn-tab caption="Billing">Billing Content</zn-tab>
224
+ </zn-page>
225
+ `);
226
+ await aTimeout(20);
227
+
228
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
229
+ const navItems = Array.from(navbar.shadowRoot!.querySelectorAll('li:not(.more)'));
230
+
231
+ expect(navItems.map(item => item.textContent?.trim())).to.deep.equal([
232
+ 'Zoo',
233
+ 'Accounts',
234
+ 'Billing'
235
+ ]);
236
+ expect(el.querySelector<HTMLElement>('zn-tab[caption="Accounts"]')!.getAttribute('id')).to.equal('accounts');
237
+ });
238
+
239
+ it('preserves explicit empty tab ids', async () => {
240
+ const el = await fixture<ZnPage>(html`
241
+ <zn-page caption="Page Title">
242
+ <zn-tab caption="Custom Overview" id="">Overview Content</zn-tab>
243
+ <zn-tab caption="Details">Details Content</zn-tab>
244
+ </zn-page>
245
+ `);
246
+ await aTimeout(20);
247
+
248
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
249
+ const navItems = navbar.shadowRoot!.querySelectorAll('li:not(.more)');
250
+
251
+ expect(navItems[0].textContent?.trim()).to.equal('Custom Overview');
252
+ expect(navItems[0].getAttribute('tab')).to.equal('');
253
+ });
254
+
255
+ it('shows a header border when scrolled without visible navigation', async () => {
256
+ const el = await fixture<ZnPage>(html`
257
+ <zn-page caption="Page Title" style="height: 120px;">
258
+ <zn-tab caption="Overview">
259
+ <div style="height: 400px;">Overview Content</div>
260
+ </zn-tab>
261
+ </zn-page>
262
+ `);
263
+ await aTimeout(20);
264
+
265
+ const page = el.shadowRoot!.querySelector<HTMLElement>('.page')!;
266
+ const header = el.shadowRoot!.querySelector<HTMLElement>('.page__header')!;
267
+
268
+ expect(header.classList.contains('page__header--scrolled-no-navigation')).to.equal(false);
269
+
270
+ page.scrollTop = 10;
271
+ page.dispatchEvent(new Event('scroll'));
272
+ await el.updateComplete;
273
+
274
+ expect(header.classList.contains('page__header--scrolled-no-navigation')).to.equal(false);
275
+
276
+ page.scrollTop = 25;
277
+ page.dispatchEvent(new Event('scroll'));
278
+ await el.updateComplete;
279
+
280
+ expect(header.classList.contains('page__header--scrolled-no-navigation')).to.equal(true);
281
+
282
+ page.scrollTop = 0;
283
+ page.dispatchEvent(new Event('scroll'));
284
+ await el.updateComplete;
285
+
286
+ expect(header.classList.contains('page__header--scrolled-no-navigation')).to.equal(false);
287
+ });
288
+
289
+ it('does not show a header border when navigation is visible', async () => {
290
+ const el = await fixture<ZnPage>(html`
291
+ <zn-page caption="Page Title" style="height: 120px;">
292
+ <zn-tab caption="Overview">
293
+ <div style="height: 400px;">Overview Content</div>
294
+ </zn-tab>
295
+ <zn-tab caption="Details">Details Content</zn-tab>
296
+ </zn-page>
297
+ `);
298
+ await aTimeout(20);
299
+
300
+ const page = el.shadowRoot!.querySelector<HTMLElement>('.page')!;
301
+ const header = el.shadowRoot!.querySelector<HTMLElement>('.page__header')!;
302
+
303
+ page.scrollTop = 25;
304
+ page.dispatchEvent(new Event('scroll'));
305
+ await el.updateComplete;
306
+
307
+ expect(header.classList.contains('page__header--scrolled-no-navigation')).to.equal(false);
308
+ });
309
+
310
+ it('reflects nested and modal modes', async () => {
311
+ const el = await fixture<ZnPage>(html`
312
+ <zn-page caption="Page Title" nested modal>
313
+ <zn-tab caption="Overview">Overview Content</zn-tab>
314
+ </zn-page>
315
+ `);
316
+ await aTimeout(20);
317
+
318
+ expect(el.hasAttribute('nested')).to.equal(true);
319
+ expect(el.hasAttribute('modal')).to.equal(true);
320
+ });
321
+
322
+ it('uses the page background for the sticky header surface', async () => {
323
+ const el = await fixture<ZnPage>(html`
324
+ <div style="--zn-body: 12, 34, 56; --zn-panel: 1, 2, 3;">
325
+ <zn-page caption="Page Title" modal>
326
+ <zn-tab caption="Overview">Overview Content</zn-tab>
327
+ </zn-page>
328
+ </div>
329
+ `);
330
+ await aTimeout(20);
331
+
332
+ const page = el.querySelector<ZnPage>('zn-page')!;
333
+ const scroller = page.shadowRoot!.querySelector<HTMLElement>('.page')!;
334
+ const header = page.shadowRoot!.querySelector<HTMLElement>('.page__header')!;
335
+
336
+ expect(getComputedStyle(scroller).backgroundColor).to.equal('rgb(12, 34, 56)');
337
+ expect(getComputedStyle(header).backgroundColor).to.equal('rgba(12, 34, 56, 0.95)');
338
+ });
339
+
340
+ it('uses the panel background when placed inside a panel', async () => {
341
+ const el = await fixture<HTMLElement>(html`
342
+ <div style="--zn-body: 12, 34, 56; --zn-panel: 1, 2, 3;">
343
+ <zn-panel flush>
344
+ <zn-page caption="Page Title" modal>
345
+ <zn-tab caption="Overview">Overview Content</zn-tab>
346
+ </zn-page>
347
+ </zn-panel>
348
+ </div>
349
+ `);
350
+ await aTimeout(20);
351
+
352
+ const page = el.querySelector<ZnPage>('zn-page')!;
353
+ const scroller = page.shadowRoot!.querySelector<HTMLElement>('.page')!;
354
+ const header = page.shadowRoot!.querySelector<HTMLElement>('.page__header')!;
355
+
356
+ expect(getComputedStyle(scroller).backgroundColor).to.equal('rgb(1, 2, 3)');
357
+ expect(getComputedStyle(header).backgroundColor).to.equal('rgba(1, 2, 3, 0.95)');
358
+ });
359
+
360
+ it('uses the page surface background for active navigation items', async () => {
361
+ const el = await fixture<HTMLElement>(html`
362
+ <div style="--zn-body: 12, 34, 56; --zn-panel: 1, 2, 3;">
363
+ <zn-panel flush>
364
+ <zn-page caption="Page Title">
365
+ <zn-tab caption="Overview">Overview Content</zn-tab>
366
+ <zn-tab caption="Details">Details Content</zn-tab>
367
+ </zn-page>
368
+ </zn-panel>
369
+ </div>
370
+ `);
371
+ await aTimeout(40);
372
+
373
+ const page = el.querySelector<ZnPage>('zn-page')!;
374
+ const navbar = page.shadowRoot!.querySelector('zn-navbar')!;
375
+ const activeNavItem = navbar.shadowRoot!.querySelector<HTMLElement>('li.zn-tb-active')!;
376
+
377
+ await aTimeout(120);
378
+
379
+ expect(getComputedStyle(activeNavItem).backgroundColor).to.equal('rgb(1, 2, 3)');
380
+ expect(getComputedStyle(activeNavItem).transitionDuration).to.equal('0.1s');
381
+ });
382
+
383
+ it('disables breadcrumbs in modal mode', async () => {
384
+ const el = await fixture<ZnPage>(html`
385
+ <zn-page caption="Page Title" modal>
386
+ <a slot="breadcrumb" href="/parent">Parent</a>
387
+ <zn-tab caption="Overview">Overview Content</zn-tab>
388
+ </zn-page>
389
+ `);
390
+ await aTimeout(20);
391
+
392
+ const header = el.shadowRoot!.querySelector<HTMLElement>('.header')!;
393
+
394
+ expect(header.classList.contains('header--has-breadcrumb')).to.equal(false);
395
+ expect(el.shadowRoot!.querySelector('slot[name="breadcrumb"]')).to.equal(null);
396
+ });
397
+ });
@@ -7,8 +7,10 @@
7
7
  height: 100%;
8
8
 
9
9
  --page-nav-crumb-height: 50px;
10
- --page-nav-width: 240px;
10
+ --page-nav-width: 200px;
11
11
  --page-nav-animation-duration: 0.3s;
12
+
13
+ --page-nav-bg: var(--zn-page-content-background, var(--zn-body));
12
14
  }
13
15
 
14
16
  .breadcrumb {
@@ -41,10 +43,10 @@
41
43
  position: relative;
42
44
  overflow: hidden;
43
45
  height: 100%;
44
- border-right: 1px solid rgb(var(--zn-border-color));
46
+ //border-right: 1px solid rgb(var(--zn-border-color));
45
47
  max-width: var(--page-nav-width);
46
48
  width: var(--page-nav-width);
47
- background-color: rgb(var(--zn-body));
49
+ background-color: rgb(var(--page-nav-bg));
48
50
  overflow-y: auto;
49
51
  padding: var(--zn-spacing-medium);
50
52
  @include wc.scrollbars;
@@ -89,7 +91,7 @@
89
91
  border-bottom: 1px solid rgb(var(--zn-border-color));
90
92
  width: 100%;
91
93
  height: var(--page-nav-crumb-height);
92
- background-color: rgb(var(--zn-body));
94
+ background-color: rgb(var(--page-nav-bg));
93
95
  gap: 10px;
94
96
  }
95
97
 
@@ -103,7 +105,7 @@
103
105
 
104
106
 
105
107
  height: 100%;
106
- background: linear-gradient(180deg, rgb(var(--zn-body)) 70%, rgba(0, 0, 0, 0) 100%);
108
+ background: linear-gradient(180deg, rgb(var(--page-nav-bg)) 70%, rgba(0, 0, 0, 0) 100%);
107
109
  width: calc(var(--page-nav-width) - 20px);
108
110
 
109
111
  z-index: calc(var(--zn-z-index-page-nav) + 1);
@@ -27,11 +27,11 @@ export default class ZnPane extends ZincElement {
27
27
 
28
28
  @property({type: Boolean}) flush: boolean;
29
29
 
30
- protected _header: HTMLElement;
30
+ protected _header: HTMLElement | null = null;
31
31
 
32
32
  connectedCallback() {
33
33
  super.connectedCallback();
34
- this._header = this.querySelector("zn-header") as HTMLElement;
34
+ this._header = Array.from(this.children).find((child): child is HTMLElement => child.tagName === 'ZN-HEADER') ?? null;
35
35
  }
36
36
 
37
37
  render() {
@@ -25,7 +25,6 @@ zn-header {
25
25
  overflow: auto;
26
26
  display: flex;
27
27
  flex: 1 1;
28
- padding: var(--zn-spacing-medium);
29
28
  }
30
29
 
31
30
  .pane__content--flush {
@@ -7,4 +7,35 @@ describe('<zn-pane>', () => {
7
7
 
8
8
  expect(el).to.exist;
9
9
  });
10
+
11
+ it('only pulls direct child headers into the pane header area', async () => {
12
+ const el = await fixture(html`
13
+ <zn-pane>
14
+ <zn-panel>
15
+ <zn-tab caption="Overview">
16
+ <zn-header caption="Nested Header"></zn-header>
17
+ </zn-tab>
18
+ </zn-panel>
19
+ </zn-pane>
20
+ `);
21
+
22
+ const nestedHeader = el.querySelector('zn-header')!;
23
+
24
+ expect(el.classList.contains('with-header')).to.equal(false);
25
+ expect(nestedHeader.getAttribute('slot')).to.not.equal('top');
26
+ expect(nestedHeader.parentElement?.tagName).to.equal('ZN-TAB');
27
+ });
28
+
29
+ it('pulls direct child headers into the pane header area', async () => {
30
+ const el = await fixture(html`
31
+ <zn-pane>
32
+ <zn-header caption="Direct Header"></zn-header>
33
+ </zn-pane>
34
+ `);
35
+
36
+ const directHeader = (el.querySelector('zn-header') ?? el.shadowRoot!.querySelector('zn-header'))!;
37
+
38
+ expect(el.classList.contains('with-header')).to.equal(true);
39
+ expect(directHeader.getAttribute('slot')).to.equal('top');
40
+ });
10
41
  });
@@ -10,6 +10,7 @@
10
10
  position: relative;
11
11
  min-width: var(--zn-spacing-small-panel);
12
12
  flex-basis: var(--zn-panel-basis);
13
+ --zn-page-surface-background: var(--zn-panel);
13
14
  }
14
15
 
15
16
 
@@ -42,7 +43,7 @@
42
43
 
43
44
 
44
45
  &__header {
45
- padding: var(--zn-spacing-x-small) var(--zn-spacing-medium);
46
+ padding: var(--zn-base-gap);
46
47
 
47
48
  // background-color: rgba(var(--zn-panel-highlight), 0.6);
48
49
  // border-bottom: 1px solid rgb(var(--zn-border-color));
@@ -73,7 +74,7 @@
73
74
  flex-direction: column;
74
75
  overflow-y: auto;
75
76
  flex-grow: 1;
76
- padding: 0 var(--zn-spacing-medium) var(--zn-spacing-medium);
77
+ padding: 0 var(--zn-base-gap) var(--zn-base-gap) var(--zn-base-gap);
77
78
 
78
79
  > :not([hidden]) ~ :not([hidden]) {
79
80
  border: 1px solid rgb(var(--zn-border-color));
@@ -83,7 +84,7 @@
83
84
  &__footer {
84
85
  font-size: var(--zn-font-size-small);
85
86
  display: block;
86
- padding: var(--zn-spacing-small) var(--zn-spacing-medium);
87
+ padding: var(--zn-spacing-small) var(--zn-base-gap);
87
88
  background: rgb(var(--zn-panel-secondary));
88
89
  border-bottom-left-radius: var(--zn-border-radius-large);
89
90
  border-bottom-right-radius: var(--zn-border-radius-large);
@@ -108,21 +109,9 @@
108
109
  }
109
110
  }
110
111
 
111
- &:before {
112
- position: absolute;
113
- content: '';
114
- bottom: -3px;
115
- left: 0;
116
- right: 0;
117
- height: 20px;
118
- background-color: rgb(var(--zn-shadow));
119
- border-radius: var(--zn-border-radius-large);
120
- z-index: -1;
121
- }
122
-
123
112
 
124
113
  &:not(.panel--has-header):not(.panel--flush, .panel--flush-y) &__body {
125
- padding-top: var(--zn-spacing-medium);
114
+ padding-top: var(--zn-base-gap);
126
115
  }
127
116
 
128
117
  &--flush {
@@ -4,6 +4,8 @@ import ZincElement from '../../internal/zinc-element';
4
4
 
5
5
  import styles from './progress-bar.scss';
6
6
 
7
+ export type ProgressBarColor = 'current' | 'info' | 'error' | 'success' | 'warning';
8
+
7
9
  /**
8
10
  * @summary Progress bars provide visual feedback about the completion status of a task or process.
9
11
  * @documentation https://zinc.style/components/progress-bar
@@ -20,7 +22,7 @@ import styles from './progress-bar.scss';
20
22
  * @csspart info - The description text element.
21
23
  *
22
24
  * @cssproperty --zn-border-color - The color of the progress bar background track.
23
- * @cssproperty --zn-primary - The color of the progress bar fill.
25
+ * @cssproperty --zn-progress-bar-color - The color of the progress bar fill.
24
26
  * @cssproperty --zn-text-heading - The color of the caption text.
25
27
  * @cssproperty --zn-text - The color of the progress percentage and description text.
26
28
  * @cssproperty --zn-spacing-x-small - The spacing between header/footer and the progress bar.
@@ -29,6 +31,7 @@ export default class ZnProgressBar extends ZincElement {
29
31
  static styles: CSSResultGroup = unsafeCSS(styles);
30
32
 
31
33
  @property({type: String, reflect: true}) caption: string | undefined;
34
+ @property({reflect: true}) color: ProgressBarColor = 'current';
32
35
  @property({type: String, reflect: true}) description: string | undefined;
33
36
  @property({type: Number, reflect: true}) value: number | undefined;
34
37
  @property({type: Boolean, reflect: true, attribute: 'show-progress'})
@@ -62,7 +65,7 @@ export default class ZnProgressBar extends ZincElement {
62
65
  />
63
66
  <rect
64
67
  part="fill"
65
- fill='rgb(var(--zn-primary))'
68
+ fill="var(--zn-progress-bar-color)"
66
69
  width="${this.value}%"
67
70
  height="100%"
68
71
  clip-path="url(#clip-path)"
@@ -6,6 +6,23 @@
6
6
  display: flex;
7
7
  flex-direction: column;
8
8
  padding: 10px 0;
9
+ --zn-progress-bar-color: rgb(var(--zn-primary));
10
+ }
11
+
12
+ :host([color="info"]) {
13
+ --zn-progress-bar-color: rgb(var(--zn-color-info));
14
+ }
15
+
16
+ :host([color="error"]) {
17
+ --zn-progress-bar-color: rgb(var(--zn-color-error));
18
+ }
19
+
20
+ :host([color="success"]) {
21
+ --zn-progress-bar-color: rgb(var(--zn-color-success));
22
+ }
23
+
24
+ :host([color="warning"]) {
25
+ --zn-progress-bar-color: rgb(var(--zn-color-warning));
9
26
  }
10
27
 
11
28
  .header {
@@ -43,4 +60,3 @@
43
60
  .footer {
44
61
  margin-top: var(--zn-spacing-x-small);
45
62
  }
46
-
@@ -7,4 +7,16 @@ describe('<zn-progress-bar>', () => {
7
7
 
8
8
  expect(el).to.exist;
9
9
  });
10
+
11
+ it('should support color variants', async () => {
12
+ const variants = ['current', 'info', 'error', 'success', 'warning'];
13
+
14
+ for (const color of variants) {
15
+ const el = await fixture(html` <zn-progress-bar color=${color} value="50"></zn-progress-bar> `);
16
+ const fill = el.shadowRoot?.querySelector('[part="fill"]');
17
+
18
+ expect(el.getAttribute('color')).to.equal(color);
19
+ expect(fill?.getAttribute('fill')).to.equal('var(--zn-progress-bar-color)');
20
+ }
21
+ });
10
22
  });