@oslokommune/punkt-elements 16.26.7 → 17.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -0
- package/dist/accordion-CbXDxOho.cjs +47 -0
- package/dist/{accordion-CBdIk9gw.js → accordion-DEUHQ4JD.js} +45 -35
- package/dist/{alert-HHTGY0sp.js → alert-CbtVGrHD.js} +31 -17
- package/dist/{alert-B8dLsiTz.cjs → alert-CkUnbPdA.cjs} +5 -5
- package/dist/base-elements/input-element.d.ts +1 -0
- package/dist/button-Cod17rKo.js +115 -0
- package/dist/{button-B5tQpKeG.cjs → button-gvDXtBMC.cjs} +5 -2
- package/dist/{calendar-DIzGBdbh.cjs → calendar-BVXW8DqC.cjs} +1 -1
- package/dist/{calendar-CjlUA-Lb.js → calendar-CjC4qlh6.js} +1 -1
- package/dist/{checkbox-Bt_sC_90.cjs → checkbox-8uxbeU30.cjs} +1 -1
- package/dist/{checkbox-D03BC2Va.js → checkbox-DJDphhqz.js} +1 -1
- package/dist/{combobox-CoQBTWqL.js → combobox-Du5yr-xM.js} +16 -2
- package/dist/combobox-hKrbYLUe.cjs +136 -0
- package/dist/components/accordion/accordion-context.d.ts +7 -0
- package/dist/components/accordion/accordion.d.ts +2 -0
- package/dist/components/accordion/accordionitem.d.ts +1 -0
- package/dist/components/alert/alert.d.ts +5 -0
- package/dist/components/alert/alert.test.d.ts +1 -0
- package/dist/components/button/button.d.ts +4 -1
- package/dist/components/datepicker/datepicker-base.d.ts +2 -0
- package/dist/components/datepicker/datepicker-utils.d.ts +2 -2
- package/dist/components/footer/footer-base.d.ts +44 -0
- package/dist/components/footer/footer-simple.d.ts +20 -0
- package/dist/components/footer/footer.accessibility.test.d.ts +0 -0
- package/dist/components/footer/footer.d.ts +22 -0
- package/dist/components/footer/footer.test.d.ts +0 -0
- package/dist/components/footer/index.d.ts +3 -0
- package/dist/components/footer/types.d.ts +16 -0
- package/dist/components/header/header-chrome-controller.d.ts +44 -0
- package/dist/components/header/header-global.accessibility.test.d.ts +0 -0
- package/dist/components/header/header-global.d.ts +78 -0
- package/dist/components/header/header-global.test.d.ts +0 -0
- package/dist/components/header/header-service.accessibility.test.d.ts +0 -0
- package/dist/components/header/header-service.d.ts +6 -21
- package/dist/components/header/header-service.test.d.ts +1 -1
- package/dist/components/header/header.d.ts +11 -3
- package/dist/components/header/header.test.d.ts +0 -0
- package/dist/components/header/index.d.ts +2 -1
- package/dist/components/header/types.d.ts +28 -2
- package/dist/components/header-menu/header-menu.d.ts +4 -5
- package/dist/components/header-menu/types.d.ts +1 -1
- package/dist/components/index.d.ts +5 -3
- package/dist/components/input-wrapper/input-wrapper.d.ts +2 -0
- package/dist/components/input-wrapper/input-wrapper.test.d.ts +1 -0
- package/dist/components/searchinput/searchinput.d.ts +5 -0
- package/dist/components/select/index.d.ts +1 -1
- package/dist/components/select/select.d.ts +2 -2
- package/dist/components/select/select.test.d.ts +1 -0
- package/dist/components/textarea/textarea.test.d.ts +2 -0
- package/dist/components/textinput/textinput.test.d.ts +1 -0
- package/dist/components/timepicker/timepicker.d.ts +2 -0
- package/dist/components/timepicker/timepicker.test.d.ts +3 -0
- package/dist/consent--Nw43WFU.cjs +18 -0
- package/dist/{consent-D5sY6JK3.js → consent-B2hSTCGC.js} +14 -7
- package/dist/consume-BiOhrhEy.js +167 -0
- package/dist/consume-DKf5SmP9.cjs +1 -0
- package/dist/{converters-BKGBRBpS.js → converters-BBfsEJV5.js} +25 -25
- package/dist/{converters-9jHlXDyy.cjs → converters-Cd0fPEly.cjs} +1 -1
- package/dist/{datepicker-BE5Ll0W4.js → datepicker--Fq8q-J3.js} +30 -12
- package/dist/{datepicker-x4xM3KQz.cjs → datepicker-CEoBYTBU.cjs} +11 -7
- package/dist/docs/components/alert.d.ts +3 -0
- package/dist/docs/components/footer.d.ts +9 -0
- package/dist/docs/components/form-sizes.d.ts +10 -0
- package/dist/docs/components/header-global-full.d.ts +20 -0
- package/dist/docs/components/header-global.d.ts +14 -0
- package/dist/docs/components/searchinput.d.ts +6 -3
- package/dist/fetch-header-footer-data-Pc32etrk.js +88 -0
- package/dist/fetch-header-footer-data-xohWFmYC.cjs +1 -0
- package/dist/fileupload-CGKUJrlj.cjs +401 -0
- package/dist/{fileupload-2hf8YNhO.js → fileupload-D4gQN64T.js} +156 -176
- package/dist/focus-trap-DG3pEuyJ.js +60 -0
- package/dist/focus-trap-DSv4Ifqm.cjs +1 -0
- package/dist/footer-BIrJeRuZ.js +303 -0
- package/dist/footer-Ber-jqM1.cjs +114 -0
- package/dist/footer.d.ts +1 -0
- package/dist/header-BFk1an4z.cjs +465 -0
- package/dist/header-Deg_LGJo.js +1287 -0
- package/dist/header-menu-BHqK4QA6.cjs +126 -0
- package/dist/{header-menu-C8b5vd3n.js → header-menu-GFtJKM4j.js} +25 -97
- package/dist/{helptext-OfqH5npD.js → helptext-C_brWCIT.js} +1 -1
- package/dist/{helptext-eckq4JEX.cjs → helptext-Cq5rNaAC.cjs} +1 -1
- package/dist/{input-element-ZGOWsvKM.cjs → input-element-B0qLbEdF.cjs} +1 -1
- package/dist/{input-element-BFHpeoV8.js → input-element-C0RnXi-J.js} +5 -2
- package/dist/{input-wrapper-DwwE0JcO.js → input-wrapper-BweP6t3O.js} +12 -0
- package/dist/{input-wrapper-BRjQB4zM.cjs → input-wrapper-DPkkiG7S.cjs} +13 -11
- package/dist/{input-wrapper-DaA98B5l.js → input-wrapper-fWV5P4MN.js} +22 -15
- package/dist/{input-wrapper-LcZjpt0L.cjs → input-wrapper-wh3fWIhk.cjs} +1 -1
- package/dist/modal-CPnFteYS.cjs +35 -0
- package/dist/{modal-C6pScpdG.js → modal-xNK_wpHk.js} +11 -20
- package/dist/pkt-accordion.cjs +1 -1
- package/dist/pkt-accordion.js +1 -1
- package/dist/pkt-alert.cjs +1 -1
- package/dist/pkt-alert.js +1 -1
- package/dist/pkt-button.cjs +1 -1
- package/dist/pkt-button.js +1 -1
- package/dist/pkt-calendar.cjs +1 -1
- package/dist/pkt-calendar.js +1 -1
- package/dist/pkt-checkbox.cjs +1 -1
- package/dist/pkt-checkbox.js +1 -1
- package/dist/pkt-combobox.cjs +1 -1
- package/dist/pkt-combobox.js +1 -1
- package/dist/pkt-consent.cjs +1 -1
- package/dist/pkt-consent.js +1 -1
- package/dist/pkt-datepicker.cjs +1 -1
- package/dist/pkt-datepicker.js +1 -1
- package/dist/pkt-fileupload.cjs +1 -1
- package/dist/pkt-fileupload.js +1 -1
- package/dist/pkt-footer.cjs +1 -0
- package/dist/pkt-footer.js +2 -0
- package/dist/pkt-header-menu.cjs +1 -1
- package/dist/pkt-header-menu.js +1 -1
- package/dist/pkt-header.cjs +1 -1
- package/dist/pkt-header.js +2 -2
- package/dist/pkt-helptext.cjs +1 -1
- package/dist/pkt-helptext.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +27 -26
- package/dist/pkt-input-wrapper.cjs +1 -1
- package/dist/pkt-input-wrapper.js +1 -1
- package/dist/pkt-modal.cjs +1 -1
- package/dist/pkt-modal.js +1 -1
- package/dist/pkt-radiobutton.cjs +1 -1
- package/dist/pkt-radiobutton.js +1 -1
- package/dist/pkt-searchinput.cjs +1 -1
- package/dist/pkt-searchinput.js +1 -1
- package/dist/pkt-select.cjs +1 -1
- package/dist/pkt-select.js +1 -1
- package/dist/pkt-tabs.cjs +1 -1
- package/dist/pkt-tabs.js +1 -1
- package/dist/pkt-textarea.cjs +1 -1
- package/dist/pkt-textarea.js +1 -1
- package/dist/pkt-textinput.cjs +1 -1
- package/dist/pkt-textinput.js +1 -1
- package/dist/pkt-timepicker.cjs +1 -1
- package/dist/pkt-timepicker.js +1 -1
- package/dist/{radiobutton-DGO6H2Ki.js → radiobutton-C6htwDrT.js} +1 -1
- package/dist/{radiobutton-DY1WWepJ.cjs → radiobutton-DjWBoSHv.cjs} +1 -1
- package/dist/scroll-lock-BN8lOhV5.cjs +1 -0
- package/dist/scroll-lock-DAbNKAUy.js +18 -0
- package/dist/searchinput-CfUVfpqL.js +156 -0
- package/dist/searchinput-DGjeN1YI.cjs +60 -0
- package/dist/{select-D4_HQFkh.cjs → select-CZvAW7W2.cjs} +2 -1
- package/dist/{select-BwHUIlwQ.js → select-CsBXXbLP.js} +4 -3
- package/dist/setupTests.d.ts +0 -0
- package/dist/shared-types/footer.d.ts +95 -0
- package/dist/shared-types/{header-menu.d.ts → header-footer.d.ts} +51 -4
- package/dist/shared-types/header.d.ts +1 -1
- package/dist/shared-types/index.d.ts +6 -4
- package/dist/shared-types/searchinput.d.ts +2 -0
- package/dist/shared-types/select.d.ts +3 -2
- package/dist/shared-types/size.d.ts +4 -0
- package/dist/shared-types/timepicker.d.ts +0 -6
- package/dist/shared-utils/datepicker-utils.d.ts +5 -4
- package/dist/shared-utils/fileupload/focus-trap.d.ts +4 -10
- package/dist/shared-utils/focus-trap/index.d.ts +22 -0
- package/dist/shared-utils/{header-menu → header-footer}/example-data.d.ts +5 -4
- package/dist/shared-utils/header-footer/external-domain.d.ts +26 -0
- package/dist/shared-utils/{header-menu → header-footer}/fetch-header-footer-data.d.ts +6 -2
- package/dist/shared-utils/{header-menu → header-footer}/index.d.ts +2 -1
- package/dist/shared-utils/{header-menu → header-footer}/select-megamenu.d.ts +1 -1
- package/dist/shared-utils/scroll-lock/index.d.ts +24 -0
- package/dist/tabs-BqrKLCjd.js +214 -0
- package/dist/tabs-q62MK_-r.cjs +40 -0
- package/dist/{textarea-D6M7ZhtA.js → textarea-Cm392CEI.js} +5 -3
- package/dist/{textarea-Bt_Zs8gq.cjs → textarea-sTcZxOIB.cjs} +3 -2
- package/dist/{textinput-EDKjcUdg.cjs → textinput-DqJbTglM.cjs} +3 -2
- package/dist/{textinput-oU2EvFsi.js → textinput-TSPcBcih.js} +5 -3
- package/dist/{timepicker-CgOL2sd9.cjs → timepicker-CqFYMV8q.cjs} +8 -7
- package/dist/{timepicker-_G0V_HV3.js → timepicker-DWbgTDKq.js} +10 -9
- package/package.json +3 -3
- package/src/components/accordion/accordion-context.ts +8 -0
- package/src/components/accordion/accordion.test.ts +44 -0
- package/src/components/accordion/accordion.ts +13 -3
- package/src/components/accordion/accordionitem.ts +9 -1
- package/src/components/alert/alert.test.ts +51 -2
- package/src/components/alert/alert.ts +11 -5
- package/src/components/breadcrumbs/breadcrumbs.test.ts +11 -3
- package/src/components/button/button.test.ts +23 -0
- package/src/components/button/button.ts +28 -3
- package/src/components/combobox/combobox.core.test.ts +20 -0
- package/src/components/combobox/combobox.ts +2 -0
- package/src/components/consent/consent.test.ts +6 -3
- package/src/components/consent/consent.ts +15 -7
- package/src/components/datepicker/datepicker-base.ts +5 -1
- package/src/components/datepicker/datepicker-utils.test.ts +10 -0
- package/src/components/datepicker/datepicker.core.test.ts +17 -0
- package/src/components/datepicker/datepicker.ts +8 -0
- package/src/components/footer/footer-base.ts +238 -0
- package/src/components/footer/footer-simple.ts +71 -0
- package/src/components/footer/footer.accessibility.test.ts +66 -0
- package/src/components/footer/footer.test.ts +238 -0
- package/src/components/footer/footer.ts +81 -0
- package/src/components/footer/index.ts +4 -0
- package/src/components/footer/types.ts +21 -0
- package/src/components/header/header-chrome-controller.ts +123 -0
- package/src/components/header/header-global.accessibility.test.ts +110 -0
- package/src/components/header/header-global.test.ts +329 -0
- package/src/components/header/header-global.ts +648 -0
- package/src/components/header/header-service.accessibility.test.ts +79 -0
- package/src/components/header/header-service.test.ts +32 -12
- package/src/components/header/header-service.ts +38 -162
- package/src/components/header/header-user-menu.ts +0 -1
- package/src/components/header/header.test.ts +84 -0
- package/src/components/header/header.ts +52 -5
- package/src/components/header/index.ts +6 -0
- package/src/components/header/types.ts +38 -0
- package/src/components/header-menu/header-menu.test.ts +131 -10
- package/src/components/header-menu/header-menu.ts +23 -24
- package/src/components/header-menu/types.ts +1 -1
- package/src/components/index.ts +18 -2
- package/src/components/input-wrapper/input-wrapper.test.ts +45 -0
- package/src/components/input-wrapper/input-wrapper.ts +17 -9
- package/src/components/modal/modal.ts +8 -9
- package/src/components/searchinput/searchinput.test.ts +43 -0
- package/src/components/searchinput/searchinput.ts +29 -12
- package/src/components/select/index.ts +1 -1
- package/src/components/select/select.test.ts +28 -0
- package/src/components/select/select.ts +4 -3
- package/src/components/textarea/textarea.test.ts +16 -0
- package/src/components/textarea/textarea.ts +3 -1
- package/src/components/textinput/textinput.test.ts +28 -0
- package/src/components/textinput/textinput.ts +3 -1
- package/src/components/timepicker/timepicker.test.ts +26 -0
- package/src/components/timepicker/timepicker.ts +12 -6
- package/dist/accordion-FISAiAbX.cjs +0 -47
- package/dist/button-C_YzTSuU.js +0 -96
- package/dist/combobox-D6H9rWPV.cjs +0 -135
- package/dist/consent-X3wvhteO.cjs +0 -10
- package/dist/fileupload-BLVrslUX.cjs +0 -401
- package/dist/header-CFrsaq0c.cjs +0 -310
- package/dist/header-D_xvPR4R.js +0 -801
- package/dist/header-menu-DMdP_c6X.cjs +0 -126
- package/dist/modal-DKU7PnjJ.cjs +0 -35
- package/dist/searchinput-CyO5k4Q4.js +0 -148
- package/dist/searchinput-DG6YetL1.cjs +0 -60
- package/dist/shared-utils/modal-scroll-lock.d.ts +0 -10
- package/dist/tabs-CGo0ODPa.cjs +0 -40
- package/dist/tabs-ChdR0D1C.js +0 -379
- /package/dist/shared-utils/{header-menu → header-footer}/icon-map.d.ts +0 -0
|
@@ -12,6 +12,9 @@ import type {
|
|
|
12
12
|
TLogOutButtonPlacement,
|
|
13
13
|
THeaderPosition,
|
|
14
14
|
THeaderScrollBehavior,
|
|
15
|
+
THeaderFooterApi,
|
|
16
|
+
THeaderMenuLocale,
|
|
17
|
+
IPktHeaderGlobal as ISharedPktHeaderGlobal,
|
|
15
18
|
} from 'shared-types'
|
|
16
19
|
|
|
17
20
|
export type {
|
|
@@ -21,7 +24,15 @@ export type {
|
|
|
21
24
|
TLogOutButtonPlacement,
|
|
22
25
|
THeaderPosition,
|
|
23
26
|
THeaderScrollBehavior,
|
|
27
|
+
THeaderFooterApi,
|
|
28
|
+
THeaderMenuLocale,
|
|
24
29
|
}
|
|
30
|
+
|
|
31
|
+
/** Which header variant `<pkt-header>` renders. */
|
|
32
|
+
export type THeaderType = 'service' | 'global'
|
|
33
|
+
|
|
34
|
+
/** Which dropdown/overlay is open in the global header. */
|
|
35
|
+
export type TGlobalHeaderMenu = 'none' | 'menu' | 'user' | 'search'
|
|
25
36
|
export { booleanishConverter }
|
|
26
37
|
|
|
27
38
|
// Re-export with PktIconName typing
|
|
@@ -85,4 +96,31 @@ export interface IPktHeader {
|
|
|
85
96
|
slotMenuVariant?: TSlotMenuVariant
|
|
86
97
|
/** Text for the slot menu button. Default: 'Meny' */
|
|
87
98
|
slotMenuText?: string
|
|
99
|
+
/** Which header variant to render. Default: 'service' */
|
|
100
|
+
type?: THeaderType
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Interface for the global (kommune-wide) Header element props.
|
|
105
|
+
*
|
|
106
|
+
* Extends the framework-agnostic {@link ISharedPktHeaderGlobal} base
|
|
107
|
+
* (in shared-types), narrowing boolean fields to `Booleanish`, typing
|
|
108
|
+
* `userMenu` icons as `PktIconName`, and adding the Lit-only `hasLogOut`
|
|
109
|
+
* signal.
|
|
110
|
+
*/
|
|
111
|
+
export interface IPktHeaderGlobal
|
|
112
|
+
extends Omit<
|
|
113
|
+
ISharedPktHeaderGlobal,
|
|
114
|
+
'showSearch' | 'showContact' | 'canChangeRepresentation' | 'userMenu'
|
|
115
|
+
> {
|
|
116
|
+
/** Show the search field. Default: true */
|
|
117
|
+
showSearch?: Booleanish
|
|
118
|
+
/** Show the contact link (from the payload's `header.contact`). Default: true */
|
|
119
|
+
showContact?: Booleanish
|
|
120
|
+
/** Allow the user to change representation. */
|
|
121
|
+
canChangeRepresentation?: Booleanish
|
|
122
|
+
/** User menu items. */
|
|
123
|
+
userMenu?: UserMenuItem[]
|
|
124
|
+
/** Whether a logout handler is attached (required for the logout button to show). */
|
|
125
|
+
hasLogOut?: Booleanish
|
|
88
126
|
}
|
|
@@ -8,12 +8,16 @@ import './header-menu'
|
|
|
8
8
|
import { PktHeaderMenu } from './header-menu'
|
|
9
9
|
import {
|
|
10
10
|
deriveSocialIcon,
|
|
11
|
+
fetchHeaderFooterData,
|
|
12
|
+
getExternalDomainLabel,
|
|
13
|
+
getLinkDomainLabel,
|
|
11
14
|
ICON_MAP_ODS_TO_PUNKT,
|
|
12
15
|
mapOdsIcon,
|
|
16
|
+
resetHeaderFooterDataCache,
|
|
13
17
|
selectLocaleData,
|
|
14
18
|
selectMegamenu,
|
|
15
|
-
} from 'shared-utils/header-
|
|
16
|
-
import type { THeaderFooterApi } from 'shared-types/header-
|
|
19
|
+
} from 'shared-utils/header-footer'
|
|
20
|
+
import type { THeaderFooterApi } from 'shared-types/header-footer'
|
|
17
21
|
|
|
18
22
|
const LOCAL_DATA: THeaderFooterApi = {
|
|
19
23
|
'nb-NO': {
|
|
@@ -130,9 +134,9 @@ describe('shared-utils: icon-map', () => {
|
|
|
130
134
|
})
|
|
131
135
|
|
|
132
136
|
test('deriveSocialIcon ignores localized text when URL resolves', () => {
|
|
133
|
-
expect(
|
|
134
|
-
|
|
135
|
-
)
|
|
137
|
+
expect(deriveSocialIcon('https://www.facebook.com/Oslo/', 'Følg oss på Facebook')).toBe(
|
|
138
|
+
'facebook',
|
|
139
|
+
)
|
|
136
140
|
})
|
|
137
141
|
|
|
138
142
|
test('deriveSocialIcon falls back to text when the URL is unknown or invalid', () => {
|
|
@@ -170,6 +174,127 @@ describe('shared-utils: select-megamenu', () => {
|
|
|
170
174
|
})
|
|
171
175
|
})
|
|
172
176
|
|
|
177
|
+
describe('shared-utils: external-domain', () => {
|
|
178
|
+
test('returns the top domain for hosts outside oslo.kommune.no', () => {
|
|
179
|
+
expect(
|
|
180
|
+
getExternalDomainLabel(
|
|
181
|
+
'https://candidate.webcruiter.com/nb-no/home/companyadverts?companylock=2180#search',
|
|
182
|
+
),
|
|
183
|
+
).toBe('webcruiter.com')
|
|
184
|
+
expect(getExternalDomainLabel('https://www.facebook.com/Oslo/')).toBe('facebook.com')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('handles two-part TLDs', () => {
|
|
188
|
+
expect(getExternalDomainLabel('https://news.bbc.co.uk/article')).toBe('bbc.co.uk')
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('returns undefined for oslo.kommune.no and subdomains', () => {
|
|
192
|
+
expect(getExternalDomainLabel('https://www.oslo.kommune.no/kontakt/')).toBeUndefined()
|
|
193
|
+
expect(getExternalDomainLabel('https://oslo.kommune.no/')).toBeUndefined()
|
|
194
|
+
expect(getExternalDomainLabel('https://tjenester.oslo.kommune.no/booking')).toBeUndefined()
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('returns undefined for relative URLs, fragments, and non-http schemes', () => {
|
|
198
|
+
expect(getExternalDomainLabel('/jobb-i-oslo-kommune/')).toBeUndefined()
|
|
199
|
+
expect(getExternalDomainLabel('#cb-trigger')).toBeUndefined()
|
|
200
|
+
expect(getExternalDomainLabel('mailto:postmottak@oslo.kommune.no')).toBeUndefined()
|
|
201
|
+
expect(getExternalDomainLabel('tel:+4721802180')).toBeUndefined()
|
|
202
|
+
expect(getExternalDomainLabel('')).toBeUndefined()
|
|
203
|
+
expect(getExternalDomainLabel(undefined)).toBeUndefined()
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
test('honors a custom internal host', () => {
|
|
207
|
+
expect(
|
|
208
|
+
getExternalDomainLabel('https://intranett.example.no/side', 'example.no'),
|
|
209
|
+
).toBeUndefined()
|
|
210
|
+
expect(getExternalDomainLabel('https://www.oslo.kommune.no/', 'example.no')).toBe('oslo.kommune.no')
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('getLinkDomainLabel skips the internal-host check (explicit external flag)', () => {
|
|
214
|
+
expect(getLinkDomainLabel('https://www.oslo.kommune.no/kontakt/')).toBe('oslo.kommune.no')
|
|
215
|
+
expect(getLinkDomainLabel('https://example.test/side')).toBe('example.test')
|
|
216
|
+
expect(getLinkDomainLabel('/relativ')).toBeUndefined()
|
|
217
|
+
expect(getLinkDomainLabel('mailto:foo@example.test')).toBeUndefined()
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
describe('shared-utils: fetchHeaderFooterData dedup', () => {
|
|
222
|
+
const PAYLOAD_URL = 'https://dedup.test/payload.json'
|
|
223
|
+
|
|
224
|
+
const mockFetchOk = () =>
|
|
225
|
+
vi.spyOn(globalThis, 'fetch').mockImplementation(() =>
|
|
226
|
+
Promise.resolve(
|
|
227
|
+
new Response(JSON.stringify(LOCAL_DATA), {
|
|
228
|
+
status: 200,
|
|
229
|
+
headers: { 'Content-Type': 'application/json' },
|
|
230
|
+
}),
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
beforeEach(() => {
|
|
235
|
+
resetHeaderFooterDataCache()
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('concurrent calls for the same URL share one request', async () => {
|
|
239
|
+
const fetchSpy = mockFetchOk()
|
|
240
|
+
const [a, b] = await Promise.all([
|
|
241
|
+
fetchHeaderFooterData(PAYLOAD_URL),
|
|
242
|
+
fetchHeaderFooterData(PAYLOAD_URL),
|
|
243
|
+
])
|
|
244
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1)
|
|
245
|
+
expect(a).toEqual(LOCAL_DATA)
|
|
246
|
+
expect(b).toEqual(LOCAL_DATA)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('successful payloads stay cached for later calls', async () => {
|
|
250
|
+
const fetchSpy = mockFetchOk()
|
|
251
|
+
await fetchHeaderFooterData(PAYLOAD_URL)
|
|
252
|
+
await fetchHeaderFooterData(PAYLOAD_URL)
|
|
253
|
+
expect(fetchSpy).toHaveBeenCalledTimes(1)
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
test('different URLs fetch independently', async () => {
|
|
257
|
+
const fetchSpy = mockFetchOk()
|
|
258
|
+
await Promise.all([
|
|
259
|
+
fetchHeaderFooterData(PAYLOAD_URL),
|
|
260
|
+
fetchHeaderFooterData('https://dedup.test/other.json'),
|
|
261
|
+
])
|
|
262
|
+
expect(fetchSpy).toHaveBeenCalledTimes(2)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('failed fetches are evicted so a retry hits the network', async () => {
|
|
266
|
+
const fetchSpy = vi
|
|
267
|
+
.spyOn(globalThis, 'fetch')
|
|
268
|
+
.mockResolvedValueOnce(new Response('boom', { status: 500 }))
|
|
269
|
+
.mockResolvedValueOnce(
|
|
270
|
+
new Response(JSON.stringify(LOCAL_DATA), {
|
|
271
|
+
status: 200,
|
|
272
|
+
headers: { 'Content-Type': 'application/json' },
|
|
273
|
+
}),
|
|
274
|
+
)
|
|
275
|
+
await expect(fetchHeaderFooterData(PAYLOAD_URL)).rejects.toThrow('HTTP 500')
|
|
276
|
+
await expect(fetchHeaderFooterData(PAYLOAD_URL)).resolves.toEqual(LOCAL_DATA)
|
|
277
|
+
expect(fetchSpy).toHaveBeenCalledTimes(2)
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test('aborting one caller does not cancel the shared request', async () => {
|
|
281
|
+
mockFetchOk()
|
|
282
|
+
const controller = new AbortController()
|
|
283
|
+
const aborted = fetchHeaderFooterData(PAYLOAD_URL, controller.signal)
|
|
284
|
+
const kept = fetchHeaderFooterData(PAYLOAD_URL)
|
|
285
|
+
controller.abort()
|
|
286
|
+
await expect(aborted).rejects.toThrow()
|
|
287
|
+
await expect(kept).resolves.toEqual(LOCAL_DATA)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
test('rejects immediately when the signal is already aborted', async () => {
|
|
291
|
+
mockFetchOk()
|
|
292
|
+
const controller = new AbortController()
|
|
293
|
+
controller.abort()
|
|
294
|
+
await expect(fetchHeaderFooterData(PAYLOAD_URL, controller.signal)).rejects.toThrow()
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
|
|
173
298
|
describe('PktHeaderMenu rendering', () => {
|
|
174
299
|
test('renders services, sections, and footer when given pre-fetched data', async () => {
|
|
175
300
|
const el = await mountWithData()
|
|
@@ -268,7 +393,6 @@ describe('PktHeaderMenu fetching', () => {
|
|
|
268
393
|
await el.updateComplete
|
|
269
394
|
expect(globalThis.fetch).toHaveBeenCalledWith(
|
|
270
395
|
'https://cdn.web.oslo.kommune.no/header-footer/header-footer.json',
|
|
271
|
-
expect.any(Object),
|
|
272
396
|
)
|
|
273
397
|
expect(el.querySelector('.pkt-header-menu__services-title')?.textContent).toContain(
|
|
274
398
|
'Tjenester og tilbud',
|
|
@@ -281,10 +405,7 @@ describe('PktHeaderMenu fetching', () => {
|
|
|
281
405
|
document.body.appendChild(el)
|
|
282
406
|
await waitForReady()
|
|
283
407
|
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
284
|
-
expect(globalThis.fetch).toHaveBeenCalledWith(
|
|
285
|
-
'https://custom.test/payload.json',
|
|
286
|
-
expect.any(Object),
|
|
287
|
-
)
|
|
408
|
+
expect(globalThis.fetch).toHaveBeenCalledWith('https://custom.test/payload.json')
|
|
288
409
|
})
|
|
289
410
|
|
|
290
411
|
test('emits data-error when the fetch fails', async () => {
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
fetchHeaderFooterData,
|
|
10
10
|
mapOdsIcon,
|
|
11
11
|
selectLocaleData,
|
|
12
|
-
} from 'shared-utils/header-
|
|
12
|
+
} from 'shared-utils/header-footer'
|
|
13
13
|
import type {
|
|
14
14
|
IHeaderFooterLocaleData,
|
|
15
15
|
IHeaderMenuButton,
|
|
@@ -39,9 +39,6 @@ export class PktHeaderMenu extends PktElement<IPktHeaderMenu> implements IPktHea
|
|
|
39
39
|
@property({ type: Boolean, reflect: true, converter: booleanishConverter })
|
|
40
40
|
open: Booleanish = false
|
|
41
41
|
|
|
42
|
-
@property({ type: String, attribute: 'aria-labelledby', reflect: true })
|
|
43
|
-
ariaLabelledBy: string = ''
|
|
44
|
-
|
|
45
42
|
@property({ type: Number, attribute: 'mobile-breakpoint' })
|
|
46
43
|
mobileBreakpoint: number = 768
|
|
47
44
|
|
|
@@ -50,13 +47,14 @@ export class PktHeaderMenu extends PktElement<IPktHeaderMenu> implements IPktHea
|
|
|
50
47
|
@state() private isMobile = false
|
|
51
48
|
|
|
52
49
|
private abortController?: AbortController
|
|
53
|
-
private
|
|
50
|
+
private menuWidth = 0
|
|
51
|
+
private resizeObserver?: ResizeObserver
|
|
54
52
|
|
|
55
53
|
override connectedCallback(): void {
|
|
56
54
|
super.connectedCallback()
|
|
57
55
|
this.classList.add('pkt-header-menu')
|
|
58
56
|
this.updateOpenClass()
|
|
59
|
-
this.
|
|
57
|
+
this.observeWidth()
|
|
60
58
|
if (!this.data) {
|
|
61
59
|
void this.loadData()
|
|
62
60
|
} else {
|
|
@@ -78,7 +76,8 @@ export class PktHeaderMenu extends PktElement<IPktHeaderMenu> implements IPktHea
|
|
|
78
76
|
override disconnectedCallback(): void {
|
|
79
77
|
super.disconnectedCallback()
|
|
80
78
|
this.abortController?.abort()
|
|
81
|
-
this.
|
|
79
|
+
this.resizeObserver?.disconnect()
|
|
80
|
+
this.resizeObserver = undefined
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
override updated(changedProperties: PropertyValues): void {
|
|
@@ -92,12 +91,9 @@ export class PktHeaderMenu extends PktElement<IPktHeaderMenu> implements IPktHea
|
|
|
92
91
|
changedProperties.has('mobileBreakpoint') &&
|
|
93
92
|
changedProperties.get('mobileBreakpoint') !== undefined
|
|
94
93
|
) {
|
|
95
|
-
this.
|
|
94
|
+
this.refreshIsMobile()
|
|
96
95
|
}
|
|
97
96
|
|
|
98
|
-
// Re-fetch only when dataUrl genuinely changes after first load.
|
|
99
|
-
// The initial fetch is kicked off from connectedCallback, so we
|
|
100
|
-
// skip the noop "field default" change that fires on first update.
|
|
101
97
|
if (
|
|
102
98
|
changedProperties.has('dataUrl') &&
|
|
103
99
|
changedProperties.get('dataUrl') !== undefined &&
|
|
@@ -118,21 +114,24 @@ export class PktHeaderMenu extends PktElement<IPktHeaderMenu> implements IPktHea
|
|
|
118
114
|
}
|
|
119
115
|
}
|
|
120
116
|
|
|
121
|
-
private
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.
|
|
125
|
-
this.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
117
|
+
private observeWidth() {
|
|
118
|
+
if (typeof ResizeObserver === 'undefined') return
|
|
119
|
+
this.menuWidth = this.offsetWidth
|
|
120
|
+
this.refreshIsMobile()
|
|
121
|
+
this.resizeObserver = new ResizeObserver((entries) => {
|
|
122
|
+
for (const entry of entries) {
|
|
123
|
+
this.menuWidth =
|
|
124
|
+
entry.borderBoxSize && entry.borderBoxSize.length > 0
|
|
125
|
+
? entry.borderBoxSize[0].inlineSize
|
|
126
|
+
: entry.contentRect.width
|
|
127
|
+
this.refreshIsMobile()
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
this.resizeObserver.observe(this)
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
private
|
|
135
|
-
this.isMobile =
|
|
133
|
+
private refreshIsMobile() {
|
|
134
|
+
this.isMobile = this.menuWidth < this.mobileBreakpoint
|
|
136
135
|
}
|
|
137
136
|
|
|
138
137
|
private async loadData() {
|
package/src/components/index.ts
CHANGED
|
@@ -12,7 +12,13 @@ export { PktCheckbox } from '@/components/checkbox'
|
|
|
12
12
|
export { PktFileUpload } from '@/components/fileupload'
|
|
13
13
|
export { PktDateTags } from '@/components/datepicker/date-tags.js'
|
|
14
14
|
export { PktDatepicker } from '@/components/datepicker/datepicker.js'
|
|
15
|
-
export {
|
|
15
|
+
export { PktFooter, PktFooterSimple } from '@/components/footer'
|
|
16
|
+
export {
|
|
17
|
+
PktHeader,
|
|
18
|
+
PktHeaderService,
|
|
19
|
+
PktHeaderGlobal,
|
|
20
|
+
PktHeaderUserMenu,
|
|
21
|
+
} from '@/components/header'
|
|
16
22
|
export { PktHeaderMenu } from '@/components/header-menu'
|
|
17
23
|
export { PktHelptext } from '@/components/helptext'
|
|
18
24
|
export { PktHeading } from '@/components/heading'
|
|
@@ -58,7 +64,10 @@ export type {
|
|
|
58
64
|
THeaderPosition,
|
|
59
65
|
THeaderScrollBehavior,
|
|
60
66
|
TSlotMenuVariant,
|
|
67
|
+
THeaderType,
|
|
68
|
+
TGlobalHeaderMenu,
|
|
61
69
|
IPktHeader,
|
|
70
|
+
IPktHeaderGlobal,
|
|
62
71
|
} from '@/components/header'
|
|
63
72
|
|
|
64
73
|
export type { IPktHeaderMenu } from '@/components/header-menu'
|
|
@@ -84,7 +93,7 @@ export type { TTagSkin, TTagType } from '@/components/tag'
|
|
|
84
93
|
|
|
85
94
|
export type { IPktTabs, IPktTabItem, TSkin as TTabItemSkin } from '@/components/tabs'
|
|
86
95
|
|
|
87
|
-
export type { TSelectOption } from '@/components/select'
|
|
96
|
+
export type { IPktSelectOption, TSelectOption } from '@/components/select'
|
|
88
97
|
|
|
89
98
|
export type { IPktBackLink } from '@/components/backlink'
|
|
90
99
|
|
|
@@ -93,6 +102,13 @@ export type { IPktBreadcrumbs, IBreadcrumbItem } from '@/components/breadcrumbs'
|
|
|
93
102
|
export type { IPktLoader, TPktLoaderVariant } from '@/components/loader'
|
|
94
103
|
|
|
95
104
|
export type { IPktConsent } from '@/components/consent'
|
|
105
|
+
|
|
106
|
+
export type {
|
|
107
|
+
IPktFooter,
|
|
108
|
+
IPktFooterSimple,
|
|
109
|
+
IPktFooterColumn,
|
|
110
|
+
IPktFooterLink,
|
|
111
|
+
} from '@/components/footer'
|
|
96
112
|
export type {
|
|
97
113
|
FileItem,
|
|
98
114
|
IPktFileUpload,
|
|
@@ -31,6 +31,7 @@ export interface InputWrapperTestConfig extends BaseTestConfig {
|
|
|
31
31
|
requiredText?: string
|
|
32
32
|
tagText?: string
|
|
33
33
|
role?: string
|
|
34
|
+
size?: 'small' | 'medium' | 'xsmall'
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
// Use shared framework
|
|
@@ -280,6 +281,27 @@ describe('PktInputWrapper', () => {
|
|
|
280
281
|
const errorMessage = wrapper.querySelector('.pkt-alert--error')
|
|
281
282
|
expect(errorMessage).toBeInTheDocument()
|
|
282
283
|
expect(errorMessage?.textContent).toContain('This field is required')
|
|
284
|
+
expect(errorMessage).toHaveClass('pkt-alert--small')
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
test('uses xsmall error alert when wrapper size is small or xsmall', async () => {
|
|
288
|
+
const { wrapper: smallWrapper } = await createInputWrapperTest({
|
|
289
|
+
label: 'Test Label',
|
|
290
|
+
hasError: true,
|
|
291
|
+
errorMessage: 'Required',
|
|
292
|
+
})
|
|
293
|
+
smallWrapper.size = 'small'
|
|
294
|
+
await smallWrapper.updateComplete
|
|
295
|
+
expect(smallWrapper.querySelector('.pkt-alert--error')).toHaveClass('pkt-alert--xsmall')
|
|
296
|
+
|
|
297
|
+
const { wrapper: xsmallWrapper } = await createInputWrapperTest({
|
|
298
|
+
label: 'Test Label',
|
|
299
|
+
hasError: true,
|
|
300
|
+
errorMessage: 'Required',
|
|
301
|
+
})
|
|
302
|
+
xsmallWrapper.size = 'xsmall'
|
|
303
|
+
await xsmallWrapper.updateComplete
|
|
304
|
+
expect(xsmallWrapper.querySelector('.pkt-alert--error')).toHaveClass('pkt-alert--xsmall')
|
|
283
305
|
})
|
|
284
306
|
|
|
285
307
|
test('does not display error message when hasError is false', async () => {
|
|
@@ -303,6 +325,29 @@ describe('PktInputWrapper', () => {
|
|
|
303
325
|
const inputWrapper = wrapper.querySelector('.pkt-inputwrapper')
|
|
304
326
|
expect(inputWrapper?.classList.contains('pkt-inputwrapper--error')).toBe(true)
|
|
305
327
|
})
|
|
328
|
+
|
|
329
|
+
test('applies small size modifier class', async () => {
|
|
330
|
+
const { wrapper } = await createInputWrapperTest({
|
|
331
|
+
label: 'Test Label',
|
|
332
|
+
})
|
|
333
|
+
wrapper.size = 'small'
|
|
334
|
+
await wrapper.updateComplete
|
|
335
|
+
|
|
336
|
+
const inputWrapper = wrapper.querySelector('.pkt-inputwrapper')
|
|
337
|
+
expect(inputWrapper?.classList.contains('pkt-inputwrapper--small')).toBe(true)
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
test('applies xsmall size modifier class', async () => {
|
|
341
|
+
const { wrapper } = await createInputWrapperTest({
|
|
342
|
+
label: 'Test Label',
|
|
343
|
+
})
|
|
344
|
+
wrapper.size = 'xsmall'
|
|
345
|
+
await wrapper.updateComplete
|
|
346
|
+
|
|
347
|
+
const inputWrapper = wrapper.querySelector('.pkt-inputwrapper')
|
|
348
|
+
expect(inputWrapper?.classList.contains('pkt-inputwrapper--xsmall')).toBe(true)
|
|
349
|
+
expect(inputWrapper?.classList.contains('pkt-inputwrapper--small')).toBe(false)
|
|
350
|
+
})
|
|
306
351
|
})
|
|
307
352
|
|
|
308
353
|
describe('Fieldset functionality', () => {
|
|
@@ -15,6 +15,7 @@ import '@/components/icon'
|
|
|
15
15
|
import '@/components/alert'
|
|
16
16
|
|
|
17
17
|
type TCounterPosition = 'top' | 'bottom'
|
|
18
|
+
type TInputWrapperSize = 'small' | 'medium' | 'xsmall'
|
|
18
19
|
type Props = ElementProps<
|
|
19
20
|
PktInputWrapper,
|
|
20
21
|
| 'forId'
|
|
@@ -69,6 +70,8 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
69
70
|
@property({ type: String, reflect: true }) role: string | null = 'group'
|
|
70
71
|
@property({ type: Boolean, reflect: true, converter: booleanishConverter })
|
|
71
72
|
useWrapper: Booleanish = specs.props.useWrapper.default
|
|
73
|
+
@property({ type: String, attribute: 'input-wrapper-size' })
|
|
74
|
+
size: TInputWrapperSize = 'medium'
|
|
72
75
|
|
|
73
76
|
protected updated(changedProperties: PropertyValues): void {
|
|
74
77
|
super.updated(changedProperties)
|
|
@@ -86,6 +89,8 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
86
89
|
'pkt-inputwrapper--error': this.hasError,
|
|
87
90
|
'pkt-inputwrapper--disabled': this.disabled,
|
|
88
91
|
'pkt-inputwrapper--inline': this.inline,
|
|
92
|
+
'pkt-inputwrapper--small': this.size === 'small',
|
|
93
|
+
'pkt-inputwrapper--xsmall': this.size === 'xsmall',
|
|
89
94
|
}
|
|
90
95
|
|
|
91
96
|
const tagClasses = 'pkt-tag pkt-tag--small pkt-tag--thin-text'
|
|
@@ -172,15 +177,18 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
172
177
|
|
|
173
178
|
const errorElement = () => {
|
|
174
179
|
if (this.hasError && this.errorMessage) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
180
|
+
const errorAlertSize = this.size === 'medium' ? 'small' : 'xsmall'
|
|
181
|
+
return html`<div class="pkt-inputwrapper__alert-wrapper">
|
|
182
|
+
<pkt-alert
|
|
183
|
+
skin="error"
|
|
184
|
+
.size=${errorAlertSize}
|
|
185
|
+
id=${`${this.forId}-error`}
|
|
186
|
+
aria-live="assertive"
|
|
187
|
+
aria-atomic="true"
|
|
188
|
+
>
|
|
189
|
+
${unsafeHTML(this.errorMessage)}
|
|
190
|
+
</pkt-alert>
|
|
191
|
+
</div>`
|
|
184
192
|
} else {
|
|
185
193
|
return nothing
|
|
186
194
|
}
|
|
@@ -7,11 +7,10 @@ import { ref } from 'lit/directives/ref.js'
|
|
|
7
7
|
import { Ref, createRef } from 'lit/directives/ref.js'
|
|
8
8
|
import { TPktSize } from '@/types/size'
|
|
9
9
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
} from 'shared-utils/modal-scroll-lock'
|
|
10
|
+
createScrollLockToken,
|
|
11
|
+
setScrollLock,
|
|
12
|
+
type ScrollLockToken,
|
|
13
|
+
} from 'shared-utils/scroll-lock'
|
|
15
14
|
import specs from 'componentSpecs/modal.json'
|
|
16
15
|
import '@/components/icon'
|
|
17
16
|
|
|
@@ -46,7 +45,7 @@ export class PktModal extends PktElementWithSlot implements IPktModal {
|
|
|
46
45
|
|
|
47
46
|
dialogRef: Ref<HTMLDialogElement> = createRef()
|
|
48
47
|
|
|
49
|
-
private _scrollLockToken:
|
|
48
|
+
private _scrollLockToken: ScrollLockToken = createScrollLockToken()
|
|
50
49
|
|
|
51
50
|
@state() _isOpen: boolean = false
|
|
52
51
|
|
|
@@ -66,7 +65,7 @@ export class PktModal extends PktElementWithSlot implements IPktModal {
|
|
|
66
65
|
document.removeEventListener('keydown', this.handleKeyDown)
|
|
67
66
|
document.removeEventListener('click', this.handleBackdropClick)
|
|
68
67
|
// Rydd opp scroll-lock hvis elementet fjernes fra DOM mens det er åpent
|
|
69
|
-
|
|
68
|
+
setScrollLock(this._scrollLockToken, false)
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
protected updated(changedProperties: PropertyValues): void {
|
|
@@ -122,7 +121,7 @@ export class PktModal extends PktElementWithSlot implements IPktModal {
|
|
|
122
121
|
this._isOpen = false
|
|
123
122
|
this.open = false
|
|
124
123
|
|
|
125
|
-
|
|
124
|
+
setScrollLock(this._scrollLockToken, false)
|
|
126
125
|
|
|
127
126
|
// Scroll to the opener element if it's not in the viewport
|
|
128
127
|
const openerElement = document.activeElement as HTMLElement
|
|
@@ -143,7 +142,7 @@ export class PktModal extends PktElementWithSlot implements IPktModal {
|
|
|
143
142
|
this.open = true
|
|
144
143
|
this.dialogRef.value?.showModal()
|
|
145
144
|
|
|
146
|
-
|
|
145
|
+
setScrollLock(this._scrollLockToken, true)
|
|
147
146
|
this.dispatchEvent(
|
|
148
147
|
new CustomEvent('showModal', { detail: { origin: event }, bubbles: true, composed: true }),
|
|
149
148
|
)
|
|
@@ -13,6 +13,7 @@ interface SearchInputTestConfig extends BaseTestConfig {
|
|
|
13
13
|
id?: string
|
|
14
14
|
name?: string
|
|
15
15
|
appearance?: 'local' | 'local-with-button' | 'global'
|
|
16
|
+
inputSize?: 'small' | 'medium' | 'xsmall'
|
|
16
17
|
label?: string
|
|
17
18
|
placeholder?: string
|
|
18
19
|
disabled?: boolean
|
|
@@ -61,6 +62,20 @@ describe('PktSearchInput', () => {
|
|
|
61
62
|
})
|
|
62
63
|
})
|
|
63
64
|
|
|
65
|
+
describe('aria-label', () => {
|
|
66
|
+
test('flytter host aria-label til input og fjerner den fra verten', async () => {
|
|
67
|
+
const el = document.createElement('pkt-searchinput') as CustomElementFor<'pkt-searchinput'>
|
|
68
|
+
el.id = 'aria-search'
|
|
69
|
+
el.setAttribute('aria-label', 'Søk i hele nettstedet')
|
|
70
|
+
document.body.appendChild(el)
|
|
71
|
+
await el.updateComplete
|
|
72
|
+
|
|
73
|
+
expect(el.hasAttribute('aria-label')).toBe(false)
|
|
74
|
+
const input = el.querySelector('input')
|
|
75
|
+
expect(input).toHaveAttribute('aria-label', 'Søk i hele nettstedet')
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
64
79
|
describe('label', () => {
|
|
65
80
|
test('renders label when label exists', async () => {
|
|
66
81
|
const { searchinput } = await createSearchInputTest({ label: 'Test Label' })
|
|
@@ -134,6 +149,34 @@ describe('PktSearchInput', () => {
|
|
|
134
149
|
expect(lwb.querySelector('.pkt-searchinput__field')).toBeTruthy()
|
|
135
150
|
expect(lwb.querySelector('.pkt-input__container')).toBeNull()
|
|
136
151
|
})
|
|
152
|
+
|
|
153
|
+
test('applies inputSize classes on wrapper, input, button and container', async () => {
|
|
154
|
+
const { searchinput } = await createSearchInputTest({
|
|
155
|
+
appearance: 'local-with-button',
|
|
156
|
+
})
|
|
157
|
+
searchinput.inputSize = 'small'
|
|
158
|
+
await searchinput.updateComplete
|
|
159
|
+
|
|
160
|
+
const wrapper = searchinput.querySelector('.pkt-searchinput')
|
|
161
|
+
expect(wrapper?.classList.contains('pkt-searchinput--small')).toBe(true)
|
|
162
|
+
|
|
163
|
+
const input = searchinput.querySelector('input')
|
|
164
|
+
expect(input?.classList.contains('pkt-input--small')).toBe(true)
|
|
165
|
+
|
|
166
|
+
const button = searchinput.querySelector('.pkt-searchinput__button')
|
|
167
|
+
expect(button?.classList.contains('pkt-btn--small')).toBe(true)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('local inputSize applies container modifier', async () => {
|
|
171
|
+
const { searchinput } = await createSearchInputTest({
|
|
172
|
+
appearance: 'local',
|
|
173
|
+
})
|
|
174
|
+
searchinput.inputSize = 'xsmall'
|
|
175
|
+
await searchinput.updateComplete
|
|
176
|
+
|
|
177
|
+
const container = searchinput.querySelector('.pkt-input__container')
|
|
178
|
+
expect(container?.classList.contains('pkt-input__container--xsmall')).toBe(true)
|
|
179
|
+
})
|
|
137
180
|
})
|
|
138
181
|
|
|
139
182
|
describe('pkt-search', () => {
|