@oslokommune/punkt-elements 13.22.0 → 14.0.1

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.
@@ -1,5 +1,5 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const b=require("./alert-CCjoJo11.cjs"),l=require("./accordionitem-DCZrHVNR.cjs"),m=require("./backlink-BzEvli8m.cjs"),g=require("./button-abD9URn0.cjs"),P=require("./calendar-Dz1Cnzx5.cjs"),h=require("./card-BAoH1g6O.cjs"),f=require("./combobox-DYYCdlex.cjs"),y=require("./consent-B1N02CuT.cjs"),O=require("./checkbox-BP5zOlPy.cjs"),t=require("./element-CJ_QKaki.cjs"),j=require("./pkt-slot-controller-BzddBp7z.cjs"),s=require("./ref-BFa5Utho.cjs"),q=require("./class-map-C_erArZz.cjs"),k=require("./datepicker-CnCOXI2x.cjs"),x=require("./helptext-EPTR9AIl.cjs"),C=require("./heading-Dv_cH6N1.cjs"),v=require("./icon-BGuizDwk.cjs"),S=require("./input-wrapper-WEGSbIA6.cjs"),T=require("./link-Da3pZ_CW.cjs"),$=require("./linkcard-BM23gzhS.cjs"),L=require("./loader-Bo8RCbCJ.cjs"),_=require("./messagebox-C76IcXTl.cjs"),I=require("./modal-Cdz9JcCX.cjs"),A=require("./progressbar-8gzOtJyh.cjs"),p=require("./radiobutton-BuKXgQm_.cjs"),B=require("./tag-BkuJjOy7.cjs"),d=require("./tabitem-D5zyipN1.cjs"),D=require("./textarea-CcIQXCmC.cjs"),M=require("./textinput-Dn704gQw.cjs"),R=require("./select-BSnylWof.cjs");var H=Object.defineProperty,w=Object.getOwnPropertyDescriptor,o=(a,e,r,i)=>{for(var n=i>1?void 0:i?w(e,r):e,u=a.length-1,c;u>=0;u--)(c=a[u])&&(n=(i?c(e,r,n):c(n))||n);return i&&n&&H(e,r,n),n};exports.PktComponent=class extends t.PktElement{constructor(){super(),this.string="",this.strings=[],this.darkmode=!1,this._list=[],this.defaultSlot=s.e(),this.namedSlot=s.e(),this.slotController=new j.PktSlotController(this,this.defaultSlot,this.namedSlot)}connectedCallback(){this.strings.length&&this.strings.forEach(e=>{this._list.push(e.toUpperCase())}),super.connectedCallback()}render(){const e={"pkt-component":!0,"pkt-component--has-list":this.strings.length>0,"pkt-darkmode":this.darkmode};return t.x`
2
- <div class="${q.e(e)}">
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const m=require("./alert-CCjoJo11.cjs"),P=require("./accordionitem-DCZrHVNR.cjs"),g=require("./backlink-BzEvli8m.cjs"),h=require("./button-abD9URn0.cjs"),k=require("./calendar-Dz1Cnzx5.cjs"),f=require("./card-BAoH1g6O.cjs"),y=require("./combobox-DYYCdlex.cjs"),O=require("./consent-B1N02CuT.cjs"),j=require("./checkbox-BP5zOlPy.cjs"),t=require("./element-CJ_QKaki.cjs"),q=require("./pkt-slot-controller-BzddBp7z.cjs"),a=require("./ref-BFa5Utho.cjs"),x=require("./class-map-C_erArZz.cjs"),d=require("./datepicker-CnCOXI2x.cjs"),l=require("./pkt-header.cjs"),C=require("./helptext-EPTR9AIl.cjs"),v=require("./heading-Dv_cH6N1.cjs"),S=require("./icon-BGuizDwk.cjs"),T=require("./input-wrapper-WEGSbIA6.cjs"),$=require("./link-Da3pZ_CW.cjs"),H=require("./linkcard-BM23gzhS.cjs"),L=require("./loader-Bo8RCbCJ.cjs"),_=require("./messagebox-C76IcXTl.cjs"),I=require("./modal-Cdz9JcCX.cjs"),A=require("./progressbar-8gzOtJyh.cjs"),p=require("./radiobutton-BuKXgQm_.cjs"),B=require("./tag-BkuJjOy7.cjs"),b=require("./tabitem-D5zyipN1.cjs"),M=require("./textarea-CcIQXCmC.cjs"),D=require("./textinput-Dn704gQw.cjs"),R=require("./select-BSnylWof.cjs");var w=Object.defineProperty,E=Object.getOwnPropertyDescriptor,o=(s,e,r,i)=>{for(var n=i>1?void 0:i?E(e,r):e,u=s.length-1,c;u>=0;u--)(c=s[u])&&(n=(i?c(e,r,n):c(n))||n);return i&&n&&w(e,r,n),n};exports.PktComponent=class extends t.PktElement{constructor(){super(),this.string="",this.strings=[],this.darkmode=!1,this._list=[],this.defaultSlot=a.e(),this.namedSlot=a.e(),this.slotController=new q.PktSlotController(this,this.defaultSlot,this.namedSlot)}connectedCallback(){this.strings.length&&this.strings.forEach(e=>{this._list.push(e.toUpperCase())}),super.connectedCallback()}render(){const e={"pkt-component":!0,"pkt-component--has-list":this.strings.length>0,"pkt-darkmode":this.darkmode};return t.x`
2
+ <div class="${x.e(e)}">
3
3
  <h1 class="pkt-txt-28">${this.string}</h1>
4
4
 
5
5
  <h2 class="pkt-txt-22">Innhold fra attributter og funksjoner</h2>
@@ -7,11 +7,11 @@
7
7
  <div>${this.renderList(this.doStuff(this._list))}</div>
8
8
 
9
9
  <h2 class="pkt-txt-22">Slot</h2>
10
- <div ${s.n(this.defaultSlot)}>defaultSlotRef</div>
10
+ <div ${a.n(this.defaultSlot)}>defaultSlotRef</div>
11
11
  <h2 class="pkt-txt-22">Named slot</h2>
12
12
  <select
13
13
  name="named-slot"
14
- ${s.n(this.namedSlot)}
14
+ ${a.n(this.namedSlot)}
15
15
  @change=${r=>alert(r.target.value)}
16
16
  >
17
17
  namedSlotRef
@@ -26,4 +26,4 @@
26
26
  <ul>
27
27
  ${e.map(r=>t.x`<li>${r}</li>`)}
28
28
  </ul>
29
- `}doStuff(e){return e.reverse()}handleGreeting(){this.dispatchEvent(new CustomEvent("pkt-greeting",{detail:"Hei på deg!"}))}};o([t.n({type:String})],exports.PktComponent.prototype,"string",2);o([t.n({converter:P.csvToArray})],exports.PktComponent.prototype,"strings",2);o([t.n({type:Boolean})],exports.PktComponent.prototype,"darkmode",2);o([t.n({type:Array})],exports.PktComponent.prototype,"_list",2);exports.PktComponent=o([t.t("pkt-component")],exports.PktComponent);Object.defineProperty(exports,"PktAlert",{enumerable:!0,get:()=>b.PktAlert});exports.PktAccordion=l.PktAccordion;exports.PktAccordionItem=l.PktAccordionItem;Object.defineProperty(exports,"PktBackLink",{enumerable:!0,get:()=>m.PktBackLink});Object.defineProperty(exports,"PktButton",{enumerable:!0,get:()=>g.PktButton});Object.defineProperty(exports,"PktCalendar",{enumerable:!0,get:()=>P.PktCalendar});Object.defineProperty(exports,"PktCard",{enumerable:!0,get:()=>h.PktCard});Object.defineProperty(exports,"PktCombobox",{enumerable:!0,get:()=>f.PktCombobox});Object.defineProperty(exports,"PktConsent",{enumerable:!0,get:()=>y.PktConsent});Object.defineProperty(exports,"PktCheckbox",{enumerable:!0,get:()=>O.PktCheckbox});Object.defineProperty(exports,"PktDateTags",{enumerable:!0,get:()=>k.PktDateTags});Object.defineProperty(exports,"PktDatepicker",{enumerable:!0,get:()=>k.PktDatepicker});Object.defineProperty(exports,"PktHelptext",{enumerable:!0,get:()=>x.PktHelptext});Object.defineProperty(exports,"PktHeading",{enumerable:!0,get:()=>C.PktHeading});Object.defineProperty(exports,"PktIcon",{enumerable:!0,get:()=>v.PktIcon});Object.defineProperty(exports,"PktInputWrapper",{enumerable:!0,get:()=>S.PktInputWrapper});Object.defineProperty(exports,"PktLink",{enumerable:!0,get:()=>T.PktLink});Object.defineProperty(exports,"PktLinkCard",{enumerable:!0,get:()=>$.PktLinkCard});Object.defineProperty(exports,"PktLoader",{enumerable:!0,get:()=>L.PktLoader});Object.defineProperty(exports,"PktMessagebox",{enumerable:!0,get:()=>_.PktMessagebox});Object.defineProperty(exports,"PktModal",{enumerable:!0,get:()=>I.PktModal});Object.defineProperty(exports,"PktProgressbar",{enumerable:!0,get:()=>A.PktProgressbar});Object.defineProperty(exports,"PktRadioButton",{enumerable:!0,get:()=>p.PktRadioButton});Object.defineProperty(exports,"PktRadiobutton",{enumerable:!0,get:()=>p.PktRadioButton});Object.defineProperty(exports,"PktTag",{enumerable:!0,get:()=>B.PktTag});exports.PktTabItem=d.PktTabItem;exports.PktTabs=d.PktTabs;Object.defineProperty(exports,"PktTextarea",{enumerable:!0,get:()=>D.PktTextarea});Object.defineProperty(exports,"PktTextinput",{enumerable:!0,get:()=>M.PktTextinput});Object.defineProperty(exports,"PktSelect",{enumerable:!0,get:()=>R.PktSelect});
29
+ `}doStuff(e){return e.reverse()}handleGreeting(){this.dispatchEvent(new CustomEvent("pkt-greeting",{detail:"Hei på deg!"}))}};o([t.n({type:String})],exports.PktComponent.prototype,"string",2);o([t.n({converter:k.csvToArray})],exports.PktComponent.prototype,"strings",2);o([t.n({type:Boolean})],exports.PktComponent.prototype,"darkmode",2);o([t.n({type:Array})],exports.PktComponent.prototype,"_list",2);exports.PktComponent=o([t.t("pkt-component")],exports.PktComponent);Object.defineProperty(exports,"PktAlert",{enumerable:!0,get:()=>m.PktAlert});exports.PktAccordion=P.PktAccordion;exports.PktAccordionItem=P.PktAccordionItem;Object.defineProperty(exports,"PktBackLink",{enumerable:!0,get:()=>g.PktBackLink});Object.defineProperty(exports,"PktButton",{enumerable:!0,get:()=>h.PktButton});Object.defineProperty(exports,"PktCalendar",{enumerable:!0,get:()=>k.PktCalendar});Object.defineProperty(exports,"PktCard",{enumerable:!0,get:()=>f.PktCard});Object.defineProperty(exports,"PktCombobox",{enumerable:!0,get:()=>y.PktCombobox});Object.defineProperty(exports,"PktConsent",{enumerable:!0,get:()=>O.PktConsent});Object.defineProperty(exports,"PktCheckbox",{enumerable:!0,get:()=>j.PktCheckbox});Object.defineProperty(exports,"PktDateTags",{enumerable:!0,get:()=>d.PktDateTags});Object.defineProperty(exports,"PktDatepicker",{enumerable:!0,get:()=>d.PktDatepicker});Object.defineProperty(exports,"PktHeader",{enumerable:!0,get:()=>l.PktHeader});Object.defineProperty(exports,"PktHeaderService",{enumerable:!0,get:()=>l.PktHeaderService});Object.defineProperty(exports,"PktHeaderUserMenu",{enumerable:!0,get:()=>l.PktHeaderUserMenu});Object.defineProperty(exports,"PktHelptext",{enumerable:!0,get:()=>C.PktHelptext});Object.defineProperty(exports,"PktHeading",{enumerable:!0,get:()=>v.PktHeading});Object.defineProperty(exports,"PktIcon",{enumerable:!0,get:()=>S.PktIcon});Object.defineProperty(exports,"PktInputWrapper",{enumerable:!0,get:()=>T.PktInputWrapper});Object.defineProperty(exports,"PktLink",{enumerable:!0,get:()=>$.PktLink});Object.defineProperty(exports,"PktLinkCard",{enumerable:!0,get:()=>H.PktLinkCard});Object.defineProperty(exports,"PktLoader",{enumerable:!0,get:()=>L.PktLoader});Object.defineProperty(exports,"PktMessagebox",{enumerable:!0,get:()=>_.PktMessagebox});Object.defineProperty(exports,"PktModal",{enumerable:!0,get:()=>I.PktModal});Object.defineProperty(exports,"PktProgressbar",{enumerable:!0,get:()=>A.PktProgressbar});Object.defineProperty(exports,"PktRadioButton",{enumerable:!0,get:()=>p.PktRadioButton});Object.defineProperty(exports,"PktRadiobutton",{enumerable:!0,get:()=>p.PktRadioButton});Object.defineProperty(exports,"PktTag",{enumerable:!0,get:()=>B.PktTag});exports.PktTabItem=b.PktTabItem;exports.PktTabs=b.PktTabs;Object.defineProperty(exports,"PktTextarea",{enumerable:!0,get:()=>M.PktTextarea});Object.defineProperty(exports,"PktTextinput",{enumerable:!0,get:()=>D.PktTextinput});Object.defineProperty(exports,"PktSelect",{enumerable:!0,get:()=>R.PktSelect});
package/dist/pkt-index.js CHANGED
@@ -1,40 +1,41 @@
1
1
  import { P as T } from "./alert-CPY1IhxY.js";
