@kubex/zinc 1.1.159 → 1.1.160
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 +1742 -1717
- package/dist/vscode.html-custom-data.json +85 -85
- package/dist/web-types.json +176 -176
- package/dist/zn.d.ts +112 -109
- package/dist/zn.min.js +138 -138
- package/docs/pages/components/page.md +15 -0
- package/package.json +1 -1
- package/src/components/page/page.component.ts +24 -3
- package/src/components/page/page.test.ts +52 -0
- package/src/components/tabs/tabs.component.ts +1 -1
|
@@ -133,6 +133,21 @@ Use `priority` to control tab order. Lower numbers appear first after the empty-
|
|
|
133
133
|
</zn-page>
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
### Linking to a Tab
|
|
137
|
+
|
|
138
|
+
Give any element in the page content a `tab` attribute holding a tab's `id` to open that tab when it is clicked. Links inside a nested `zn-page` or `zn-tabs` open tabs in that nested container.
|
|
139
|
+
|
|
140
|
+
```html:preview
|
|
141
|
+
<zn-page caption="Scorecard">
|
|
142
|
+
<zn-tab caption="Details" id="details">
|
|
143
|
+
<zn-button tab="section-general" icon="edit">Edit General section</zn-button>
|
|
144
|
+
</zn-tab>
|
|
145
|
+
<zn-tab caption="General" id="section-general">
|
|
146
|
+
General section settings
|
|
147
|
+
</zn-tab>
|
|
148
|
+
</zn-page>
|
|
149
|
+
```
|
|
150
|
+
|
|
136
151
|
### Dynamic Tabs
|
|
137
152
|
|
|
138
153
|
Set `uri` on `zn-tab` to create a dynamic tab. The generated navigation item receives `tab-uri`.
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import {classMap} from 'lit/directives/class-map.js';
|
|
|
2
2
|
import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
|
|
3
3
|
import {HasSlotController} from '../../internal/slot';
|
|
4
4
|
import {property, state} from 'lit/decorators.js';
|
|
5
|
+
import {tabContainerSelector} from '../tabs/tabs.component';
|
|
5
6
|
import ZnButton from '../button';
|
|
6
7
|
import ZnCopyButton from '../copy-button';
|
|
7
8
|
import ZnExpandingAction from '../expanding-action';
|
|
@@ -30,6 +31,7 @@ interface TabDefinition {
|
|
|
30
31
|
* @since 1.0
|
|
31
32
|
*
|
|
32
33
|
* @slot - Page content. Use zn-tab for named tabs and header-action/header-actions for header actions.
|
|
34
|
+
* Clicking an element in the content with `tab="<tab id>"` opens that tab.
|
|
33
35
|
* @slot description - Rich subtitle/description content. Falls back to the `summary` attribute when empty.
|
|
34
36
|
* @slot bottom - Content rendered below the navbar row (e.g. chips, filters). Forwarded to the navbar's bottom slot.
|
|
35
37
|
*/
|
|
@@ -68,6 +70,7 @@ export default class ZnPage extends ZnTabs {
|
|
|
68
70
|
const connected = super.connectedCallback();
|
|
69
71
|
window.addEventListener('alt-press', this.handleAltPress);
|
|
70
72
|
window.addEventListener('alt-up', this.handleAltUp);
|
|
73
|
+
this.addEventListener('click', this.handleContentTabClick);
|
|
71
74
|
this.prepareTabs();
|
|
72
75
|
this.refreshExpandingActionsState();
|
|
73
76
|
this.tabObserver = new MutationObserver((mutations) => {
|
|
@@ -90,6 +93,7 @@ export default class ZnPage extends ZnTabs {
|
|
|
90
93
|
super.disconnectedCallback();
|
|
91
94
|
window.removeEventListener('alt-press', this.handleAltPress);
|
|
92
95
|
window.removeEventListener('alt-up', this.handleAltUp);
|
|
96
|
+
this.removeEventListener('click', this.handleContentTabClick);
|
|
93
97
|
this.tabObserver?.disconnect();
|
|
94
98
|
this.tabObserver = null;
|
|
95
99
|
this.actionObserver?.disconnect();
|
|
@@ -280,6 +284,23 @@ export default class ZnPage extends ZnTabs {
|
|
|
280
284
|
}
|
|
281
285
|
}
|
|
282
286
|
|
|
287
|
+
// Only elements whose nearest tab container is this page count, so links inside a nested
|
|
288
|
+
// zn-tabs or zn-page drive that container instead.
|
|
289
|
+
private handleContentTabClick = (event: MouseEvent) => {
|
|
290
|
+
const link = event.composedPath().find((node): node is HTMLElement =>
|
|
291
|
+
node instanceof HTMLElement && node.hasAttribute('tab') && node.closest(tabContainerSelector) === this
|
|
292
|
+
);
|
|
293
|
+
if (!link || link.hasAttribute('disabled')) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const definition = this.tabDefinitions.find(tab => tab.id === link.getAttribute('tab'));
|
|
298
|
+
if (definition) {
|
|
299
|
+
event.preventDefault();
|
|
300
|
+
this.activateTabDefinition(definition, true, true);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
283
304
|
private handleNavigationSelect(event: ZnSelectEvent) {
|
|
284
305
|
const item = event.detail.item as HTMLElement;
|
|
285
306
|
if (this.getNavigationItemPage(item) !== this) {
|
|
@@ -383,16 +404,16 @@ export default class ZnPage extends ZnTabs {
|
|
|
383
404
|
return this.pageHistoryKey;
|
|
384
405
|
}
|
|
385
406
|
|
|
386
|
-
private activateTabDefinition(tab: TabDefinition, store = false) {
|
|
407
|
+
private activateTabDefinition(tab: TabDefinition, store = false, pushHistory = false) {
|
|
387
408
|
if (tab.uri) {
|
|
388
409
|
const navItem = this.findNavItemForUri(tab.uri);
|
|
389
410
|
if (navItem) {
|
|
390
|
-
this.clickTab(navItem, false);
|
|
411
|
+
this.clickTab(navItem, false, pushHistory);
|
|
391
412
|
this.syncNavigationActive(navItem);
|
|
392
413
|
return;
|
|
393
414
|
}
|
|
394
415
|
}
|
|
395
|
-
this.activateTab(tab.id, store);
|
|
416
|
+
this.activateTab(tab.id, store, pushHistory);
|
|
396
417
|
}
|
|
397
418
|
|
|
398
419
|
private findNavItemForUri(uri: string): HTMLElement | null {
|
|
@@ -350,6 +350,58 @@ describe('<zn-page>', () => {
|
|
|
350
350
|
expect(innerDynamicPanel.hasAttribute('selected')).to.equal(true);
|
|
351
351
|
});
|
|
352
352
|
|
|
353
|
+
it('opens a tab when an element in the content with a tab attribute is clicked', async () => {
|
|
354
|
+
const el = await fixture<ZnPage>(html`
|
|
355
|
+
<zn-page caption="Content Tab Link">
|
|
356
|
+
<zn-tab caption="Overview" id="overview">
|
|
357
|
+
<zn-button tab="details">Open details</zn-button>
|
|
358
|
+
<zn-button tab="details" disabled>Disabled link</zn-button>
|
|
359
|
+
</zn-tab>
|
|
360
|
+
<zn-tab caption="Details" id="details">Details Content</zn-tab>
|
|
361
|
+
</zn-page>
|
|
362
|
+
`);
|
|
363
|
+
await aTimeout(40);
|
|
364
|
+
|
|
365
|
+
// zn-button.click() skips event dispatch, so click the rendered button as a user would.
|
|
366
|
+
const [link, disabledLink] = Array.from(el.querySelectorAll('zn-button'))
|
|
367
|
+
.map(button => button.shadowRoot!.querySelector<HTMLElement>('button')!);
|
|
368
|
+
|
|
369
|
+
disabledLink.click();
|
|
370
|
+
await aTimeout(40);
|
|
371
|
+
expect(el.getAttribute('active')).to.equal('overview');
|
|
372
|
+
|
|
373
|
+
link.click();
|
|
374
|
+
await aTimeout(40);
|
|
375
|
+
expect(el.getAttribute('active')).to.equal('details');
|
|
376
|
+
expect(el.shadowRoot!.querySelector('#details')!.hasAttribute('selected')).to.equal(true);
|
|
377
|
+
const activeItem = el.shadowRoot!.querySelector<HTMLElement>('zn-navbar li.active');
|
|
378
|
+
expect(activeItem?.getAttribute('tab')).to.equal('details');
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
it('leaves content tab links inside a nested page to that page', async () => {
|
|
382
|
+
const outerPage = await fixture<ZnPage>(html`
|
|
383
|
+
<zn-page caption="Nested Content Tab Link">
|
|
384
|
+
<zn-tab caption="Outer One" id="outer-one">
|
|
385
|
+
<zn-page caption="Nested Content Tab Link Inner" nested>
|
|
386
|
+
<zn-tab caption="Inner One" id="inner-one">
|
|
387
|
+
<zn-button tab="inner-two">Inner link</zn-button>
|
|
388
|
+
</zn-tab>
|
|
389
|
+
<zn-tab caption="Inner Two" id="inner-two">Inner Two Content</zn-tab>
|
|
390
|
+
</zn-page>
|
|
391
|
+
</zn-tab>
|
|
392
|
+
<zn-tab caption="Outer Two" id="inner-two">Outer Two Content</zn-tab>
|
|
393
|
+
</zn-page>
|
|
394
|
+
`);
|
|
395
|
+
await aTimeout(80);
|
|
396
|
+
|
|
397
|
+
const innerPage = outerPage.querySelector<ZnPage>('zn-page')!;
|
|
398
|
+
innerPage.querySelector('zn-button')!.shadowRoot!.querySelector<HTMLElement>('button')!.click();
|
|
399
|
+
await aTimeout(40);
|
|
400
|
+
|
|
401
|
+
expect(innerPage.getAttribute('active')).to.equal('inner-two');
|
|
402
|
+
expect(outerPage.getAttribute('active')).to.equal('outer-one');
|
|
403
|
+
});
|
|
404
|
+
|
|
353
405
|
it('keeps nested page tab selections scoped to the nested page', async () => {
|
|
354
406
|
const outerPage = await fixture<ZnPage>(html`
|
|
355
407
|
<zn-page caption="Outer Page">
|
|
@@ -25,7 +25,7 @@ import styles from './tabs.scss';
|
|
|
25
25
|
// Every element that manages its own tabs (ZnTabs and its subclasses). Tab
|
|
26
26
|
// registration must stop at these so an outer container never claims a nested
|
|
27
27
|
// container's tabs.
|
|
28
|
-
const tabContainerSelector = 'zn-tabs, zn-page, zn-page-nav';
|
|
28
|
+
export const tabContainerSelector = 'zn-tabs, zn-page, zn-page-nav';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* @summary Short summary of the component's intended use.
|