@oslokommune/punkt-elements 16.7.7 → 16.8.2
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/CHANGELOG.md +17 -0
- package/dist/index.d.ts +8 -2
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +1 -1
- package/dist/pkt-tabs.cjs +1 -1
- package/dist/pkt-tabs.js +1 -1
- package/dist/{tabs-Y7utmJi9.js → tabs-C0DWPmn2.js} +64 -28
- package/dist/{tabs-D8nL9rWx.cjs → tabs-COwm9b3n.cjs} +19 -16
- package/package.json +2 -2
- package/src/components/tabs/tabitem.ts +35 -9
- package/src/components/tabs/tabs-context.ts +1 -1
- package/src/components/tabs/tabs.test.ts +87 -10
- package/src/components/tabs/tabs.ts +36 -13
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,23 @@ og skriver commits ca etter [Conventional Commits](https://conventionalcommits.o
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [16.8.2](https://github.com/oslokommune/punkt/compare/16.8.1...16.8.2) (2026-04-21)
|
|
9
|
+
|
|
10
|
+
### ⚠ BREAKING CHANGES
|
|
11
|
+
Ingen
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
Ingen
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
Ingen
|
|
18
|
+
|
|
19
|
+
### Chores
|
|
20
|
+
Ingen
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
|
|
8
25
|
## [16.7.7](https://github.com/oslokommune/punkt/compare/16.7.6...16.7.7) (2026-04-16)
|
|
9
26
|
|
|
10
27
|
### ⚠ BREAKING CHANGES
|
package/dist/index.d.ts
CHANGED
|
@@ -471,6 +471,7 @@ declare interface IPktSelect {
|
|
|
471
471
|
|
|
472
472
|
export declare interface IPktTabItem {
|
|
473
473
|
active?: boolean;
|
|
474
|
+
disabled?: boolean;
|
|
474
475
|
href?: string;
|
|
475
476
|
icon?: string;
|
|
476
477
|
controls?: string;
|
|
@@ -1685,6 +1686,7 @@ declare class PktShadowElement<T = {}> extends LitElement {
|
|
|
1685
1686
|
|
|
1686
1687
|
export declare class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktTabItem {
|
|
1687
1688
|
active: boolean;
|
|
1689
|
+
disabled: boolean;
|
|
1688
1690
|
href: string;
|
|
1689
1691
|
icon: string;
|
|
1690
1692
|
controls: string;
|
|
@@ -1695,8 +1697,9 @@ export declare class PktTabItem extends PktElementWithSlot<IPktTabItem> implemen
|
|
|
1695
1697
|
elementRef: Ref<HTMLAnchorElement | HTMLButtonElement>;
|
|
1696
1698
|
constructor();
|
|
1697
1699
|
connectedCallback(): void;
|
|
1700
|
+
updated(): void;
|
|
1698
1701
|
private handleClick;
|
|
1699
|
-
private
|
|
1702
|
+
private handleKeyDown;
|
|
1700
1703
|
render(): TemplateResult<1>;
|
|
1701
1704
|
}
|
|
1702
1705
|
|
|
@@ -1704,11 +1707,14 @@ export declare class PktTabs extends PktElementWithSlot<IPktTabs> implements IPk
|
|
|
1704
1707
|
arrowNav: boolean;
|
|
1705
1708
|
disableArrowNav: boolean;
|
|
1706
1709
|
private tabRefs;
|
|
1710
|
+
private disabledMap;
|
|
1707
1711
|
private tabCount;
|
|
1708
1712
|
private get useArrowNav();
|
|
1709
1713
|
private context;
|
|
1710
1714
|
updated(changedProperties: PropertyValues): void;
|
|
1711
1715
|
private registerTab;
|
|
1716
|
+
private isTabDisabled;
|
|
1717
|
+
private findEnabledIndex;
|
|
1712
1718
|
private handleClick;
|
|
1713
1719
|
private handleKeyUp;
|
|
1714
1720
|
render(): TemplateResult<1>;
|
|
@@ -1864,7 +1870,7 @@ declare interface TabsContext {
|
|
|
1864
1870
|
/**
|
|
1865
1871
|
* Register a tab item with the parent tabs container
|
|
1866
1872
|
*/
|
|
1867
|
-
registerTab: (element: HTMLElement, index: number) => void;
|
|
1873
|
+
registerTab: (element: HTMLElement, index: number, disabled?: boolean) => void;
|
|
1868
1874
|
/**
|
|
1869
1875
|
* Handle click event from a tab item
|
|
1870
1876
|
*/
|
package/dist/pkt-index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./alert-BEEnTiCb.cjs`),t=require(`./icon-Dr8sfT2X.cjs`),n=require(`./button-D-TOBaJB.cjs`),r=require(`./accordion-BB1JrONI.cjs`),i=require(`./backlink-Ba03LlbB.cjs`),a=require(`./breadcrumbs-CD1zib3s.cjs`),o=require(`./calendar-E5jJLt8D.cjs`),s=require(`./card-CUFhMVim.cjs`),c=require(`./tag-DLthw5b4.cjs`),l=require(`./heading-BgSWIRii.cjs`),u=require(`./combobox-CGbFTWq_.cjs`),d=require(`./header-DWgPgGfS.cjs`),f=require(`./datepicker-DpmlsnH_.cjs`),p=require(`./helptext-D-3QfZv5.cjs`),m=require(`./input-wrapper-BRuVELpK.cjs`),h=require(`./consent-hwu7pp3S.cjs`),g=require(`./checkbox-BARDb9iV.cjs`),_=require(`./link-CyTCpI0k.cjs`),v=require(`./textinput-DFh8_9cx.cjs`),y=require(`./linkcard-BE4-FewK.cjs`),b=require(`./loader-CX2rsC01.cjs`),x=require(`./messagebox-Din9X53b.cjs`),S=require(`./modal-DS_wUOIv.cjs`),C=require(`./progressbar-CdXtmOBb.cjs`),w=require(`./radiobutton-GOnpUbCW.cjs`),T=require(`./tabs-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./alert-BEEnTiCb.cjs`),t=require(`./icon-Dr8sfT2X.cjs`),n=require(`./button-D-TOBaJB.cjs`),r=require(`./accordion-BB1JrONI.cjs`),i=require(`./backlink-Ba03LlbB.cjs`),a=require(`./breadcrumbs-CD1zib3s.cjs`),o=require(`./calendar-E5jJLt8D.cjs`),s=require(`./card-CUFhMVim.cjs`),c=require(`./tag-DLthw5b4.cjs`),l=require(`./heading-BgSWIRii.cjs`),u=require(`./combobox-CGbFTWq_.cjs`),d=require(`./header-DWgPgGfS.cjs`),f=require(`./datepicker-DpmlsnH_.cjs`),p=require(`./helptext-D-3QfZv5.cjs`),m=require(`./input-wrapper-BRuVELpK.cjs`),h=require(`./consent-hwu7pp3S.cjs`),g=require(`./checkbox-BARDb9iV.cjs`),_=require(`./link-CyTCpI0k.cjs`),v=require(`./textinput-DFh8_9cx.cjs`),y=require(`./linkcard-BE4-FewK.cjs`),b=require(`./loader-CX2rsC01.cjs`),x=require(`./messagebox-Din9X53b.cjs`),S=require(`./modal-DS_wUOIv.cjs`),C=require(`./progressbar-CdXtmOBb.cjs`),w=require(`./radiobutton-GOnpUbCW.cjs`),T=require(`./tabs-COwm9b3n.cjs`),E=require(`./textarea-D7aGOyDf.cjs`),D=require(`./select-C2i8sOk5.cjs`),O=require(`./timepicker-BIfE7KKJ.cjs`);exports.PktAccordion=r.r,exports.PktAccordionItem=r.n,exports.PktAlert=e.n,exports.PktBackLink=i.n,exports.PktBreadcrumbs=a.t,exports.PktButton=n.n,exports.PktCalendar=o.n,exports.PktCard=s.n,exports.PktCheckbox=g.n,exports.PktCombobox=u.n,exports.PktConsent=h.n,exports.PktDateTags=f.a,exports.PktDatepicker=f.t,exports.PktHeader=d.t,exports.PktHeaderService=d.n,exports.PktHeaderUserMenu=d.r,exports.PktHeading=l.n,exports.PktHelptext=p.n,exports.PktIcon=t.n,exports.PktInputWrapper=m.n,exports.PktLink=_.n,exports.PktLinkCard=y.n,exports.PktLoader=b.n,exports.PktMessagebox=x.n,exports.PktModal=S.n,exports.PktProgressbar=C.n,exports.PktRadioButton=w.n,exports.PktRadiobutton=w.n,exports.PktSelect=D.n,exports.PktTabItem=T.n,exports.PktTabs=T.r,exports.PktTag=c.n,exports.PktTextarea=E.n,exports.PktTextinput=v.n,exports.PktTimepicker=O.t;
|
package/dist/pkt-index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { n as T } from "./messagebox-Cwlg5sOr.js";
|
|
|
23
23
|
import { n as E } from "./modal-C_wtsh9m.js";
|
|
24
24
|
import { n as D } from "./progressbar-Dy9AiYQz.js";
|
|
25
25
|
import { n as O } from "./radiobutton-V_KPbup_.js";
|
|
26
|
-
import { n as k, r as A } from "./tabs-
|
|
26
|
+
import { n as k, r as A } from "./tabs-C0DWPmn2.js";
|
|
27
27
|
import { n as j } from "./textarea-DYOBhJrN.js";
|
|
28
28
|
import { n as M } from "./select-BSAFd8Wa.js";
|
|
29
29
|
import { t as N } from "./timepicker-DaSwn1FA.js";
|
package/dist/pkt-tabs.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require(`./tabs-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require(`./tabs-COwm9b3n.cjs`);exports.PktTabItem=e.n,exports.PktTabs=e.r,exports.default=e.t;
|
package/dist/pkt-tabs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as e, r as t, t as n } from "./tabs-
|
|
1
|
+
import { n as e, r as t, t as n } from "./tabs-C0DWPmn2.js";
|
|
2
2
|
export { e as PktTabItem, t as PktTabs, n as default };
|
|
@@ -171,7 +171,7 @@ function _({ context: e, subscribe: t }) {
|
|
|
171
171
|
//#region src/components/tabs/tabs-context.ts
|
|
172
172
|
var v = d(Symbol("pkt-tabs-context")), y = class extends o {
|
|
173
173
|
constructor(...e) {
|
|
174
|
-
super(...e), this.arrowNav = !0, this.disableArrowNav = !1, this.tabRefs = [], this.tabCount = 0, this.context = {
|
|
174
|
+
super(...e), this.arrowNav = !0, this.disableArrowNav = !1, this.tabRefs = [], this.disabledMap = {}, this.tabCount = 0, this.context = {
|
|
175
175
|
useArrowNav: this.useArrowNav,
|
|
176
176
|
registerTab: this.registerTab.bind(this),
|
|
177
177
|
handleClick: this.handleClick.bind(this),
|
|
@@ -187,22 +187,41 @@ var v = d(Symbol("pkt-tabs-context")), y = class extends o {
|
|
|
187
187
|
useArrowNav: this.useArrowNav
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
|
-
registerTab(e, t) {
|
|
191
|
-
this.tabRefs[t] = e, this.tabCount = Math.max(this.tabCount, t + 1);
|
|
190
|
+
registerTab(e, t, n = !1) {
|
|
191
|
+
this.tabRefs[t] = e, this.disabledMap[t] = n, this.tabCount = Math.max(this.tabCount, t + 1);
|
|
192
|
+
}
|
|
193
|
+
isTabDisabled(e) {
|
|
194
|
+
return !!this.disabledMap[e];
|
|
195
|
+
}
|
|
196
|
+
findEnabledIndex(e, t) {
|
|
197
|
+
let n = e + t;
|
|
198
|
+
for (; n >= 0 && n < this.tabCount;) {
|
|
199
|
+
if (!this.isTabDisabled(n)) return n;
|
|
200
|
+
n += t;
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
192
203
|
}
|
|
193
204
|
handleClick(e) {
|
|
194
|
-
this.dispatchEvent(new CustomEvent("tab-selected", {
|
|
205
|
+
this.isTabDisabled(e) || this.dispatchEvent(new CustomEvent("tab-selected", {
|
|
195
206
|
detail: { index: e },
|
|
196
207
|
bubbles: !0,
|
|
197
208
|
composed: !0
|
|
198
209
|
}));
|
|
199
210
|
}
|
|
200
211
|
handleKeyUp(e, t) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
if (this.useArrowNav) {
|
|
213
|
+
if (e.key === "ArrowLeft") {
|
|
214
|
+
e.preventDefault();
|
|
215
|
+
let n = this.findEnabledIndex(t, -1);
|
|
216
|
+
n !== null && this.tabRefs[n]?.focus();
|
|
217
|
+
}
|
|
218
|
+
if (e.key === "ArrowRight") {
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
let n = this.findEnabledIndex(t, 1);
|
|
221
|
+
n !== null && this.tabRefs[n]?.focus();
|
|
222
|
+
}
|
|
223
|
+
(e.key === "Enter" || e.key === " " || e.key === "Spacebar" || e.key === "ArrowDown") && (e.preventDefault(), this.handleClick(t));
|
|
224
|
+
}
|
|
206
225
|
}
|
|
207
226
|
render() {
|
|
208
227
|
return t`
|
|
@@ -220,7 +239,7 @@ r([i({
|
|
|
220
239
|
type: Boolean,
|
|
221
240
|
reflect: !0,
|
|
222
241
|
attribute: "disable-arrow-nav"
|
|
223
|
-
})], y.prototype, "disableArrowNav", void 0), r([n()], y.prototype, "tabRefs", void 0), r([n()], y.prototype, "tabCount", void 0), r([g({ context: v }), n()], y.prototype, "context", void 0);
|
|
242
|
+
})], y.prototype, "disableArrowNav", void 0), r([n()], y.prototype, "tabRefs", void 0), r([n()], y.prototype, "disabledMap", void 0), r([n()], y.prototype, "tabCount", void 0), r([g({ context: v }), n()], y.prototype, "context", void 0);
|
|
224
243
|
try {
|
|
225
244
|
e("pkt-tabs")(y);
|
|
226
245
|
} catch {
|
|
@@ -230,21 +249,32 @@ try {
|
|
|
230
249
|
//#region src/components/tabs/tabitem.ts
|
|
231
250
|
var b = class extends o {
|
|
232
251
|
constructor() {
|
|
233
|
-
super(), this.active = !1, this.href = "", this.icon = "", this.controls = "", this.tag = "", this.tagSkin = "blue", this.index = 0, this.elementRef = l();
|
|
252
|
+
super(), this.active = !1, this.disabled = !1, this.href = "", this.icon = "", this.controls = "", this.tag = "", this.tagSkin = "blue", this.index = 0, this.elementRef = l();
|
|
234
253
|
}
|
|
235
254
|
connectedCallback() {
|
|
236
255
|
super.connectedCallback(), this.updateComplete.then(() => {
|
|
237
|
-
this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index);
|
|
256
|
+
this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index, this.disabled);
|
|
238
257
|
});
|
|
239
258
|
}
|
|
240
|
-
|
|
259
|
+
updated() {
|
|
260
|
+
this.elementRef.value && this.context && this.context.registerTab(this.elementRef.value, this.index, this.disabled);
|
|
261
|
+
}
|
|
262
|
+
handleClick(e) {
|
|
263
|
+
if (this.disabled) {
|
|
264
|
+
e.preventDefault(), e.stopPropagation();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
241
267
|
this.context && this.context.handleClick(this.index);
|
|
242
268
|
}
|
|
243
|
-
|
|
269
|
+
handleKeyDown(e) {
|
|
270
|
+
if (this.disabled && (e.key === "Enter" || e.key === " " || e.key === "Spacebar")) {
|
|
271
|
+
e.preventDefault(), e.stopPropagation();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
244
274
|
this.context && this.context.handleKeyUp(e, this.index);
|
|
245
275
|
}
|
|
246
276
|
render() {
|
|
247
|
-
let e = this.context?.useArrowNav ?? !0, n = this.active ? "active" : "",
|
|
277
|
+
let e = this.context?.useArrowNav ?? !0, n = this.active && !this.disabled, r = [n ? "active" : "", this.disabled ? "pkt-tabs__item--disabled" : ""].filter(Boolean).join(" "), i = e ? "tab" : void 0, o = e ? n : void 0, l = this.disabled ? -1 : n || !e ? void 0 : -1, u = t`
|
|
248
278
|
${this.icon ? t`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>` : ""}
|
|
249
279
|
<span>${a(this)}</span>
|
|
250
280
|
${this.tag ? t`<pkt-tag skin=${this.tagSkin} size="small">${this.tag}</pkt-tag>` : ""}
|
|
@@ -252,30 +282,33 @@ var b = class extends o {
|
|
|
252
282
|
return this.href ? t`
|
|
253
283
|
<a
|
|
254
284
|
${c(this.elementRef)}
|
|
255
|
-
href=${this.href}
|
|
256
|
-
class="pkt-tabs__link ${
|
|
257
|
-
role=${s(
|
|
258
|
-
aria-selected=${s(
|
|
285
|
+
href=${s(this.disabled ? void 0 : this.href)}
|
|
286
|
+
class="pkt-tabs__link ${r}"
|
|
287
|
+
role=${s(i)}
|
|
288
|
+
aria-selected=${s(o)}
|
|
289
|
+
aria-disabled=${s(this.disabled ? "true" : void 0)}
|
|
259
290
|
aria-controls=${s(this.controls || void 0)}
|
|
260
|
-
tabindex=${s(
|
|
291
|
+
tabindex=${s(l)}
|
|
261
292
|
@click=${this.handleClick}
|
|
262
|
-
@
|
|
293
|
+
@keydown=${this.handleKeyDown}
|
|
263
294
|
>
|
|
264
|
-
${
|
|
295
|
+
${u}
|
|
265
296
|
</a>
|
|
266
297
|
` : t`
|
|
267
298
|
<button
|
|
268
299
|
${c(this.elementRef)}
|
|
269
300
|
type="button"
|
|
270
|
-
class="pkt-tabs__button pkt-link-button ${
|
|
271
|
-
|
|
272
|
-
aria-
|
|
301
|
+
class="pkt-tabs__button pkt-link-button ${r}"
|
|
302
|
+
?disabled=${this.disabled}
|
|
303
|
+
aria-disabled=${s(this.disabled ? "true" : void 0)}
|
|
304
|
+
role=${s(i)}
|
|
305
|
+
aria-selected=${s(o)}
|
|
273
306
|
aria-controls=${s(this.controls || void 0)}
|
|
274
|
-
tabindex=${s(
|
|
307
|
+
tabindex=${s(l)}
|
|
275
308
|
@click=${this.handleClick}
|
|
276
|
-
@
|
|
309
|
+
@keydown=${this.handleKeyDown}
|
|
277
310
|
>
|
|
278
|
-
${
|
|
311
|
+
${u}
|
|
279
312
|
</button>
|
|
280
313
|
`;
|
|
281
314
|
}
|
|
@@ -284,6 +317,9 @@ r([i({
|
|
|
284
317
|
type: Boolean,
|
|
285
318
|
reflect: !0
|
|
286
319
|
})], b.prototype, "active", void 0), r([i({
|
|
320
|
+
type: Boolean,
|
|
321
|
+
reflect: !0
|
|
322
|
+
})], b.prototype, "disabled", void 0), r([i({
|
|
287
323
|
type: String,
|
|
288
324
|
reflect: !0
|
|
289
325
|
})], b.prototype, "href", void 0), r([i({
|
|
@@ -1,37 +1,40 @@
|
|
|
1
|
-
const e=require(`./element-DjtxO-1r.cjs`),t=require(`./element-with-slot-CWoTGA1B.cjs`),n=require(`./if-defined-XKOD_t_V.cjs`),r=require(`./ref-BaJ0mBT_.cjs`);var i=class extends Event{constructor(e,t,n,r){super(`context-request`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t,this.callback=n,this.subscribe=r??!1}};function a(e){return e}var o=class{constructor(e,t,n,r){if(this.subscribe=!1,this.provided=!1,this.value=void 0,this.t=(e,t)=>{this.unsubscribe&&(this.unsubscribe!==t&&(this.provided=!1,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=e,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=!0,this.callback&&this.callback(e,t)),this.unsubscribe=t},this.host=e,t.context!==void 0){let e=t;this.context=e.context,this.callback=e.callback,this.subscribe=e.subscribe??!1}else this.context=t,this.callback=n,this.subscribe=r??!1;this.host.addController(this)}hostConnected(){this.dispatchRequest()}hostDisconnected(){this.unsubscribe&&=(this.unsubscribe(),void 0)}dispatchRequest(){this.host.dispatchEvent(new i(this.context,this.host,this.t,this.subscribe))}},s=class{get value(){return this.o}set value(e){this.setValue(e)}setValue(e,t=!1){let n=t||!Object.is(e,this.o);this.o=e,n&&this.updateObservers()}constructor(e){this.subscriptions=new Map,this.updateObservers=()=>{for(let[e,{disposer:t}]of this.subscriptions)e(this.o,t)},e!==void 0&&(this.value=e)}addCallback(e,t,n){if(!n)return void e(this.value);this.subscriptions.has(e)||this.subscriptions.set(e,{disposer:()=>{this.subscriptions.delete(e)},consumerHost:t});let{disposer:r}=this.subscriptions.get(e);e(this.value,r)}clearCallbacks(){this.subscriptions.clear()}},c=class extends Event{constructor(e,t){super(`context-provider`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t}},l=class extends s{constructor(e,t,n){super(t.context===void 0?n:t.initialValue),this.onContextRequest=e=>{if(e.context!==this.context)return;let t=e.contextTarget??e.composedPath()[0];t!==this.host&&(e.stopPropagation(),this.addCallback(e.callback,t,e.subscribe))},this.onProviderRequest=e=>{if(e.context!==this.context||(e.contextTarget??e.composedPath()[0])===this.host)return;let t=new Set;for(let[e,{consumerHost:n}]of this.subscriptions)t.has(e)||(t.add(e),n.dispatchEvent(new i(this.context,n,e,!0)));e.stopPropagation()},this.host=e,t.context===void 0?this.context=t:this.context=t.context,this.attachListeners(),this.host.addController?.(this)}attachListeners(){this.host.addEventListener(`context-request`,this.onContextRequest),this.host.addEventListener(`context-provider`,this.onProviderRequest)}hostConnected(){this.host.dispatchEvent(new c(this.context,this.host))}};function u({context:e}){return(t,n)=>{let r=new WeakMap;if(typeof n==`object`)return{get(){return t.get.call(this)},set(e){return r.get(this).setValue(e),t.set.call(this,e)},init(t){return r.set(this,new l(this,{context:e,initialValue:t})),t}};{t.constructor.addInitializer((t=>{r.set(t,new l(t,{context:e}))}));let i=Object.getOwnPropertyDescriptor(t,n),a;if(i===void 0){let e=new WeakMap;a={get(){return e.get(this)},set(t){r.get(this).setValue(t),e.set(this,t)},configurable:!0,enumerable:!0}}else{let e=i.set;a={...i,set(t){r.get(this).setValue(t),e?.call(this,t)}}}Object.defineProperty(t,n,a);return}}}function d({context:e,subscribe:t}){return(n,r)=>{typeof r==`object`?r.addInitializer((function(){new o(this,{context:e,callback:e=>{n.set.call(this,e)},subscribe:t})})):n.constructor.addInitializer((n=>{new o(n,{context:e,callback:e=>{n[r]=e},subscribe:t})}))}}var f=a(Symbol(`pkt-tabs-context`)),p=class extends t.t{constructor(...e){super(...e),this.arrowNav=!0,this.disableArrowNav=!1,this.tabRefs=[],this.tabCount=0,this.context={useArrowNav:this.useArrowNav,registerTab:this.registerTab.bind(this),handleClick:this.handleClick.bind(this),handleKeyUp:this.handleKeyUp.bind(this)}}get useArrowNav(){return this.arrowNav&&!this.disableArrowNav}updated(e){(e.has(`arrowNav`)||e.has(`disableArrowNav`))&&(this.context={...this.context,useArrowNav:this.useArrowNav})}registerTab(e,t){this.tabRefs[t]=e,this.tabCount=Math.max(this.tabCount,t+1)}handleClick(e){this.dispatchEvent(new CustomEvent(`tab-selected`,{detail:{index:e},bubbles:!0,composed:!0}))}handleKeyUp(e,t){this.useArrowNav
|
|
1
|
+
const e=require(`./element-DjtxO-1r.cjs`),t=require(`./element-with-slot-CWoTGA1B.cjs`),n=require(`./if-defined-XKOD_t_V.cjs`),r=require(`./ref-BaJ0mBT_.cjs`);var i=class extends Event{constructor(e,t,n,r){super(`context-request`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t,this.callback=n,this.subscribe=r??!1}};function a(e){return e}var o=class{constructor(e,t,n,r){if(this.subscribe=!1,this.provided=!1,this.value=void 0,this.t=(e,t)=>{this.unsubscribe&&(this.unsubscribe!==t&&(this.provided=!1,this.unsubscribe()),this.subscribe||this.unsubscribe()),this.value=e,this.host.requestUpdate(),this.provided&&!this.subscribe||(this.provided=!0,this.callback&&this.callback(e,t)),this.unsubscribe=t},this.host=e,t.context!==void 0){let e=t;this.context=e.context,this.callback=e.callback,this.subscribe=e.subscribe??!1}else this.context=t,this.callback=n,this.subscribe=r??!1;this.host.addController(this)}hostConnected(){this.dispatchRequest()}hostDisconnected(){this.unsubscribe&&=(this.unsubscribe(),void 0)}dispatchRequest(){this.host.dispatchEvent(new i(this.context,this.host,this.t,this.subscribe))}},s=class{get value(){return this.o}set value(e){this.setValue(e)}setValue(e,t=!1){let n=t||!Object.is(e,this.o);this.o=e,n&&this.updateObservers()}constructor(e){this.subscriptions=new Map,this.updateObservers=()=>{for(let[e,{disposer:t}]of this.subscriptions)e(this.o,t)},e!==void 0&&(this.value=e)}addCallback(e,t,n){if(!n)return void e(this.value);this.subscriptions.has(e)||this.subscriptions.set(e,{disposer:()=>{this.subscriptions.delete(e)},consumerHost:t});let{disposer:r}=this.subscriptions.get(e);e(this.value,r)}clearCallbacks(){this.subscriptions.clear()}},c=class extends Event{constructor(e,t){super(`context-provider`,{bubbles:!0,composed:!0}),this.context=e,this.contextTarget=t}},l=class extends s{constructor(e,t,n){super(t.context===void 0?n:t.initialValue),this.onContextRequest=e=>{if(e.context!==this.context)return;let t=e.contextTarget??e.composedPath()[0];t!==this.host&&(e.stopPropagation(),this.addCallback(e.callback,t,e.subscribe))},this.onProviderRequest=e=>{if(e.context!==this.context||(e.contextTarget??e.composedPath()[0])===this.host)return;let t=new Set;for(let[e,{consumerHost:n}]of this.subscriptions)t.has(e)||(t.add(e),n.dispatchEvent(new i(this.context,n,e,!0)));e.stopPropagation()},this.host=e,t.context===void 0?this.context=t:this.context=t.context,this.attachListeners(),this.host.addController?.(this)}attachListeners(){this.host.addEventListener(`context-request`,this.onContextRequest),this.host.addEventListener(`context-provider`,this.onProviderRequest)}hostConnected(){this.host.dispatchEvent(new c(this.context,this.host))}};function u({context:e}){return(t,n)=>{let r=new WeakMap;if(typeof n==`object`)return{get(){return t.get.call(this)},set(e){return r.get(this).setValue(e),t.set.call(this,e)},init(t){return r.set(this,new l(this,{context:e,initialValue:t})),t}};{t.constructor.addInitializer((t=>{r.set(t,new l(t,{context:e}))}));let i=Object.getOwnPropertyDescriptor(t,n),a;if(i===void 0){let e=new WeakMap;a={get(){return e.get(this)},set(t){r.get(this).setValue(t),e.set(this,t)},configurable:!0,enumerable:!0}}else{let e=i.set;a={...i,set(t){r.get(this).setValue(t),e?.call(this,t)}}}Object.defineProperty(t,n,a);return}}}function d({context:e,subscribe:t}){return(n,r)=>{typeof r==`object`?r.addInitializer((function(){new o(this,{context:e,callback:e=>{n.set.call(this,e)},subscribe:t})})):n.constructor.addInitializer((n=>{new o(n,{context:e,callback:e=>{n[r]=e},subscribe:t})}))}}var f=a(Symbol(`pkt-tabs-context`)),p=class extends t.t{constructor(...e){super(...e),this.arrowNav=!0,this.disableArrowNav=!1,this.tabRefs=[],this.disabledMap={},this.tabCount=0,this.context={useArrowNav:this.useArrowNav,registerTab:this.registerTab.bind(this),handleClick:this.handleClick.bind(this),handleKeyUp:this.handleKeyUp.bind(this)}}get useArrowNav(){return this.arrowNav&&!this.disableArrowNav}updated(e){(e.has(`arrowNav`)||e.has(`disableArrowNav`))&&(this.context={...this.context,useArrowNav:this.useArrowNav})}registerTab(e,t,n=!1){this.tabRefs[t]=e,this.disabledMap[t]=n,this.tabCount=Math.max(this.tabCount,t+1)}isTabDisabled(e){return!!this.disabledMap[e]}findEnabledIndex(e,t){let n=e+t;for(;n>=0&&n<this.tabCount;){if(!this.isTabDisabled(n))return n;n+=t}return null}handleClick(e){this.isTabDisabled(e)||this.dispatchEvent(new CustomEvent(`tab-selected`,{detail:{index:e},bubbles:!0,composed:!0}))}handleKeyUp(e,t){if(this.useArrowNav){if(e.key===`ArrowLeft`){e.preventDefault();let n=this.findEnabledIndex(t,-1);n!==null&&this.tabRefs[n]?.focus()}if(e.key===`ArrowRight`){e.preventDefault();let n=this.findEnabledIndex(t,1);n!==null&&this.tabRefs[n]?.focus()}(e.key===`Enter`||e.key===` `||e.key===`Spacebar`||e.key===`ArrowDown`)&&(e.preventDefault(),this.handleClick(t))}}render(){return e.d`
|
|
2
2
|
<div class="pkt-tabs">
|
|
3
3
|
<div class="pkt-tabs__list" role=${this.useArrowNav?`tablist`:`navigation`}>${t.n(this)}</div>
|
|
4
4
|
</div>
|
|
5
|
-
`}};e.r([e.s({type:Boolean,reflect:!0,attribute:`arrow-nav`})],p.prototype,`arrowNav`,void 0),e.r([e.s({type:Boolean,reflect:!0,attribute:`disable-arrow-nav`})],p.prototype,`disableArrowNav`,void 0),e.r([e.o()],p.prototype,`tabRefs`,void 0),e.r([e.o()],p.prototype,`tabCount`,void 0),e.r([u({context:f}),e.o()],p.prototype,`context`,void 0);try{e.c(`pkt-tabs`)(p)}catch{console.warn(`Forsøker å definere <pkt-tabs>, men den er allerede definert`)}var m=class extends t.t{constructor(){super(),this.active=!1,this.href=``,this.icon=``,this.controls=``,this.tag=``,this.tagSkin=`blue`,this.index=0,this.elementRef=r.t()}connectedCallback(){super.connectedCallback(),this.updateComplete.then(()=>{this.elementRef.value&&this.context&&this.context.registerTab(this.elementRef.value,this.index)})}handleClick(){this.context&&this.context.handleClick(this.index)}
|
|
5
|
+
`}};e.r([e.s({type:Boolean,reflect:!0,attribute:`arrow-nav`})],p.prototype,`arrowNav`,void 0),e.r([e.s({type:Boolean,reflect:!0,attribute:`disable-arrow-nav`})],p.prototype,`disableArrowNav`,void 0),e.r([e.o()],p.prototype,`tabRefs`,void 0),e.r([e.o()],p.prototype,`disabledMap`,void 0),e.r([e.o()],p.prototype,`tabCount`,void 0),e.r([u({context:f}),e.o()],p.prototype,`context`,void 0);try{e.c(`pkt-tabs`)(p)}catch{console.warn(`Forsøker å definere <pkt-tabs>, men den er allerede definert`)}var m=class extends t.t{constructor(){super(),this.active=!1,this.disabled=!1,this.href=``,this.icon=``,this.controls=``,this.tag=``,this.tagSkin=`blue`,this.index=0,this.elementRef=r.t()}connectedCallback(){super.connectedCallback(),this.updateComplete.then(()=>{this.elementRef.value&&this.context&&this.context.registerTab(this.elementRef.value,this.index,this.disabled)})}updated(){this.elementRef.value&&this.context&&this.context.registerTab(this.elementRef.value,this.index,this.disabled)}handleClick(e){if(this.disabled){e.preventDefault(),e.stopPropagation();return}this.context&&this.context.handleClick(this.index)}handleKeyDown(e){if(this.disabled&&(e.key===`Enter`||e.key===` `||e.key===`Spacebar`)){e.preventDefault(),e.stopPropagation();return}this.context&&this.context.handleKeyUp(e,this.index)}render(){let i=this.context?.useArrowNav??!0,a=this.active&&!this.disabled,o=[a?`active`:``,this.disabled?`pkt-tabs__item--disabled`:``].filter(Boolean).join(` `),s=i?`tab`:void 0,c=i?a:void 0,l=this.disabled?-1:a||!i?void 0:-1,u=e.d`
|
|
6
6
|
${this.icon?e.d`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>`:``}
|
|
7
7
|
<span>${t.n(this)}</span>
|
|
8
8
|
${this.tag?e.d`<pkt-tag skin=${this.tagSkin} size="small">${this.tag}</pkt-tag>`:``}
|
|
9
9
|
`;return this.href?e.d`
|
|
10
10
|
<a
|
|
11
11
|
${r.n(this.elementRef)}
|
|
12
|
-
href=${this.href}
|
|
13
|
-
class="pkt-tabs__link ${
|
|
14
|
-
role=${n.t(
|
|
15
|
-
aria-selected=${n.t(
|
|
12
|
+
href=${n.t(this.disabled?void 0:this.href)}
|
|
13
|
+
class="pkt-tabs__link ${o}"
|
|
14
|
+
role=${n.t(s)}
|
|
15
|
+
aria-selected=${n.t(c)}
|
|
16
|
+
aria-disabled=${n.t(this.disabled?`true`:void 0)}
|
|
16
17
|
aria-controls=${n.t(this.controls||void 0)}
|
|
17
|
-
tabindex=${n.t(
|
|
18
|
+
tabindex=${n.t(l)}
|
|
18
19
|
@click=${this.handleClick}
|
|
19
|
-
@
|
|
20
|
+
@keydown=${this.handleKeyDown}
|
|
20
21
|
>
|
|
21
|
-
${
|
|
22
|
+
${u}
|
|
22
23
|
</a>
|
|
23
24
|
`:e.d`
|
|
24
25
|
<button
|
|
25
26
|
${r.n(this.elementRef)}
|
|
26
27
|
type="button"
|
|
27
|
-
class="pkt-tabs__button pkt-link-button ${
|
|
28
|
-
|
|
29
|
-
aria-
|
|
28
|
+
class="pkt-tabs__button pkt-link-button ${o}"
|
|
29
|
+
?disabled=${this.disabled}
|
|
30
|
+
aria-disabled=${n.t(this.disabled?`true`:void 0)}
|
|
31
|
+
role=${n.t(s)}
|
|
32
|
+
aria-selected=${n.t(c)}
|
|
30
33
|
aria-controls=${n.t(this.controls||void 0)}
|
|
31
|
-
tabindex=${n.t(
|
|
34
|
+
tabindex=${n.t(l)}
|
|
32
35
|
@click=${this.handleClick}
|
|
33
|
-
@
|
|
36
|
+
@keydown=${this.handleKeyDown}
|
|
34
37
|
>
|
|
35
|
-
${
|
|
38
|
+
${u}
|
|
36
39
|
</button>
|
|
37
|
-
`}};e.r([e.s({type:Boolean,reflect:!0})],m.prototype,`active`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`href`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`icon`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`controls`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`tag`,void 0),e.r([e.s({type:String,reflect:!0,attribute:`tag-skin`})],m.prototype,`tagSkin`,void 0),e.r([e.s({type:Number,reflect:!0})],m.prototype,`index`,void 0),e.r([d({context:f,subscribe:!0}),e.s({attribute:!1})],m.prototype,`context`,void 0);try{e.c(`pkt-tab-item`)(m)}catch{console.warn(`Forsøker å definere <pkt-tab-item>, men den er allerede definert`)}var h=p;Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return h}});
|
|
40
|
+
`}};e.r([e.s({type:Boolean,reflect:!0})],m.prototype,`active`,void 0),e.r([e.s({type:Boolean,reflect:!0})],m.prototype,`disabled`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`href`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`icon`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`controls`,void 0),e.r([e.s({type:String,reflect:!0})],m.prototype,`tag`,void 0),e.r([e.s({type:String,reflect:!0,attribute:`tag-skin`})],m.prototype,`tagSkin`,void 0),e.r([e.s({type:Number,reflect:!0})],m.prototype,`index`,void 0),e.r([d({context:f,subscribe:!0}),e.s({attribute:!1})],m.prototype,`context`,void 0);try{e.c(`pkt-tab-item`)(m)}catch{console.warn(`Forsøker å definere <pkt-tab-item>, men den er allerede definert`)}var h=p;Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return h}});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-elements",
|
|
3
|
-
"version": "16.
|
|
3
|
+
"version": "16.8.2",
|
|
4
4
|
"description": "Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo",
|
|
5
5
|
"homepage": "https://punkt.oslo.kommune.no",
|
|
6
6
|
"author": "Team Designsystem, Oslo Origo",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
79
79
|
},
|
|
80
80
|
"license": "MIT",
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "1ff7d19b9d19c3b3e810dcee4bc3bd1d3fcc3610"
|
|
82
82
|
}
|
|
@@ -11,6 +11,7 @@ export type TSkin = 'blue' | 'green' | 'red' | 'beige' | 'yellow' | 'grey' | 'gr
|
|
|
11
11
|
|
|
12
12
|
export interface IPktTabItem {
|
|
13
13
|
active?: boolean
|
|
14
|
+
disabled?: boolean
|
|
14
15
|
href?: string
|
|
15
16
|
icon?: string
|
|
16
17
|
controls?: string
|
|
@@ -21,6 +22,7 @@ export interface IPktTabItem {
|
|
|
21
22
|
|
|
22
23
|
export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktTabItem {
|
|
23
24
|
@property({ type: Boolean, reflect: true }) active: boolean = false
|
|
25
|
+
@property({ type: Boolean, reflect: true }) disabled: boolean = false
|
|
24
26
|
@property({ type: String, reflect: true }) href: string = ''
|
|
25
27
|
@property({ type: String, reflect: true }) icon: string = ''
|
|
26
28
|
@property({ type: String, reflect: true }) controls: string = ''
|
|
@@ -44,18 +46,36 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
|
|
|
44
46
|
// Wait for element to be fully initialized
|
|
45
47
|
this.updateComplete.then(() => {
|
|
46
48
|
if (this.elementRef.value && this.context) {
|
|
47
|
-
this.context.registerTab(this.elementRef.value, this.index)
|
|
49
|
+
this.context.registerTab(this.elementRef.value, this.index, this.disabled)
|
|
48
50
|
}
|
|
49
51
|
})
|
|
50
52
|
}
|
|
51
53
|
|
|
52
|
-
|
|
54
|
+
updated() {
|
|
55
|
+
if (this.elementRef.value && this.context) {
|
|
56
|
+
this.context.registerTab(this.elementRef.value, this.index, this.disabled)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private handleClick(event: Event) {
|
|
61
|
+
if (this.disabled) {
|
|
62
|
+
event.preventDefault()
|
|
63
|
+
event.stopPropagation()
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
53
67
|
if (this.context) {
|
|
54
68
|
this.context.handleClick(this.index)
|
|
55
69
|
}
|
|
56
70
|
}
|
|
57
71
|
|
|
58
|
-
private
|
|
72
|
+
private handleKeyDown(event: KeyboardEvent) {
|
|
73
|
+
if (this.disabled && (event.key === 'Enter' || event.key === ' ' || event.key === 'Spacebar')) {
|
|
74
|
+
event.preventDefault()
|
|
75
|
+
event.stopPropagation()
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
59
79
|
if (this.context) {
|
|
60
80
|
this.context.handleKeyUp(event, this.index)
|
|
61
81
|
}
|
|
@@ -63,10 +83,13 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
|
|
|
63
83
|
|
|
64
84
|
render() {
|
|
65
85
|
const useArrowNav = this.context?.useArrowNav ?? true
|
|
66
|
-
const
|
|
86
|
+
const isActive = this.active && !this.disabled
|
|
87
|
+
const commonClasses = [isActive ? 'active' : '', this.disabled ? 'pkt-tabs__item--disabled' : '']
|
|
88
|
+
.filter(Boolean)
|
|
89
|
+
.join(' ')
|
|
67
90
|
const role = useArrowNav ? 'tab' : undefined
|
|
68
|
-
const ariaSelected = useArrowNav ?
|
|
69
|
-
const tabIndex = this.
|
|
91
|
+
const ariaSelected = useArrowNav ? isActive : undefined
|
|
92
|
+
const tabIndex = this.disabled ? -1 : isActive || !useArrowNav ? undefined : -1
|
|
70
93
|
|
|
71
94
|
const content = html`
|
|
72
95
|
${this.icon ? html`<pkt-icon name=${this.icon} class="pkt-icon--small"></pkt-icon>` : ''}
|
|
@@ -78,14 +101,15 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
|
|
|
78
101
|
return html`
|
|
79
102
|
<a
|
|
80
103
|
${ref(this.elementRef)}
|
|
81
|
-
href=${this.href}
|
|
104
|
+
href=${ifDefined(this.disabled ? undefined : this.href)}
|
|
82
105
|
class="pkt-tabs__link ${commonClasses}"
|
|
83
106
|
role=${ifDefined(role)}
|
|
84
107
|
aria-selected=${ifDefined(ariaSelected)}
|
|
108
|
+
aria-disabled=${ifDefined(this.disabled ? 'true' : undefined)}
|
|
85
109
|
aria-controls=${ifDefined(this.controls || undefined)}
|
|
86
110
|
tabindex=${ifDefined(tabIndex)}
|
|
87
111
|
@click=${this.handleClick}
|
|
88
|
-
@
|
|
112
|
+
@keydown=${this.handleKeyDown}
|
|
89
113
|
>
|
|
90
114
|
${content}
|
|
91
115
|
</a>
|
|
@@ -97,12 +121,14 @@ export class PktTabItem extends PktElementWithSlot<IPktTabItem> implements IPktT
|
|
|
97
121
|
${ref(this.elementRef)}
|
|
98
122
|
type="button"
|
|
99
123
|
class="pkt-tabs__button pkt-link-button ${commonClasses}"
|
|
124
|
+
?disabled=${this.disabled}
|
|
125
|
+
aria-disabled=${ifDefined(this.disabled ? 'true' : undefined)}
|
|
100
126
|
role=${ifDefined(role)}
|
|
101
127
|
aria-selected=${ifDefined(ariaSelected)}
|
|
102
128
|
aria-controls=${ifDefined(this.controls || undefined)}
|
|
103
129
|
tabindex=${ifDefined(tabIndex)}
|
|
104
130
|
@click=${this.handleClick}
|
|
105
|
-
@
|
|
131
|
+
@keydown=${this.handleKeyDown}
|
|
106
132
|
>
|
|
107
133
|
${content}
|
|
108
134
|
</button>
|
|
@@ -9,7 +9,7 @@ export interface TabsContext {
|
|
|
9
9
|
/**
|
|
10
10
|
* Register a tab item with the parent tabs container
|
|
11
11
|
*/
|
|
12
|
-
registerTab: (element: HTMLElement, index: number) => void
|
|
12
|
+
registerTab: (element: HTMLElement, index: number, disabled?: boolean) => void
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Handle click event from a tab item
|
|
@@ -216,7 +216,7 @@ describe('PktTabs', () => {
|
|
|
216
216
|
expect(document.activeElement).toBe(firstButton)
|
|
217
217
|
|
|
218
218
|
// Simulate ArrowRight key
|
|
219
|
-
const keyEvent = new KeyboardEvent('
|
|
219
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
220
220
|
firstButton.dispatchEvent(keyEvent)
|
|
221
221
|
|
|
222
222
|
// Wait for focus to change
|
|
@@ -236,7 +236,7 @@ describe('PktTabs', () => {
|
|
|
236
236
|
expect(document.activeElement).toBe(secondButton)
|
|
237
237
|
|
|
238
238
|
// Simulate ArrowLeft key
|
|
239
|
-
const keyEvent = new KeyboardEvent('
|
|
239
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true })
|
|
240
240
|
secondButton.dispatchEvent(keyEvent)
|
|
241
241
|
|
|
242
242
|
// Wait for focus to change
|
|
@@ -255,7 +255,7 @@ describe('PktTabs', () => {
|
|
|
255
255
|
expect(document.activeElement).toBe(thirdButton)
|
|
256
256
|
|
|
257
257
|
// Simulate ArrowRight key
|
|
258
|
-
const keyEvent = new KeyboardEvent('
|
|
258
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
259
259
|
thirdButton.dispatchEvent(keyEvent)
|
|
260
260
|
|
|
261
261
|
// Wait
|
|
@@ -275,7 +275,7 @@ describe('PktTabs', () => {
|
|
|
275
275
|
expect(document.activeElement).toBe(firstButton)
|
|
276
276
|
|
|
277
277
|
// Simulate ArrowLeft key
|
|
278
|
-
const keyEvent = new KeyboardEvent('
|
|
278
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true })
|
|
279
279
|
firstButton.dispatchEvent(keyEvent)
|
|
280
280
|
|
|
281
281
|
// Wait
|
|
@@ -296,7 +296,7 @@ describe('PktTabs', () => {
|
|
|
296
296
|
const button = secondTabItem.querySelector('button') as HTMLButtonElement
|
|
297
297
|
|
|
298
298
|
// Simulate Space key
|
|
299
|
-
const keyEvent = new KeyboardEvent('
|
|
299
|
+
const keyEvent = new KeyboardEvent('keydown', { key: ' ', bubbles: true })
|
|
300
300
|
button.dispatchEvent(keyEvent)
|
|
301
301
|
|
|
302
302
|
// Wait
|
|
@@ -317,7 +317,7 @@ describe('PktTabs', () => {
|
|
|
317
317
|
const button = secondTabItem.querySelector('button') as HTMLButtonElement
|
|
318
318
|
|
|
319
319
|
// Simulate ArrowDown key
|
|
320
|
-
const keyEvent = new KeyboardEvent('
|
|
320
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true })
|
|
321
321
|
button.dispatchEvent(keyEvent)
|
|
322
322
|
|
|
323
323
|
// Wait
|
|
@@ -340,7 +340,7 @@ describe('PktTabs', () => {
|
|
|
340
340
|
expect(document.activeElement).toBe(firstButton)
|
|
341
341
|
|
|
342
342
|
// Simulate ArrowRight key
|
|
343
|
-
const keyEvent = new KeyboardEvent('
|
|
343
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
344
344
|
firstButton.dispatchEvent(keyEvent)
|
|
345
345
|
|
|
346
346
|
// Wait
|
|
@@ -360,7 +360,7 @@ describe('PktTabs', () => {
|
|
|
360
360
|
expect(document.activeElement).toBe(firstButton)
|
|
361
361
|
|
|
362
362
|
// Simulate ArrowRight key
|
|
363
|
-
const keyEvent = new KeyboardEvent('
|
|
363
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
364
364
|
firstButton.dispatchEvent(keyEvent)
|
|
365
365
|
|
|
366
366
|
// Wait
|
|
@@ -379,7 +379,7 @@ describe('PktTabs', () => {
|
|
|
379
379
|
firstButton.focus()
|
|
380
380
|
|
|
381
381
|
// Simulate ArrowRight key
|
|
382
|
-
const keyEvent = new KeyboardEvent('
|
|
382
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
383
383
|
firstButton.dispatchEvent(keyEvent)
|
|
384
384
|
|
|
385
385
|
// Wait
|
|
@@ -388,6 +388,83 @@ describe('PktTabs', () => {
|
|
|
388
388
|
// Should NOT move to second tab (disableArrowNav overrides arrowNav)
|
|
389
389
|
expect(document.activeElement).toBe(firstButton)
|
|
390
390
|
})
|
|
391
|
+
|
|
392
|
+
test('skips disabled tabs during ArrowRight navigation', async () => {
|
|
393
|
+
const container = await createTabs(
|
|
394
|
+
'',
|
|
395
|
+
`
|
|
396
|
+
<pkt-tab-item index="0" active>Tab 1</pkt-tab-item>
|
|
397
|
+
<pkt-tab-item index="1" disabled>Tab 2</pkt-tab-item>
|
|
398
|
+
<pkt-tab-item index="2">Tab 3</pkt-tab-item>
|
|
399
|
+
`,
|
|
400
|
+
)
|
|
401
|
+
const tabItems = container.querySelectorAll('pkt-tab-item')
|
|
402
|
+
const firstButton = tabItems[0].querySelector('button') as HTMLButtonElement
|
|
403
|
+
const thirdButton = tabItems[2].querySelector('button') as HTMLButtonElement
|
|
404
|
+
|
|
405
|
+
firstButton.focus()
|
|
406
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
407
|
+
firstButton.dispatchEvent(keyEvent)
|
|
408
|
+
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
409
|
+
|
|
410
|
+
expect(document.activeElement).toBe(thirdButton)
|
|
411
|
+
})
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
describe('Disabled tabs', () => {
|
|
415
|
+
test('does not dispatch tab-selected for disabled button tab', async () => {
|
|
416
|
+
const container = await createTabs(
|
|
417
|
+
'',
|
|
418
|
+
`
|
|
419
|
+
<pkt-tab-item index="0" active>Tab 1</pkt-tab-item>
|
|
420
|
+
<pkt-tab-item index="1" disabled>Tab 2</pkt-tab-item>
|
|
421
|
+
`,
|
|
422
|
+
)
|
|
423
|
+
const tabs = container.querySelector('pkt-tabs') as PktTabs
|
|
424
|
+
const eventListener = vi.fn()
|
|
425
|
+
tabs.addEventListener('tab-selected', eventListener)
|
|
426
|
+
|
|
427
|
+
const disabledButton = container.querySelectorAll('pkt-tab-item')[1].querySelector('button')
|
|
428
|
+
disabledButton?.click()
|
|
429
|
+
|
|
430
|
+
expect(disabledButton).toHaveAttribute('disabled')
|
|
431
|
+
expect(eventListener).not.toHaveBeenCalled()
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
test('disabled link tab has aria-disabled and does not dispatch event', async () => {
|
|
435
|
+
const container = await createTabs(
|
|
436
|
+
'',
|
|
437
|
+
`
|
|
438
|
+
<pkt-tab-item index="0" href="/enabled" active>Enabled</pkt-tab-item>
|
|
439
|
+
<pkt-tab-item index="1" href="/disabled" disabled>Disabled</pkt-tab-item>
|
|
440
|
+
`,
|
|
441
|
+
)
|
|
442
|
+
const tabs = container.querySelector('pkt-tabs') as PktTabs
|
|
443
|
+
const eventListener = vi.fn()
|
|
444
|
+
tabs.addEventListener('tab-selected', eventListener)
|
|
445
|
+
|
|
446
|
+
const disabledLink = container.querySelectorAll('pkt-tab-item')[1].querySelector('a') as HTMLAnchorElement
|
|
447
|
+
disabledLink.click()
|
|
448
|
+
disabledLink.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
|
|
449
|
+
|
|
450
|
+
expect(disabledLink).toHaveAttribute('aria-disabled', 'true')
|
|
451
|
+
expect(disabledLink).toHaveAttribute('tabindex', '-1')
|
|
452
|
+
expect(disabledLink).not.toHaveAttribute('href')
|
|
453
|
+
expect(eventListener).not.toHaveBeenCalled()
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
test('disabled active tab is not exposed as selected', async () => {
|
|
457
|
+
const container = await createTabs(
|
|
458
|
+
'',
|
|
459
|
+
`
|
|
460
|
+
<pkt-tab-item index="0" active disabled>Disabled active</pkt-tab-item>
|
|
461
|
+
<pkt-tab-item index="1">Enabled</pkt-tab-item>
|
|
462
|
+
`,
|
|
463
|
+
)
|
|
464
|
+
const disabledButton = container.querySelector('pkt-tab-item button')
|
|
465
|
+
|
|
466
|
+
expect(disabledButton).toHaveAttribute('aria-selected', 'false')
|
|
467
|
+
})
|
|
391
468
|
})
|
|
392
469
|
|
|
393
470
|
describe('Accessibility', () => {
|
|
@@ -483,7 +560,7 @@ describe('PktTabs', () => {
|
|
|
483
560
|
|
|
484
561
|
// Navigate through all tabs
|
|
485
562
|
for (let i = 0; i < 4; i++) {
|
|
486
|
-
const keyEvent = new KeyboardEvent('
|
|
563
|
+
const keyEvent = new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })
|
|
487
564
|
;(document.activeElement as HTMLElement).dispatchEvent(keyEvent)
|
|
488
565
|
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
489
566
|
}
|
|
@@ -16,6 +16,7 @@ export class PktTabs extends PktElementWithSlot<IPktTabs> implements IPktTabs {
|
|
|
16
16
|
disableArrowNav: boolean = false
|
|
17
17
|
|
|
18
18
|
@state() private tabRefs: Array<HTMLAnchorElement | HTMLButtonElement | null> = []
|
|
19
|
+
@state() private disabledMap: Record<number, boolean> = {}
|
|
19
20
|
@state() private tabCount: number = 0
|
|
20
21
|
|
|
21
22
|
private get useArrowNav(): boolean {
|
|
@@ -42,12 +43,30 @@ export class PktTabs extends PktElementWithSlot<IPktTabs> implements IPktTabs {
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
private registerTab(element: HTMLElement, index: number) {
|
|
46
|
+
private registerTab(element: HTMLElement, index: number, disabled = false) {
|
|
46
47
|
this.tabRefs[index] = element as HTMLAnchorElement | HTMLButtonElement
|
|
48
|
+
this.disabledMap[index] = disabled
|
|
47
49
|
this.tabCount = Math.max(this.tabCount, index + 1)
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
private isTabDisabled(index: number): boolean {
|
|
53
|
+
return !!this.disabledMap[index]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private findEnabledIndex(startIndex: number, direction: -1 | 1): number | null {
|
|
57
|
+
let current = startIndex + direction
|
|
58
|
+
|
|
59
|
+
while (current >= 0 && current < this.tabCount) {
|
|
60
|
+
if (!this.isTabDisabled(current)) return current
|
|
61
|
+
current += direction
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
private handleClick(index: number) {
|
|
68
|
+
if (this.isTabDisabled(index)) return
|
|
69
|
+
|
|
51
70
|
this.dispatchEvent(
|
|
52
71
|
new CustomEvent('tab-selected', {
|
|
53
72
|
detail: { index },
|
|
@@ -60,22 +79,26 @@ export class PktTabs extends PktElementWithSlot<IPktTabs> implements IPktTabs {
|
|
|
60
79
|
private handleKeyUp(keyEvent: KeyboardEvent, index: number) {
|
|
61
80
|
if (!this.useArrowNav) return
|
|
62
81
|
|
|
63
|
-
if (keyEvent.
|
|
64
|
-
|
|
82
|
+
if (keyEvent.key === 'ArrowLeft') {
|
|
83
|
+
keyEvent.preventDefault()
|
|
84
|
+
const previousEnabled = this.findEnabledIndex(index, -1)
|
|
85
|
+
if (previousEnabled !== null) this.tabRefs[previousEnabled]?.focus()
|
|
65
86
|
}
|
|
66
87
|
|
|
67
|
-
if (keyEvent.
|
|
68
|
-
|
|
88
|
+
if (keyEvent.key === 'ArrowRight') {
|
|
89
|
+
keyEvent.preventDefault()
|
|
90
|
+
const nextEnabled = this.findEnabledIndex(index, 1)
|
|
91
|
+
if (nextEnabled !== null) this.tabRefs[nextEnabled]?.focus()
|
|
69
92
|
}
|
|
70
93
|
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
)
|
|
94
|
+
if (
|
|
95
|
+
keyEvent.key === 'Enter' ||
|
|
96
|
+
keyEvent.key === ' ' ||
|
|
97
|
+
keyEvent.key === 'Spacebar' ||
|
|
98
|
+
keyEvent.key === 'ArrowDown'
|
|
99
|
+
) {
|
|
100
|
+
keyEvent.preventDefault()
|
|
101
|
+
this.handleClick(index)
|
|
79
102
|
}
|
|
80
103
|
}
|
|
81
104
|
|