2
- import { P as I, a as w } from "./accordionitem-C_URrDjP.js";
3
- import { P as D } from "./backlink-CI_jMGzZ.js";
4
- import { P as O } from "./button-D99MF5nV.js";
5
- import { c as f } from "./calendar-Bz27nuTP.js";
6
- import { P as j } from "./calendar-Bz27nuTP.js";
7
- import { P as G } from "./card-0F02tcJV.js";
8
- import { P as K } from "./combobox-D-VZRCHk.js";
9
- import { P as U } from "./consent-BSNqH1LX.js";
2
+ import { P as A, a as I } from "./accordionitem-C_URrDjP.js";
3
+ import { P as B } from "./backlink-CI_jMGzZ.js";
4
+ import { P as E } from "./button-D99MF5nV.js";
5
+ import { c as d } from "./calendar-Bz27nuTP.js";
6
+ import { P as R } from "./calendar-Bz27nuTP.js";
7
+ import { P as M } from "./card-0F02tcJV.js";
8
+ import { P as U } from "./combobox-D-VZRCHk.js";
9
+ import { P as N } from "./consent-BSNqH1LX.js";
10
10
  import { P as q } from "./checkbox-CfXOh6Lw.js";
11
- import { P as d, t as h, x as P, n, a as c } from "./element-CRDRygXu.js";
11
+ import { P as f, t as h, x as l, n, a as c } from "./element-CRDRygXu.js";
12
12
  import { P as x } from "./pkt-slot-controller-BPGj-LC5.js";
