@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.
- package/CHANGELOG.md +44 -0
- package/dist/header.d.ts +1 -0
- package/dist/index.d.ts +259 -1
- package/dist/pkt-header.cjs +302 -0
- package/dist/pkt-header.js +852 -0
- package/dist/pkt-index.cjs +5 -5
- package/dist/pkt-index.js +62 -58
- package/package.json +4 -4
- package/src/components/header/header-service.test.ts +404 -0
- package/src/components/header/header-service.ts +762 -0
- package/src/components/header/header-user-menu.ts +215 -0
- package/src/components/header/header-utils.ts +20 -0
- package/src/components/header/header.ts +141 -0
- package/src/components/header/index.ts +15 -0
- package/src/components/header/types.ts +151 -0
- package/src/components/index.ts +10 -0
package/dist/pkt-index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
2
|
-
<div class="${
|
|
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 ${
|
|
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
|
-
${
|
|
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:
|
|
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
|
|
3
|
-
import { P as
|
|
4
|
-
import { P as
|
|
5
|
-
import { c as
|
|
6
|
-
import { P as
|
|
7
|
-
import { P as
|
|
8
|
-
import { P as
|
|
9
|
-
import { P as
|
|
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
|
|
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 {
|
|
17
|
-
import { P as
|
|
18
|
-
import { P as
|
|
19
|
-
import { P as
|
|
20
|
-
import { P as
|
|
21
|
-
import { P as
|
|
22
|
-
import { P as
|
|
23
|
-
import { P as
|
|
24
|
-
import { P as
|
|
25
|
-
import { P as
|
|
26
|
-
import { P as
|
|
27
|
-
import { P as
|
|
28
|
-
import {
|
|
29
|
-
import { P as
|
|
30
|
-
import { P as
|
|
31
|
-
import { P as
|
|
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,
|
|
34
|
-
(
|
|
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
|
|
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
|
|
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
|
|
86
|
+
return l`
|
|
86
87
|
<ul>
|
|
87
|
-
${t.map((e) =>
|
|
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:
|
|
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
|
-
|
|
125
|
-
|
|
125
|
+
A as PktAccordion,
|
|
126
|
+
I as PktAccordionItem,
|
|
126
127
|
T as PktAlert,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
B as PktBackLink,
|
|
129
|
+
E as PktButton,
|
|
130
|
+
R as PktCalendar,
|
|
131
|
+
M as PktCard,
|
|
131
132
|
q as PktCheckbox,
|
|
132
|
-
|
|
133
|
+
U as PktCombobox,
|
|
133
134
|
o as PktComponent,
|
|
134
|
-
|
|
135
|
+
N as PktConsent,
|
|
135
136
|
F as PktDateTags,
|
|
136
137
|
J as PktDatepicker,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
rt as
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
_t as
|
|
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": "
|
|
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": "^
|
|
45
|
-
"@oslokommune/punkt-css": "^
|
|
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": "
|
|
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
|
+
})
|