@oslokommune/punkt-elements 16.3.0 → 16.5.0

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 CHANGED
@@ -5,6 +5,23 @@ og skriver commits ca etter [Conventional Commits](https://conventionalcommits.o
5
5
 
6
6
  ---
7
7
 
8
+ ## [16.5.0](https://github.com/oslokommune/punkt/compare/16.4.0...16.5.0) (2026-03-27)
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.3.0](https://github.com/oslokommune/punkt/compare/16.2.0...16.3.0) (2026-03-24)
9
26
 
10
27
  ### ⚠ BREAKING CHANGES
@@ -0,0 +1 @@
1
+ export { }
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { IAriaAttributes } from '../../../../../shared-types';
4
4
  import { IDatepickerStrings } from '../../../../../shared-types/datepicker';
5
5
  import { IPktComboboxOption } from '../../../../../shared-types/combobox';
6
6
  import { LitElement } from 'lit';
7
+ import { nothing } from 'lit';
7
8
  import { PktIconName } from '@oslokommune/punkt-assets/dist/icons/icon';
8
9
  import { PropertyValues } from 'lit';
9
10
  import { ReactiveController } from 'lit';
@@ -154,6 +155,11 @@ declare class ComboboxValue extends ComboboxBase {
154
155
  */
155
156
  declare type ElementProps<Element, PropKeys extends keyof Element> = Partial<Pick<Element, PropKeys>>;
156
157
 
158
+ export declare interface IBreadcrumbItem {
159
+ text: string;
160
+ href: string;
161
+ }
162
+
157
163
  export declare interface IPktAccordion {
158
164
  compact?: boolean;
159
165
  skin?: TPktAccordionSkin;
@@ -194,6 +200,10 @@ export declare interface IPktBackLink {
194
200
  ariaLabel?: string;
195
201
  }
196
202
 
203
+ export declare interface IPktBreadcrumbs {
204
+ breadcrumbs: IBreadcrumbItem[];
205
+ }
206
+
197
207
  export declare interface IPktButton {
198
208
  iconName?: PktIconName;
199
209
  secondIconName?: PktIconName;
@@ -530,6 +540,12 @@ export declare class PktBackLink extends PktElement<IPktBackLink> implements IPk
530
540
  render(): TemplateResult<1>;
531
541
  }
532
542
 
543
+ export declare class PktBreadcrumbs extends PktElement implements IPktBreadcrumbs {
544
+ breadcrumbs: IBreadcrumbItem[];
545
+ private handleLinkClick;
546
+ render(): typeof nothing | TemplateResult<1>;
547
+ }
548
+
533
549
  export declare class PktButton extends PktElementWithSlot<IPktButton> implements IPktButton {
534
550
  iconName: string;
535
551
  secondIconName: string;
@@ -0,0 +1,43 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("./element-CMTfByxQ.cjs");require("./icon-Dj0oZZSa.cjs");var l=Object.defineProperty,p=Object.getOwnPropertyDescriptor,i=(n,e,t,s)=>{for(var r=s>1?void 0:s?p(e,t):e,c=n.length-1,b;c>=0;c--)(b=n[c])&&(r=(s?b(e,t,r):b(r))||r);return s&&r&&l(e,t,r),r};exports.PktBreadcrumbs=class extends a.PktElement{constructor(){super(...arguments),this.breadcrumbs=[]}handleLinkClick(e,t,s){const r=new CustomEvent("navigate",{detail:{item:t,index:s,originalEvent:e},bubbles:!0,composed:!0,cancelable:!0});this.dispatchEvent(r)||e.preventDefault()}render(){if(!this.breadcrumbs||this.breadcrumbs.length===0)return a.A;const e=this.breadcrumbs[this.breadcrumbs.length-2];return a.b`
2
+ <nav class="pkt-breadcrumbs" aria-label="brødsmulemeny">
3
+ <ol class="pkt-breadcrumbs__list pkt-breadcrumbs--desktop">
4
+ ${this.breadcrumbs.map((t,s)=>s===this.breadcrumbs.length-1?a.b`
5
+ <li class="pkt-breadcrumbs__item">
6
+ <span class="pkt-breadcrumbs__label" aria-current="true">
7
+ <span class="pkt-breadcrumbs__text">${t.text}</span>
8
+ </span>
9
+ </li>
10
+ `:a.b`
11
+ <li class="pkt-breadcrumbs__item">
12
+ <a
13
+ class="pkt-link pkt-link--icon-right pkt-breadcrumbs__label pkt-breadcrumbs__link"
14
+ href=${t.href}
15
+ @click=${r=>this.handleLinkClick(r,t,s)}
16
+ >
17
+ <pkt-icon
18
+ class="pkt-icon pkt-breadcrumbs__icon pkt-link__icon"
19
+ name="chevron-thin-right"
20
+ aria-hidden="true"
21
+ ></pkt-icon>
22
+ <span class="pkt-breadcrumbs__text">${t.text}</span>
23
+ </a>
24
+ </li>
25
+ `)}
26
+ </ol>
27
+
28
+ ${e?a.b`
29
+ <a
30
+ class="pkt-link pkt-link--icon-left pkt-breadcrumbs--mobile"
31
+ href=${e.href}
32
+ @click=${t=>this.handleLinkClick(t,e,this.breadcrumbs.length-2)}
33
+ >
34
+ <pkt-icon
35
+ class="pkt-back-link__icon pkt-icon pkt-link__icon"
36
+ name="chevron-thin-left"
37
+ aria-hidden="true"
38
+ ></pkt-icon>
39
+ <span class="pkt-breadcrumbs__text">${e.text}</span>
40
+ </a>
41
+ `:a.A}
42
+ </nav>
43
+ `}};i([a.n({type:Array})],exports.PktBreadcrumbs.prototype,"breadcrumbs",2);exports.PktBreadcrumbs=i([a.t("pkt-breadcrumbs")],exports.PktBreadcrumbs);
@@ -0,0 +1,79 @@
1
+ import { P as k, A as l, b as c, n as u, t as d } from "./element-CV9utnHJ.js";
2
+ import "./icon-D0IQAVwS.js";
3
+ var m = Object.defineProperty, o = Object.getOwnPropertyDescriptor, p = (e, t, r, s) => {
4
+ for (var a = s > 1 ? void 0 : s ? o(t, r) : t, n = e.length - 1, i; n >= 0; n--)
5
+ (i = e[n]) && (a = (s ? i(t, r, a) : i(a)) || a);
6
+ return s && a && m(t, r, a), a;
7
+ };
8
+ let b = class extends k {
9
+ constructor() {
10
+ super(...arguments), this.breadcrumbs = [];
11
+ }
12
+ handleLinkClick(e, t, r) {
13
+ const s = new CustomEvent("navigate", {
14
+ detail: { item: t, index: r, originalEvent: e },
15
+ bubbles: !0,
16
+ composed: !0,
17
+ cancelable: !0
18
+ });
19
+ this.dispatchEvent(s) || e.preventDefault();
20
+ }
21
+ render() {
22
+ if (!this.breadcrumbs || this.breadcrumbs.length === 0) return l;
23
+ const e = this.breadcrumbs[this.breadcrumbs.length - 2];
24
+ return c`
25
+ <nav class="pkt-breadcrumbs" aria-label="brødsmulemeny">
26
+ <ol class="pkt-breadcrumbs__list pkt-breadcrumbs--desktop">
27
+ ${this.breadcrumbs.map(
28
+ (t, r) => r === this.breadcrumbs.length - 1 ? c`
29
+ <li class="pkt-breadcrumbs__item">
30
+ <span class="pkt-breadcrumbs__label" aria-current="true">
31
+ <span class="pkt-breadcrumbs__text">${t.text}</span>
32
+ </span>
33
+ </li>
34
+ ` : c`
35
+ <li class="pkt-breadcrumbs__item">
36
+ <a
37
+ class="pkt-link pkt-link--icon-right pkt-breadcrumbs__label pkt-breadcrumbs__link"
38
+ href=${t.href}
39
+ @click=${(s) => this.handleLinkClick(s, t, r)}
40
+ >
41
+ <pkt-icon
42
+ class="pkt-icon pkt-breadcrumbs__icon pkt-link__icon"
43
+ name="chevron-thin-right"
44
+ aria-hidden="true"
45
+ ></pkt-icon>
46
+ <span class="pkt-breadcrumbs__text">${t.text}</span>
47
+ </a>
48
+ </li>
49
+ `
50
+ )}
51
+ </ol>
52
+
53
+ ${e ? c`
54
+ <a
55
+ class="pkt-link pkt-link--icon-left pkt-breadcrumbs--mobile"
56
+ href=${e.href}
57
+ @click=${(t) => this.handleLinkClick(t, e, this.breadcrumbs.length - 2)}
58
+ >
59
+ <pkt-icon
60
+ class="pkt-back-link__icon pkt-icon pkt-link__icon"
61
+ name="chevron-thin-left"
62
+ aria-hidden="true"
63
+ ></pkt-icon>
64
+ <span class="pkt-breadcrumbs__text">${e.text}</span>
65
+ </a>
66
+ ` : l}
67
+ </nav>
68
+ `;
69
+ }
70
+ };
71
+ p([
72
+ u({ type: Array })
73
+ ], b.prototype, "breadcrumbs", 2);
74
+ b = p([
75
+ d("pkt-breadcrumbs")
76
+ ], b);
77
+ export {
78
+ b as PktBreadcrumbs
79
+ };
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./alert-C95U_RZ0.cjs"),t=require("./accordionitem-D7sWqDM_.cjs"),a=require("./backlink-CQoNgoa-.cjs"),P=require("./button-BXN_JPaH.cjs"),i=require("./calendar-43Y_6FD8.cjs"),c=require("./card-DfHo45CG.cjs"),b=require("./combobox-TKL44xnV.cjs"),k=require("./consent-CIhFwuFO.cjs"),d=require("./checkbox-mmn3v9ry.cjs"),r=require("./datepicker-oN9L0Wnm.cjs"),e=require("./pkt-header.cjs"),l=require("./helptext-B366oeR9.cjs"),s=require("./heading-B0XRE0lq.cjs"),p=require("./icon-Dj0oZZSa.cjs"),g=require("./input-wrapper-BdFdJU-k.cjs"),m=require("./link-DLjXMpvi.cjs"),f=require("./linkcard-C20WC6Nw.cjs"),y=require("./loader-DxAGCcNF.cjs"),j=require("./messagebox-B8LW0PSQ.cjs"),O=require("./modal-mWn5pjs8.cjs"),q=require("./progressbar-CC9Qr82_.cjs"),n=require("./radiobutton-BUDQYAH-.cjs"),x=require("./tag-DeE1OpmF.cjs"),o=require("./tabitem-Jenus4Qd.cjs"),T=require("./textarea-DtiYQWjy.cjs"),C=require("./textinput-cKrjvbJW.cjs"),H=require("./select-PuEeK4jQ.cjs");Object.defineProperty(exports,"PktAlert",{enumerable:!0,get:()=>u.PktAlert});exports.PktAccordion=t.PktAccordion;exports.PktAccordionItem=t.PktAccordionItem_default;Object.defineProperty(exports,"PktBackLink",{enumerable:!0,get:()=>a.PktBackLink});Object.defineProperty(exports,"PktButton",{enumerable:!0,get:()=>P.PktButton});Object.defineProperty(exports,"PktCalendar",{enumerable:!0,get:()=>i.PktCalendar});Object.defineProperty(exports,"PktCard",{enumerable:!0,get:()=>c.PktCard});Object.defineProperty(exports,"PktCombobox",{enumerable:!0,get:()=>b.PktCombobox});Object.defineProperty(exports,"PktConsent",{enumerable:!0,get:()=>k.PktConsent});Object.defineProperty(exports,"PktCheckbox",{enumerable:!0,get:()=>d.PktCheckbox});Object.defineProperty(exports,"PktDateTags",{enumerable:!0,get:()=>r.PktDateTags});Object.defineProperty(exports,"PktDatepicker",{enumerable:!0,get:()=>r.PktDatepicker});Object.defineProperty(exports,"PktHeader",{enumerable:!0,get:()=>e.PktHeader});Object.defineProperty(exports,"PktHeaderService",{enumerable:!0,get:()=>e.PktHeaderService});Object.defineProperty(exports,"PktHeaderUserMenu",{enumerable:!0,get:()=>e.PktHeaderUserMenu});Object.defineProperty(exports,"PktHelptext",{enumerable:!0,get:()=>l.PktHelptext});Object.defineProperty(exports,"PktHeading",{enumerable:!0,get:()=>s.PktHeading});Object.defineProperty(exports,"PktIcon",{enumerable:!0,get:()=>p.PktIcon});Object.defineProperty(exports,"PktInputWrapper",{enumerable:!0,get:()=>g.PktInputWrapper});Object.defineProperty(exports,"PktLink",{enumerable:!0,get:()=>m.PktLink});Object.defineProperty(exports,"PktLinkCard",{enumerable:!0,get:()=>f.PktLinkCard});Object.defineProperty(exports,"PktLoader",{enumerable:!0,get:()=>y.PktLoader});Object.defineProperty(exports,"PktMessagebox",{enumerable:!0,get:()=>j.PktMessagebox});Object.defineProperty(exports,"PktModal",{enumerable:!0,get:()=>O.PktModal});Object.defineProperty(exports,"PktProgressbar",{enumerable:!0,get:()=>q.PktProgressbar});Object.defineProperty(exports,"PktRadioButton",{enumerable:!0,get:()=>n.PktRadioButton});Object.defineProperty(exports,"PktRadiobutton",{enumerable:!0,get:()=>n.PktRadioButton});Object.defineProperty(exports,"PktTag",{enumerable:!0,get:()=>x.PktTag});exports.PktTabItem=o.PktTabItem_default;exports.PktTabs=o.PktTabs;Object.defineProperty(exports,"PktTextarea",{enumerable:!0,get:()=>T.PktTextarea});Object.defineProperty(exports,"PktTextinput",{enumerable:!0,get:()=>C.PktTextinput});Object.defineProperty(exports,"PktSelect",{enumerable:!0,get:()=>H.PktSelect});
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./alert-C95U_RZ0.cjs"),t=require("./accordionitem-D7sWqDM_.cjs"),a=require("./backlink-CQoNgoa-.cjs"),P=require("./pkt-breadcrumbs.cjs"),c=require("./button-BXN_JPaH.cjs"),i=require("./calendar-43Y_6FD8.cjs"),b=require("./card-DfHo45CG.cjs"),k=require("./combobox-TKL44xnV.cjs"),d=require("./consent-CIhFwuFO.cjs"),s=require("./checkbox-mmn3v9ry.cjs"),r=require("./datepicker-oN9L0Wnm.cjs"),e=require("./pkt-header.cjs"),l=require("./helptext-B366oeR9.cjs"),p=require("./heading-B0XRE0lq.cjs"),g=require("./icon-Dj0oZZSa.cjs"),m=require("./input-wrapper-BdFdJU-k.cjs"),f=require("./link-DLjXMpvi.cjs"),y=require("./linkcard-C20WC6Nw.cjs"),j=require("./loader-DxAGCcNF.cjs"),O=require("./messagebox-B8LW0PSQ.cjs"),q=require("./modal-mWn5pjs8.cjs"),x=require("./progressbar-CC9Qr82_.cjs"),n=require("./radiobutton-BUDQYAH-.cjs"),T=require("./tag-DeE1OpmF.cjs"),o=require("./tabitem-Jenus4Qd.cjs"),C=require("./textarea-DtiYQWjy.cjs"),H=require("./textinput-cKrjvbJW.cjs"),B=require("./select-PuEeK4jQ.cjs");Object.defineProperty(exports,"PktAlert",{enumerable:!0,get:()=>u.PktAlert});exports.PktAccordion=t.PktAccordion;exports.PktAccordionItem=t.PktAccordionItem_default;Object.defineProperty(exports,"PktBackLink",{enumerable:!0,get:()=>a.PktBackLink});Object.defineProperty(exports,"PktBreadcrumbs",{enumerable:!0,get:()=>P.PktBreadcrumbs});Object.defineProperty(exports,"PktButton",{enumerable:!0,get:()=>c.PktButton});Object.defineProperty(exports,"PktCalendar",{enumerable:!0,get:()=>i.PktCalendar});Object.defineProperty(exports,"PktCard",{enumerable:!0,get:()=>b.PktCard});Object.defineProperty(exports,"PktCombobox",{enumerable:!0,get:()=>k.PktCombobox});Object.defineProperty(exports,"PktConsent",{enumerable:!0,get:()=>d.PktConsent});Object.defineProperty(exports,"PktCheckbox",{enumerable:!0,get:()=>s.PktCheckbox});Object.defineProperty(exports,"PktDateTags",{enumerable:!0,get:()=>r.PktDateTags});Object.defineProperty(exports,"PktDatepicker",{enumerable:!0,get:()=>r.PktDatepicker});Object.defineProperty(exports,"PktHeader",{enumerable:!0,get:()=>e.PktHeader});Object.defineProperty(exports,"PktHeaderService",{enumerable:!0,get:()=>e.PktHeaderService});Object.defineProperty(exports,"PktHeaderUserMenu",{enumerable:!0,get:()=>e.PktHeaderUserMenu});Object.defineProperty(exports,"PktHelptext",{enumerable:!0,get:()=>l.PktHelptext});Object.defineProperty(exports,"PktHeading",{enumerable:!0,get:()=>p.PktHeading});Object.defineProperty(exports,"PktIcon",{enumerable:!0,get:()=>g.PktIcon});Object.defineProperty(exports,"PktInputWrapper",{enumerable:!0,get:()=>m.PktInputWrapper});Object.defineProperty(exports,"PktLink",{enumerable:!0,get:()=>f.PktLink});Object.defineProperty(exports,"PktLinkCard",{enumerable:!0,get:()=>y.PktLinkCard});Object.defineProperty(exports,"PktLoader",{enumerable:!0,get:()=>j.PktLoader});Object.defineProperty(exports,"PktMessagebox",{enumerable:!0,get:()=>O.PktMessagebox});Object.defineProperty(exports,"PktModal",{enumerable:!0,get:()=>q.PktModal});Object.defineProperty(exports,"PktProgressbar",{enumerable:!0,get:()=>x.PktProgressbar});Object.defineProperty(exports,"PktRadioButton",{enumerable:!0,get:()=>n.PktRadioButton});Object.defineProperty(exports,"PktRadiobutton",{enumerable:!0,get:()=>n.PktRadioButton});Object.defineProperty(exports,"PktTag",{enumerable:!0,get:()=>T.PktTag});exports.PktTabItem=o.PktTabItem_default;exports.PktTabs=o.PktTabs;Object.defineProperty(exports,"PktTextarea",{enumerable:!0,get:()=>C.PktTextarea});Object.defineProperty(exports,"PktTextinput",{enumerable:!0,get:()=>H.PktTextinput});Object.defineProperty(exports,"PktSelect",{enumerable:!0,get:()=>B.PktSelect});
package/dist/pkt-index.js CHANGED
@@ -1,62 +1,64 @@
1
- import { P as r } from "./alert-ca_hFi-u.js";
1
+ import { P as o } from "./alert-ca_hFi-u.js";
2
2
  import { P as e, a } from "./accordionitem-Cj74XruR.js";
3
3
  import { P as s } from "./backlink-C68H4Z3b.js";
4
- import { P as x } from "./button-BRovt5nm.js";
5
- import { P as f } from "./calendar-B5Xwl8mV.js";
6
- import { P as d } from "./card-DvgCTN4B.js";
7
- import { P as c } from "./combobox-CwW0md6G.js";
8
- import { P as u } from "./consent-CgS1L1xF.js";
9
- import { P as T } from "./checkbox-CbIUMIYt.js";
10
- import { P as l, a as H } from "./datepicker-qRWWzmbo.js";
11
- import { PktHeader as L, PktHeaderService as A, PktHeaderUserMenu as B } from "./pkt-header.js";
12
- import { P as D } from "./helptext-DepcDYFV.js";
13
- import { P as S } from "./heading-D9R5UBcv.js";
14
- import { P as v } from "./icon-D0IQAVwS.js";
15
- import { P as W } from "./input-wrapper-B__6jW4U.js";
16
- import { P as q } from "./link-oGYvBLYL.js";
17
- import { P as y } from "./linkcard-BhVIc18n.js";
18
- import { P as E } from "./loader-BHt9i5Xp.js";
19
- import { P as G } from "./messagebox-DkspcMQg.js";
20
- import { P as K } from "./modal-D3u6ueP5.js";
21
- import { P as O } from "./progressbar-CcX-xiPI.js";
22
- import { P as V, P as X } from "./radiobutton-RW3h0tJj.js";
23
- import { P as Z } from "./tag-CIWBLpjv.js";
24
- import { P as $, a as tt } from "./tabitem-DZSzwag2.js";
25
- import { P as rt } from "./textarea-DR9lVDN-.js";
26
- import { P as et } from "./textinput-0Bttvt9V.js";
27
- import { P as kt } from "./select-iXKeM3yz.js";
4
+ import { PktBreadcrumbs as x } from "./pkt-breadcrumbs.js";
5
+ import { P as f } from "./button-BRovt5nm.js";
6
+ import { P as d } from "./calendar-B5Xwl8mV.js";
7
+ import { P as i } from "./card-DvgCTN4B.js";
8
+ import { P as u } from "./combobox-CwW0md6G.js";
9
+ import { P as T } from "./consent-CgS1L1xF.js";
10
+ import { P as l } from "./checkbox-CbIUMIYt.js";
11
+ import { P as B, a as I } from "./datepicker-qRWWzmbo.js";
12
+ import { PktHeader as A, PktHeaderService as M, PktHeaderUserMenu as D } from "./pkt-header.js";
13
+ import { P as S } from "./helptext-DepcDYFV.js";
14
+ import { P as v } from "./heading-D9R5UBcv.js";
15
+ import { P as W } from "./icon-D0IQAVwS.js";
16
+ import { P as q } from "./input-wrapper-B__6jW4U.js";
17
+ import { P as y } from "./link-oGYvBLYL.js";
18
+ import { P as E } from "./linkcard-BhVIc18n.js";
19
+ import { P as G } from "./loader-BHt9i5Xp.js";
20
+ import { P as K } from "./messagebox-DkspcMQg.js";
21
+ import { P as O } from "./modal-D3u6ueP5.js";
22
+ import { P as V } from "./progressbar-CcX-xiPI.js";
23
+ import { P as Y, P as Z } from "./radiobutton-RW3h0tJj.js";
24
+ import { P as $ } from "./tag-CIWBLpjv.js";
25
+ import { P as rt, a as ot } from "./tabitem-DZSzwag2.js";
26
+ import { P as et } from "./textarea-DR9lVDN-.js";
27
+ import { P as kt } from "./textinput-0Bttvt9V.js";
28
+ import { P as pt } from "./select-iXKeM3yz.js";
28
29
  export {
29
30
  e as PktAccordion,
30
31
  a as PktAccordionItem,
31
- r as PktAlert,
32
+ o as PktAlert,
32
33
  s as PktBackLink,
33
- x as PktButton,
34
- f as PktCalendar,
35
- d as PktCard,
36
- T as PktCheckbox,
37
- c as PktCombobox,
38
- u as PktConsent,
39
- l as PktDateTags,
40
- H as PktDatepicker,
41
- L as PktHeader,
42
- A as PktHeaderService,
43
- B as PktHeaderUserMenu,
44
- S as PktHeading,
45
- D as PktHelptext,
46
- v as PktIcon,
47
- W as PktInputWrapper,
48
- q as PktLink,
49
- y as PktLinkCard,
50
- E as PktLoader,
51
- G as PktMessagebox,
52
- K as PktModal,
53
- O as PktProgressbar,
54
- V as PktRadioButton,
55
- X as PktRadiobutton,
56
- kt as PktSelect,
57
- $ as PktTabItem,
58
- tt as PktTabs,
59
- Z as PktTag,
60
- rt as PktTextarea,
61
- et as PktTextinput
34
+ x as PktBreadcrumbs,
35
+ f as PktButton,
36
+ d as PktCalendar,
37
+ i as PktCard,
38
+ l as PktCheckbox,
39
+ u as PktCombobox,
40
+ T as PktConsent,
41
+ B as PktDateTags,
42
+ I as PktDatepicker,
43
+ A as PktHeader,
44
+ M as PktHeaderService,
45
+ D as PktHeaderUserMenu,
46
+ v as PktHeading,
47
+ S as PktHelptext,
48
+ W as PktIcon,
49
+ q as PktInputWrapper,
50
+ y as PktLink,
51
+ E as PktLinkCard,
52
+ G as PktLoader,
53
+ K as PktMessagebox,
54
+ O as PktModal,
55
+ V as PktProgressbar,
56
+ Y as PktRadioButton,
57
+ Z as PktRadiobutton,
58
+ pt as PktSelect,
59
+ rt as PktTabItem,
60
+ ot as PktTabs,
61
+ $ as PktTag,
62
+ et as PktTextarea,
63
+ kt as PktTextinput
62
64
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-elements",
3
- "version": "16.3.0",
3
+ "version": "16.5.0",
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",
@@ -42,7 +42,7 @@
42
42
  "@babel/preset-env": "^7.28.3",
43
43
  "@babel/preset-typescript": "^7.25.9",
44
44
  "@oslokommune/punkt-assets": "^16.0.0",
45
- "@oslokommune/punkt-css": "^16.3.0",
45
+ "@oslokommune/punkt-css": "^16.4.0",
46
46
  "@testing-library/jest-dom": "^6.6.3",
47
47
  "@typescript-eslint/eslint-plugin": "^8.46.0",
48
48
  "@typescript-eslint/parser": "^8.46.0",
@@ -79,5 +79,5 @@
79
79
  "url": "https://github.com/oslokommune/punkt/issues"
80
80
  },
81
81
  "license": "MIT",
82
- "gitHead": "3561a39e6329e3fc85d558d89cf20b58aa410d24"
82
+ "gitHead": "81940eb91344498b82fc74316684b452f9fcc82c"
83
83
  }
@@ -0,0 +1,208 @@
1
+ import '@testing-library/jest-dom'
2
+ import { axe, toHaveNoViolations } from 'jest-axe'
3
+ import { fireEvent } from '@testing-library/dom'
4
+ import { vi } from 'vitest'
5
+
6
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
7
+ import type { CustomElementFor } from '../../tests/component-registry'
8
+ import './breadcrumbs'
9
+
10
+ expect.extend(toHaveNoViolations)
11
+
12
+ interface BreadcrumbsTestConfig extends BaseTestConfig {
13
+ breadcrumbs?: { text: string; href: string }[]
14
+ }
15
+
16
+ const defaultBreadcrumbs = [
17
+ { text: 'Hjem', href: '/' },
18
+ { text: 'Produkter', href: '/produkter' },
19
+ { text: 'Detaljer', href: '/produkter/detaljer' },
20
+ ]
21
+
22
+ const createBreadcrumbsTest = async (config: BreadcrumbsTestConfig = {}) => {
23
+ const { container, element } = await createElementTest<
24
+ CustomElementFor<'pkt-breadcrumbs'>,
25
+ BreadcrumbsTestConfig
26
+ >('pkt-breadcrumbs', config)
27
+
28
+ if (config.breadcrumbs) {
29
+ element.breadcrumbs = config.breadcrumbs
30
+ await element.updateComplete
31
+ }
32
+
33
+ return { container, breadcrumbs: element }
34
+ }
35
+
36
+ afterEach(() => {
37
+ document.body.innerHTML = ''
38
+ })
39
+
40
+ describe('PktBreadcrumbs', () => {
41
+ describe('Rendering', () => {
42
+ test('renders without errors', async () => {
43
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
44
+ expect(breadcrumbs).toBeInTheDocument()
45
+ })
46
+
47
+ test('renders all breadcrumb items', async () => {
48
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
49
+ const items = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__item')
50
+ expect(items).toHaveLength(3)
51
+ })
52
+
53
+ test('renders nothing when breadcrumbs array is empty', async () => {
54
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: [] })
55
+ const nav = breadcrumbs.querySelector('nav')
56
+ expect(nav).not.toBeInTheDocument()
57
+ })
58
+
59
+ test('renders correctly when breadcrumbs are set as JSON attribute', async () => {
60
+ const container = document.createElement('div')
61
+ document.body.appendChild(container)
62
+ container.innerHTML = `<pkt-breadcrumbs breadcrumbs='${JSON.stringify(defaultBreadcrumbs)}'></pkt-breadcrumbs>`
63
+
64
+ const element = container.querySelector('pkt-breadcrumbs') as CustomElementFor<'pkt-breadcrumbs'>
65
+ await customElements.whenDefined('pkt-breadcrumbs')
66
+ await element.updateComplete
67
+
68
+ const items = element.querySelectorAll('.pkt-breadcrumbs__item')
69
+ expect(items).toHaveLength(3)
70
+
71
+ const links = element.querySelectorAll('.pkt-breadcrumbs__link')
72
+ expect(links).toHaveLength(2)
73
+ expect(links[0]).toHaveAttribute('href', '/')
74
+ })
75
+
76
+ test('last item has aria-current and no link', async () => {
77
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
78
+ const items = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__item')
79
+ const lastItem = items[items.length - 1]
80
+
81
+ const span = lastItem.querySelector('[aria-current="true"]')
82
+ expect(span).toBeInTheDocument()
83
+
84
+ const link = lastItem.querySelector('a')
85
+ expect(link).not.toBeInTheDocument()
86
+ })
87
+
88
+ test('non-last items render as links with correct href', async () => {
89
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
90
+ const links = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__link')
91
+
92
+ expect(links).toHaveLength(2)
93
+ expect(links[0]).toHaveAttribute('href', '/')
94
+ expect(links[1]).toHaveAttribute('href', '/produkter')
95
+ })
96
+
97
+ test('renders breadcrumb text correctly', async () => {
98
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
99
+ const texts = breadcrumbs.querySelectorAll('.pkt-breadcrumbs__text')
100
+
101
+ // 3 desktop + 1 mobile back-link
102
+ expect(texts).toHaveLength(4)
103
+ expect(texts[0]).toHaveTextContent('Hjem')
104
+ expect(texts[1]).toHaveTextContent('Produkter')
105
+ expect(texts[2]).toHaveTextContent('Detaljer')
106
+ })
107
+
108
+ test('renders desktop list with correct classes', async () => {
109
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
110
+ const list = breadcrumbs.querySelector('.pkt-breadcrumbs--desktop')
111
+ expect(list).toBeInTheDocument()
112
+ })
113
+
114
+ test('renders mobile back-link pointing to second-to-last item', async () => {
115
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
116
+ const mobileLink = breadcrumbs.querySelector('.pkt-breadcrumbs--mobile')
117
+
118
+ expect(mobileLink).toBeInTheDocument()
119
+ expect(mobileLink).toHaveAttribute('href', '/produkter')
120
+ expect(mobileLink).toHaveTextContent('Produkter')
121
+ })
122
+
123
+ test('renders chevron-right icons on desktop links', async () => {
124
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
125
+ const desktopIcons = breadcrumbs.querySelectorAll(
126
+ '.pkt-breadcrumbs--desktop pkt-icon[name="chevron-thin-right"]',
127
+ )
128
+ expect(desktopIcons).toHaveLength(2)
129
+ })
130
+
131
+ test('renders chevron-left icon on mobile back-link', async () => {
132
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
133
+ const mobileIcon = breadcrumbs.querySelector(
134
+ '.pkt-breadcrumbs--mobile pkt-icon[name="chevron-thin-left"]',
135
+ )
136
+ expect(mobileIcon).toBeInTheDocument()
137
+ })
138
+ })
139
+
140
+ describe('Events', () => {
141
+ test('dispatches navigate event on link click', async () => {
142
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
143
+ const navigateSpy = vi.fn()
144
+ breadcrumbs.addEventListener('navigate', navigateSpy)
145
+
146
+ const link = breadcrumbs.querySelector('.pkt-breadcrumbs__link') as HTMLAnchorElement
147
+ fireEvent.click(link)
148
+
149
+ expect(navigateSpy).toHaveBeenCalledTimes(1)
150
+ expect(navigateSpy.mock.calls[0][0].detail.item).toEqual({ text: 'Hjem', href: '/' })
151
+ expect(navigateSpy.mock.calls[0][0].detail.index).toBe(0)
152
+ })
153
+
154
+ test('dispatches navigate event on mobile back-link click', async () => {
155
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
156
+ const navigateSpy = vi.fn()
157
+ breadcrumbs.addEventListener('navigate', navigateSpy)
158
+
159
+ const mobileLink = breadcrumbs.querySelector('.pkt-breadcrumbs--mobile') as HTMLAnchorElement
160
+ fireEvent.click(mobileLink)
161
+
162
+ expect(navigateSpy).toHaveBeenCalledTimes(1)
163
+ expect(navigateSpy.mock.calls[0][0].detail.item).toEqual({
164
+ text: 'Produkter',
165
+ href: '/produkter',
166
+ })
167
+ expect(navigateSpy.mock.calls[0][0].detail.index).toBe(1)
168
+ })
169
+
170
+ test('preventDefault on navigate event prevents default link behavior', async () => {
171
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
172
+ breadcrumbs.addEventListener('navigate', (e: Event) => {
173
+ e.preventDefault()
174
+ })
175
+
176
+ const link = breadcrumbs.querySelector('.pkt-breadcrumbs__link') as HTMLAnchorElement
177
+ const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
178
+ const preventDefaultSpy = vi.spyOn(clickEvent, 'preventDefault')
179
+
180
+ link.dispatchEvent(clickEvent)
181
+
182
+ expect(preventDefaultSpy).toHaveBeenCalled()
183
+ })
184
+ })
185
+
186
+ describe('Accessibility', () => {
187
+ test('has nav with aria-label', async () => {
188
+ const { breadcrumbs } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
189
+ const nav = breadcrumbs.querySelector('nav')
190
+ expect(nav).toHaveAttribute('aria-label', 'brødsmulemeny')
191
+ })
192
+
193
+ test('has no WCAG violations', async () => {
194
+ // Suppress jsdom "Not implemented: navigation" error triggered by axe-core
195
+ const originalError = console.error
196
+ console.error = (...args: unknown[]) => {
197
+ if (typeof args[0] === 'string' && args[0].includes('Not implemented: navigation')) return
198
+ originalError(...args)
199
+ }
200
+
201
+ const { container } = await createBreadcrumbsTest({ breadcrumbs: defaultBreadcrumbs })
202
+ const results = await axe(container)
203
+ expect(results).toHaveNoViolations()
204
+
205
+ console.error = originalError
206
+ })
207
+ })
208
+ })
@@ -0,0 +1,95 @@
1
+ import { customElement, property } from 'lit/decorators.js'
2
+ import { html, nothing } from 'lit'
3
+ import { PktElement } from '@/base-elements/element'
4
+
5
+ import '@/components/icon'
6
+
7
+ export interface IBreadcrumbItem {
8
+ text: string
9
+ href: string
10
+ }
11
+
12
+ export interface IPktBreadcrumbs {
13
+ breadcrumbs: IBreadcrumbItem[]
14
+ }
15
+
16
+ @customElement('pkt-breadcrumbs')
17
+ export class PktBreadcrumbs extends PktElement implements IPktBreadcrumbs {
18
+ @property({ type: Array }) breadcrumbs: IBreadcrumbItem[] = []
19
+
20
+ private handleLinkClick(event: MouseEvent, item: IBreadcrumbItem, index: number) {
21
+ const navigateEvent = new CustomEvent('navigate', {
22
+ detail: { item, index, originalEvent: event },
23
+ bubbles: true,
24
+ composed: true,
25
+ cancelable: true,
26
+ })
27
+
28
+ const dispatched = this.dispatchEvent(navigateEvent)
29
+
30
+ // dispatchEvent returnerer false når en lytter kaller preventDefault() på navigate-eventet.
31
+ // Vi viderefører dette til den opprinnelige klikk-eventen for å hindre nettleseren i å navigere.
32
+ if (!dispatched) {
33
+ event.preventDefault()
34
+ }
35
+ }
36
+
37
+ render() {
38
+ if (!this.breadcrumbs || this.breadcrumbs.length === 0) return nothing
39
+
40
+ const backLink = this.breadcrumbs[this.breadcrumbs.length - 2]
41
+
42
+ return html`
43
+ <nav class="pkt-breadcrumbs" aria-label="brødsmulemeny">
44
+ <ol class="pkt-breadcrumbs__list pkt-breadcrumbs--desktop">
45
+ ${this.breadcrumbs.map((item, index) =>
46
+ index === this.breadcrumbs.length - 1
47
+ ? html`
48
+ <li class="pkt-breadcrumbs__item">
49
+ <span class="pkt-breadcrumbs__label" aria-current="true">
50
+ <span class="pkt-breadcrumbs__text">${item.text}</span>
51
+ </span>
52
+ </li>
53
+ `
54
+ : html`
55
+ <li class="pkt-breadcrumbs__item">
56
+ <a
57
+ class="pkt-link pkt-link--icon-right pkt-breadcrumbs__label pkt-breadcrumbs__link"
58
+ href=${item.href}
59
+ @click=${(e: MouseEvent) => this.handleLinkClick(e, item, index)}
60
+ >
61
+ <pkt-icon
62
+ class="pkt-icon pkt-breadcrumbs__icon pkt-link__icon"
63
+ name="chevron-thin-right"
64
+ aria-hidden="true"
65
+ ></pkt-icon>
66
+ <span class="pkt-breadcrumbs__text">${item.text}</span>
67
+ </a>
68
+ </li>
69
+ `,
70
+ )}
71
+ </ol>
72
+
73
+ ${backLink
74
+ ? html`
75
+ <a
76
+ class="pkt-link pkt-link--icon-left pkt-breadcrumbs--mobile"
77
+ href=${backLink.href}
78
+ @click=${(e: MouseEvent) =>
79
+ this.handleLinkClick(e, backLink, this.breadcrumbs.length - 2)}
80
+ >
81
+ <pkt-icon
82
+ class="pkt-back-link__icon pkt-icon pkt-link__icon"
83
+ name="chevron-thin-left"
84
+ aria-hidden="true"
85
+ ></pkt-icon>
86
+ <span class="pkt-breadcrumbs__text">${backLink.text}</span>
87
+ </a>
88
+ `
89
+ : nothing}
90
+ </nav>
91
+ `
92
+ }
93
+ }
94
+
95
+ export default PktBreadcrumbs
@@ -0,0 +1,2 @@
1
+ export { PktBreadcrumbs } from './breadcrumbs'
2
+ export type { IBreadcrumbItem, IPktBreadcrumbs } from './breadcrumbs'
@@ -2,6 +2,7 @@
2
2
  export { PktAlert } from '@/components/alert'
3
3
  export { PktAccordion, PktAccordionItem } from '@/components/accordion'
4
4
  export { PktBackLink } from '@/components/backlink'
5
+ export { PktBreadcrumbs } from '@/components/breadcrumbs'
5
6
  export { PktButton } from '@/components/button'
6
7
  export { PktCalendar } from '@/components/calendar'
7
8
  export { PktCard } from '@/components/card'
@@ -81,6 +82,8 @@ export type { TSelectOption } from '@/components/select'
81
82
 
82
83
  export type { IPktBackLink } from '@/components/backlink'
83
84
 
85
+ export type { IPktBreadcrumbs, IBreadcrumbItem } from '@/components/breadcrumbs'
86
+
84
87
  export type { IPktLoader, TPktLoaderVariant } from '@/components/loader'
85
88
 
86
89
  export type { IPktConsent } from '@/components/consent'