13
13
  import { e as m, n as k } from "./ref-Xa5dbh--.js";
14
14
  import { e as u } from "./class-map-wy7PUk0P.js";
15
15
  import { P as F, a as J } from "./datepicker-DsqM01iU.js";
16
- import { P as V } from "./helptext-Cs3QHeEy.js";
17
- import { P as Y } from "./heading-BUdy170t.js";
18
- import { P as tt } from "./icon-1dy7UZcu.js";
19
- import { P as rt } from "./input-wrapper-CnYj-xNE.js";
20
- import { P as st } from "./link-DzZCw8j2.js";
21
- import { P as nt } from "./linkcard-RIK5xqbd.js";
22
- import { P as pt } from "./loader-BVvBzaPI.js";
23
- import { P as Pt } from "./messagebox-DwdMXoAe.js";
24
- import { P as kt } from "./modal-BGXk3f9u.js";
25
- import { P as dt } from "./progressbar-kxcBEspG.js";
26
- import { P as ct, P as xt } from "./radiobutton-C_MzK8dE.js";
27
- import { P as gt } from "./tag-BpCdJuei.js";
28
- import { a as bt, P as St } from "./tabitem-NV2fzs_-.js";
29
- import { P as $t } from "./textarea-BJGCHy37.js";
30
- import { P as _t } from "./textinput-B3Q13J8H.js";
31
- import { P as Tt } from "./select-CtgQkH86.js";
16
+ import { PktHeader as V, PktHeaderService as X, PktHeaderUserMenu as Y } from "./pkt-header.js";
17
+ import { P as tt } from "./helptext-Cs3QHeEy.js";
18
+ import { P as rt } from "./heading-BUdy170t.js";
19
+ import { P as st } from "./icon-1dy7UZcu.js";
20
+ import { P as nt } from "./input-wrapper-CnYj-xNE.js";
21
+ import { P as pt } from "./link-DzZCw8j2.js";
22
+ import { P as lt } from "./linkcard-RIK5xqbd.js";
23
+ import { P as kt } from "./loader-BVvBzaPI.js";
24
+ import { P as ft } from "./messagebox-DwdMXoAe.js";
25
+ import { P as ct } from "./modal-BGXk3f9u.js";
26
+ import { P as ut } from "./progressbar-kxcBEspG.js";
27
+ import { P as vt, P as bt } from "./radiobutton-C_MzK8dE.js";
28
+ import { P as Ct } from "./tag-BpCdJuei.js";
29
+ import { a as yt, P as _t } from "./tabitem-NV2fzs_-.js";
30
+ import { P as Tt } from "./textarea-BJGCHy37.js";
31
+ import { P as At } from "./textinput-B3Q13J8H.js";
32
+ import { P as wt } from "./select-CtgQkH86.js";
32
33
  var g = Object.defineProperty, v = Object.getOwnPropertyDescriptor, s = (t, e, i, a) => {
33
- for (var r = a > 1 ? void 0 : a ? v(e, i) : e, p = t.length - 1, l; p >= 0; p--)
34
- (l = t[p]) && (r = (a ? l(e, i, r) : l(r)) || r);
34
+ for (var r = a > 1 ? void 0 : a ? v(e, i) : e, p = t.length - 1, P; p >= 0; p--)
35
+ (P = t[p]) && (r = (a ? P(e, i, r) : P(r)) || r);
35
36
  return a && r && g(e, i, r), r;
36
37
  };
37
- let o = class extends d {
38
+ let o = class extends f {
38
39
  constructor() {
39
40
  super(), this.string = "", this.strings = [], this.darkmode = !1, this._list = [], this.defaultSlot = m(), this.namedSlot = m(), this.slotController = new x(this, this.defaultSlot, this.namedSlot);
40
41
  }
@@ -55,7 +56,7 @@ let o = class extends d {
55
56
  "pkt-component--has-list": this.strings.length > 0,
56
57
  "pkt-darkmode": this.darkmode
57
58
  };
58
- return P`
59
+ return l`
59
60
  <div class="${u(t)}">
60
61
  <h1 class="pkt-txt-28">${this.string}</h1>
61
62
 
@@ -82,9 +83,9 @@ let o = class extends d {
82
83
  `;
83
84
  }
84
85
  renderList(t) {
85
- return P`
86
+ return l`
86
87
  <ul>
87
- ${t.map((e) => P`<li>${e}</li>`)}
88
+ ${t.map((e) => l`<li>${e}</li>`)}
88
89
  </ul>
89
90
  `;
90
91
  }
@@ -109,7 +110,7 @@ s([
109
110
  n({ type: String })
110
111
  ], o.prototype, "string", 2);
111
112
  s([
112
- n({ converter: f })
113
+ n({ converter: d })
113
114
  ], o.prototype, "strings", 2);
114
115
  s([
115
116
  n({ type: Boolean })
@@ -121,35 +122,38 @@ o = s([
121
122
  c("pkt-component")
122
123
  ], o);
123
124
  export {
124
- I as PktAccordion,
125
- w as PktAccordionItem,
125
+ A as PktAccordion,
126
+ I as PktAccordionItem,
126
127
  T as PktAlert,
127
- D as PktBackLink,
128
- O as PktButton,
129
- j as PktCalendar,
130
- G as PktCard,
128
+ B as PktBackLink,
129
+ E as PktButton,
130
+ R as PktCalendar,
131
+ M as PktCard,
131
132
  q as PktCheckbox,
132
- K as PktCombobox,
133
+ U as PktCombobox,
133
134
  o as PktComponent,
134
- U as PktConsent,
135
+ N as PktConsent,
135
136
  F as PktDateTags,
136
137
  J as PktDatepicker,
137
- Y as PktHeading,
138
- V as PktHelptext,
139
- tt as PktIcon,
140
- rt as PktInputWrapper,
141
- st as PktLink,
142
- nt as PktLinkCard,
143
- pt as PktLoader,
144
- Pt as PktMessagebox,
145
- kt as PktModal,
146
- dt as PktProgressbar,
147
- ct as PktRadioButton,
148
- xt as PktRadiobutton,
149
- Tt as PktSelect,
150
- bt as PktTabItem,
151
- St as PktTabs,
152
- gt as PktTag,
153
- $t as PktTextarea,
154
- _t as PktTextinput
138
+ V as PktHeader,
139
+ X as PktHeaderService,
140
+ Y as PktHeaderUserMenu,
141
+ rt as PktHeading,
142
+ tt as PktHelptext,
143
+ st as PktIcon,
144
+ nt as PktInputWrapper,
145
+ pt as PktLink,
146
+ lt as PktLinkCard,
147
+ kt as PktLoader,
148
+ ft as PktMessagebox,
149
+ ct as PktModal,
150
+ ut as PktProgressbar,
151
+ vt as PktRadioButton,
152
+ bt as PktRadiobutton,
153
+ wt as PktSelect,
154
+ yt as PktTabItem,
155
+ _t as PktTabs,
156
+ Ct as PktTag,
157
+ Tt as PktTextarea,
158
+ At as PktTextinput
155
159
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-elements",
3
- "version": "13.22.0",
3
+ "version": "14.0.1",
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",
@@ -41,8 +41,8 @@
41
41
  "@babel/plugin-transform-private-property-in-object": "^7.25.9",
42
42
  "@babel/preset-env": "^7.28.3",
43
43
  "@babel/preset-typescript": "^7.25.9",
44
- "@oslokommune/punkt-assets": "^13.16.0",
45
- "@oslokommune/punkt-css": "^13.22.0",
44
+ "@oslokommune/punkt-assets": "^14.0.0",
45
+ "@oslokommune/punkt-css": "^14.0.1",
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": "ac304ed1adafb9567490e5b9a8d550712f4e68f1"
82
+ "gitHead": "6bc7e06b1757136f9460e4f4e0ea1a4df772cd1f"
83
83
  }
@@ -0,0 +1,404 @@
1
+ import '@testing-library/jest-dom'
2
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
3
+ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
4
+ import { CustomElementFor } from '../../tests/component-registry'
5
+ import './header-service'
6
+
7
+ // Nested components are imported by header-service itself, but we need to wait for them
8
+ const waitForNestedElements = async () => {
9
+ await Promise.all([
10
+ customElements.whenDefined('pkt-button'),
11
+ customElements.whenDefined('pkt-icon'),
12
+ customElements.whenDefined('pkt-link'),
13
+ customElements.whenDefined('pkt-textinput'),
14
+ customElements.whenDefined('pkt-header-user-menu'),
15
+ ])
16
+ }
17
+
18
+ export interface HeaderServiceTestConfig extends BaseTestConfig {
19
+ 'service-name'?: string
20
+ 'service-link'?: string
21
+ 'logo-link'?: string
22
+ compact?: boolean
23
+ 'hide-logo'?: boolean
24
+ position?: 'fixed' | 'relative'
25
+ 'scroll-behavior'?: 'hide' | 'none'
26
+ 'show-search'?: boolean
27
+ 'search-placeholder'?: string
28
+ 'search-value'?: string
29
+ 'log-out-button-placement'?: 'userMenu' | 'header' | 'both' | 'none'
30
+ 'can-change-representation'?: boolean
31
+ 'opened-menu'?: 'none' | 'slot' | 'search' | 'user'
32
+ 'mobile-breakpoint'?: number
33
+ 'tablet-breakpoint'?: number
34
+ }
35
+
36
+ const createHeaderServiceTest = async (config: HeaderServiceTestConfig = {}) => {
37
+ const result = await createElementTest<
38
+ CustomElementFor<'pkt-header-service'>,
39
+ HeaderServiceTestConfig
40
+ >('pkt-header-service', config)
41
+ await waitForNestedElements()
42
+ await result.element.updateComplete
43
+ return result
44
+ }
45
+
46
+ describe('pkt-header-service', () => {
47
+ beforeEach(() => {
48
+ // Mock window.matchMedia for responsive behavior
49
+ Object.defineProperty(window, 'matchMedia', {
50
+ writable: true,
51
+ value: vi.fn().mockImplementation((query) => ({
52
+ matches: false,
53
+ media: query,
54
+ onchange: null,
55
+ addListener: vi.fn(),
56
+ removeListener: vi.fn(),
57
+ addEventListener: vi.fn(),
58
+ removeEventListener: vi.fn(),
59
+ dispatchEvent: vi.fn(),
60
+ })),
61
+ })
62
+
63
+ // Mock ResizeObserver
64
+ global.ResizeObserver = class ResizeObserver {
65
+ observe() {}
66
+ unobserve() {}
67
+ disconnect() {}
68
+ }
69
+
70
+ // Mock scrollTo
71
+ window.scrollTo = vi.fn()
72
+ })
73
+
74
+ afterEach(() => {
75
+ document.body.innerHTML = ''
76
+ })
77
+
78
+ describe('basic rendering', () => {
79
+ it('renders with service name', async () => {
80
+ const { element } = await createHeaderServiceTest({ 'service-name': 'My Service' })
81
+ const serviceName = element.querySelector('.pkt-header-service__service-name')
82
+ expect(serviceName).toBeTruthy()
83
+ expect(serviceName?.textContent).toContain('My Service')
84
+ })
85
+
86
+ it('applies compact class when compact attribute is set', async () => {
87
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc', compact: true })
88
+ const header = element.querySelector('.pkt-header-service')
89
+ expect(header?.classList.contains('pkt-header-service--compact')).toBe(true)
90
+ })
91
+
92
+ it('shows logo by default', async () => {
93
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
94
+ const logo = element.querySelector('.pkt-header-service__logo')
95
+ expect(logo).toBeTruthy()
96
+ })
97
+
98
+ it('hides logo when hide-logo attribute is set', async () => {
99
+ const { element } = await createHeaderServiceTest({
100
+ 'service-name': 'Svc',
101
+ 'hide-logo': true,
102
+ })
103
+ const logo = element.querySelector('.pkt-header-service__logo')
104
+ expect(logo).toBeNull()
105
+ })
106
+ })
107
+
108
+ describe('logoLink attribute', () => {
109
+ it('renders logo as link when logo-link is provided', async () => {
110
+ const { element } = await createHeaderServiceTest({
111
+ 'service-name': 'Svc',
112
+ 'logo-link': 'https://oslo.kommune.no',
113
+ })
114
+ const logoLink = element.querySelector('.pkt-header-service__logo a')
115
+ expect(logoLink).toBeTruthy()
116
+ expect(logoLink?.getAttribute('href')).toBe('https://oslo.kommune.no')
117
+ })
118
+
119
+ it('dispatches logo-click event when logo button is clicked', async () => {
120
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
121
+
122
+ const eventSpy = vi.fn()
123
+ element.addEventListener('logo-click', eventSpy)
124
+
125
+ const logoButton = element.querySelector('.pkt-header-service__logo button')
126
+
127
+ if (logoButton) {
128
+ logoButton.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }))
129
+ expect(eventSpy).toHaveBeenCalled()
130
+ }
131
+ })
132
+
133
+ it('renders logo without link when logo-link is not provided', async () => {
134
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
135
+ const logoLink = element.querySelector('.pkt-header-service__logo a')
136
+ expect(logoLink).toBeNull()
137
+ })
138
+ })
139
+
140
+ describe('serviceLink and service-click event', () => {
141
+ it('renders service name as link when service-link is provided', async () => {
142
+ const { element } = await createHeaderServiceTest({
143
+ 'service-name': 'My Service',
144
+ 'service-link': 'https://example.com',
145
+ })
146
+ // Check that serviceLink property is set correctly
147
+ expect(element.serviceLink).toBe('https://example.com')
148
+ // Check that pkt-link element exists (may not fully render in test env)
149
+ const linkElement = element.querySelector('pkt-link')
150
+ expect(linkElement).toBeTruthy()
151
+ })
152
+
153
+ it('dispatches service-click event when service button is clicked', async () => {
154
+ const { element } = await createHeaderServiceTest({ 'service-name': 'My Service' })
155
+
156
+ const eventSpy = vi.fn()
157
+ element.addEventListener('service-click', eventSpy)
158
+
159
+ const serviceButton = element.querySelector('button.pkt-header-service__service-link')
160
+
161
+ if (serviceButton) {
162
+ serviceButton.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }))
163
+ expect(eventSpy).toHaveBeenCalled()
164
+ }
165
+ })
166
+
167
+ it('renders service name as span when service-link is not provided', async () => {
168
+ const { element } = await createHeaderServiceTest({ 'service-name': 'My Service' })
169
+ const span = element.querySelector('span.pkt-header-service__service-link')
170
+ expect(span).toBeTruthy()
171
+ })
172
+ })
173
+
174
+ describe('position and scrollBehavior attributes', () => {
175
+ it('applies fixed class by default (position="fixed")', async () => {
176
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
177
+ const header = element.querySelector('.pkt-header-service')
178
+ expect(header?.classList.contains('pkt-header-service--fixed')).toBe(true)
179
+ })
180
+
181
+ it('does not apply fixed class when position="relative"', async () => {
182
+ const { element } = await createHeaderServiceTest({
183
+ 'service-name': 'Svc',
184
+ position: 'relative',
185
+ })
186
+ const header = element.querySelector('.pkt-header-service')
187
+ expect(header?.classList.contains('pkt-header-service--fixed')).toBe(false)
188
+ })
189
+
190
+ it('applies scroll-to-hide class by default (scroll-behavior="hide")', async () => {
191
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
192
+ const header = element.querySelector('.pkt-header-service')
193
+ expect(header?.classList.contains('pkt-header-service--scroll-to-hide')).toBe(true)
194
+ })
195
+
196
+ it('does not apply scroll-to-hide class when scroll-behavior="none"', async () => {
197
+ const { element } = await createHeaderServiceTest({
198
+ 'service-name': 'Svc',
199
+ 'scroll-behavior': 'none',
200
+ })
201
+ const header = element.querySelector('.pkt-header-service')
202
+ expect(header?.classList.contains('pkt-header-service--scroll-to-hide')).toBe(false)
203
+ })
204
+ })
205
+
206
+ describe('search functionality', () => {
207
+ it('does not render search input by default', async () => {
208
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
209
+ const search = element.querySelector('.pkt-header-service__search-input')
210
+ expect(search).toBeNull()
211
+ })
212
+
213
+ it('renders search container when show-search is true', async () => {
214
+ const { element } = await createHeaderServiceTest({
215
+ 'service-name': 'Svc',
216
+ 'show-search': true,
217
+ })
218
+ const searchContainer = element.querySelector('.pkt-header-service__search-container')
219
+ expect(searchContainer).toBeTruthy()
220
+ })
221
+
222
+ it('dispatches search event when Enter is pressed in search input', async () => {
223
+ const { element } = await createHeaderServiceTest({
224
+ 'service-name': 'Svc',
225
+ 'show-search': true,
226
+ })
227
+
228
+ const eventSpy = vi.fn()
229
+ element.addEventListener('search', eventSpy)
230
+
231
+ const searchInput = element.querySelector(
232
+ '.pkt-header-service__search-input input',
233
+ ) as HTMLInputElement
234
+ if (searchInput) {
235
+ searchInput.value = 'test query'
236
+ searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }))
237
+ await element.updateComplete
238
+ expect(eventSpy).toHaveBeenCalled()
239
+ }
240
+ })
241
+
242
+ it('dispatches search-change event when search input value changes', async () => {
243
+ const { element } = await createHeaderServiceTest({
244
+ 'service-name': 'Svc',
245
+ 'show-search': true,
246
+ })
247
+
248
+ const eventSpy = vi.fn()
249
+ element.addEventListener('search-change', eventSpy)
250
+
251
+ const searchInput = element.querySelector(
252
+ '.pkt-header-service__search-input input',
253
+ ) as HTMLInputElement
254
+ if (searchInput) {
255
+ searchInput.value = 'test'
256
+ searchInput.dispatchEvent(new Event('input', { bubbles: true }))
257
+ await element.updateComplete
258
+ expect(eventSpy).toHaveBeenCalled()
259
+ }
260
+ })
261
+ })
262
+
263
+ describe('user menu', () => {
264
+ it('renders user container when user is provided', async () => {
265
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
266
+ element.user = { name: 'Aksel Olsen' }
267
+ await element.updateComplete
268
+
269
+ // Check that user container exists
270
+ const userContainer = element.querySelector('.pkt-header-service__user-container')
271
+ expect(userContainer).toBeTruthy()
272
+ // Check that user property is passed correctly
273
+ expect(element.user.name).toBe('Aksel Olsen')
274
+ })
275
+
276
+ it('passes user data to user menu component', async () => {
277
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
278
+ element.user = { name: 'Aksel Olsen' }
279
+ await element.updateComplete
280
+
281
+ // Verify user property is set
282
+ expect(element.user).toEqual({ name: 'Aksel Olsen' })
283
+ })
284
+
285
+ it('passes representing data to user menu component', async () => {
286
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
287
+ element.user = { name: 'Aksel' }
288
+ element.representing = { name: 'Oslo Kommune' }
289
+ await element.updateComplete
290
+
291
+ // Verify representing property is set
292
+ expect(element.representing).toEqual({ name: 'Oslo Kommune' })
293
+ })
294
+
295
+ it('passes canChangeRepresentation prop when set', async () => {
296
+ const { element } = await createHeaderServiceTest({
297
+ 'service-name': 'Svc',
298
+ 'can-change-representation': true,
299
+ })
300
+ element.user = { name: 'Aksel' }
301
+ element.representing = { name: 'Oslo Kommune' }
302
+ await element.updateComplete
303
+
304
+ // Verify canChangeRepresentation property is set
305
+ expect(element.canChangeRepresentation).toBe(true)
306
+ })
307
+ })
308
+
309
+ describe('logout button placement', () => {
310
+ it('shows logout button in user area when log-out-button-placement="header"', async () => {
311
+ const { element } = await createHeaderServiceTest({
312
+ 'service-name': 'Svc',
313
+ 'log-out-button-placement': 'header',
314
+ })
315
+ element.user = { name: 'Aksel' }
316
+ await element.updateComplete
317
+
318
+ const userArea = element.querySelector('.pkt-header-service__user')
319
+ const logoutBtn = userArea?.querySelector('pkt-button')
320
+ expect(logoutBtn).toBeTruthy()
321
+ })
322
+
323
+ it('sets up logout button with correct properties', async () => {
324
+ const { element } = await createHeaderServiceTest({
325
+ 'service-name': 'Svc',
326
+ 'log-out-button-placement': 'header',
327
+ })
328
+ element.user = { name: 'Aksel' }
329
+ await element.updateComplete
330
+
331
+ // Verify logOutButtonPlacement property is set
332
+ expect(element.logOutButtonPlacement).toBe('header')
333
+ // Verify logout button component exists in DOM
334
+ const logoutBtn = element.querySelector('.pkt-header-service__user pkt-button')
335
+ expect(logoutBtn).toBeTruthy()
336
+ })
337
+
338
+ it('does not show logout button in header areas when log-out-button-placement="userMenu"', async () => {
339
+ const { element } = await createHeaderServiceTest({
340
+ 'service-name': 'Svc',
341
+ 'log-out-button-placement': 'userMenu',
342
+ })
343
+ element.user = { name: 'Aksel' }
344
+ await element.updateComplete
345
+
346
+ // Logout should not be in header areas
347
+ const userArea = element.querySelector('.pkt-header-service__user')
348
+ const contentArea = element.querySelector('.pkt-header-service__content')
349
+ const userAreaLogout = userArea?.querySelectorAll('pkt-button[icon-name="exit"]')
350
+ const contentAreaLogout = contentArea?.querySelectorAll('pkt-button[icon-name="exit"]')
351
+
352
+ expect(userAreaLogout?.length || 0).toBe(0)
353
+ expect(contentAreaLogout?.length || 0).toBe(0)
354
+ })
355
+ })
356
+
357
+ describe('user menu items', () => {
358
+ it('passes user menu items to user menu component', async () => {
359
+ const { element } = await createHeaderServiceTest({ 'service-name': 'Svc' })
360
+ element.user = { name: 'Aksel' }
361
+ element.userMenu = [
362
+ { title: 'Mine bookinger', iconName: 'heart', target: '/bookinger' },
363
+ { title: 'Innstillinger', iconName: 'cogwheel', target: () => {} },
364
+ ]
365
+ await element.updateComplete
366
+
367
+ // Verify userMenu property is set correctly
368
+ expect(element.userMenu).toHaveLength(2)
369
+ expect(element.userMenu[0].title).toBe('Mine bookinger')
370
+ expect(element.userMenu[1].title).toBe('Innstillinger')
371
+ })
372
+ })
373
+
374
+ describe('events', () => {
375
+ it('dispatches change-representation event when change button is clicked', async () => {
376
+ const { element } = await createHeaderServiceTest({
377
+ 'service-name': 'Svc',
378
+ 'can-change-representation': true,
379
+ })
380
+ element.user = { name: 'Aksel' }
381
+ element.representing = { name: 'Oslo Kommune' }
382
+ await element.updateComplete
383
+
384
+ const eventSpy = vi.fn()
385
+ element.addEventListener('change-representation', eventSpy)
386
+
387
+ // Open user menu and click change button
388
+ const userButton = element.querySelector('.pkt-user-menu__button') as HTMLElement
389
+ userButton?.click()
390
+ await element.updateComplete
391
+
392
+ const allButtons = element.querySelectorAll('button')
393
+ const changeButton = Array.from(allButtons || []).find((btn) =>
394
+ btn.textContent?.includes('Endre organisasjon'),
395
+ )
396
+
397
+ if (changeButton) {
398
+ changeButton.click()
399
+ await element.updateComplete
400
+ expect(eventSpy).toHaveBeenCalled()
401
+ }
402
+ })
403
+ })
404
+ })