@oslokommune/punkt-elements 18.7.1 → 18.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/dist/{accordion-NO9LYXaN.cjs → accordion-CsrLQXEA.cjs} +1 -1
- package/dist/{accordion-BI-Qiznz.js → accordion-DoxhiwK-.js} +5 -5
- package/dist/{alert-HFqn-cvb.cjs → alert-DH3VqNqh.cjs} +1 -1
- package/dist/{alert-CayKowTW.js → alert-DxAGxSX_.js} +6 -6
- package/dist/{async-directive-CAZoZMez.js → async-directive-BSwJ8-GL.js} +2 -2
- package/dist/{async-directive-DlB0Sf4j.cjs → async-directive-CfPXShXu.cjs} +1 -1
- package/dist/{backlink-DYO8qA9C.cjs → backlink-CDH824db.cjs} +1 -1
- package/dist/{backlink-BHtxOlTw.js → backlink-CGc7r9CS.js} +3 -3
- package/dist/{breadcrumbs-HG9wzGtr.cjs → breadcrumbs-B37tsIoh.cjs} +1 -1
- package/dist/{breadcrumbs-BlRDuZbZ.js → breadcrumbs-DRGhIXmN.js} +2 -2
- package/dist/{button-CIb0IxTg.cjs → button-Cx6Xdp6t.cjs} +1 -1
- package/dist/{button-DWMHtdWk.js → button-D6QhHPWH.js} +5 -5
- package/dist/{calendar-CBghTpkZ.js → calendar-42o_q-jI.js} +3 -3
- package/dist/{calendar-CA1mz04B.cjs → calendar-CAwIY7x_.cjs} +1 -1
- package/dist/{card-CsSNvjZV.cjs → card-CB72gwBC.cjs} +1 -1
- package/dist/{card-PXDm9JXv.js → card-cyZE94EO.js} +6 -6
- package/dist/{checkbox-CMu4pF8-.cjs → checkbox-CBZEGmqa.cjs} +16 -8
- package/dist/{checkbox-DReZOEy2.js → checkbox-COw1Uwnm.js} +19 -11
- package/dist/{class-map-BL_YL_Ed.js → class-map-DSoj1J68.js} +1 -1
- package/dist/{class-map-BEib48YL.cjs → class-map-DupQbnhE.cjs} +1 -1
- package/dist/{combobox-DcBFiuwP.js → combobox-Cqqokx73.js} +172 -128
- package/dist/combobox-N3NP77le.cjs +137 -0
- package/dist/components/combobox/combobox-base.d.ts +14 -12
- package/dist/components/combobox/combobox-handlers.d.ts +1 -0
- package/dist/components/combobox/combobox-utils.d.ts +3 -1
- package/dist/components/combobox/combobox-value.d.ts +5 -0
- package/dist/components/combobox/combobox.attributes.test.d.ts +0 -0
- package/dist/components/combobox/combobox.d.ts +31 -2
- package/dist/components/combobox/combobox.preselection.test.d.ts +0 -0
- package/dist/components/combobox/index.d.ts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/listbox/listbox.d.ts +3 -1
- package/dist/{consent-Yt1MpkUy.js → consent-CsgKWDsR.js} +2 -2
- package/dist/{consent-D1SMUJLs.cjs → consent-DnE6IPEj.cjs} +1 -1
- package/dist/{datepicker-NSWMVoGi.cjs → datepicker-BzRhOIkG.cjs} +1 -1
- package/dist/{datepicker-DboWQcrh.js → datepicker-CtSYgxMs.js} +11 -11
- package/dist/{directive-helpers-Ddr8wUxL.cjs → directive-helpers-2mcpDDv5.cjs} +1 -1
- package/dist/{directive-helpers-CohhRNQz.js → directive-helpers-ChEduoup.js} +1 -1
- package/dist/docs/components/switch.d.ts +4 -0
- package/dist/{element-DRc10X2R.cjs → element-D_mCyFeX.cjs} +1 -1
- package/dist/{element-e2MAMNEP.js → element-J8nynuVk.js} +1 -0
- package/dist/{element-with-slot-GwTT3WRD.js → element-with-slot-BOuXlWSY.js} +2 -2
- package/dist/{element-with-slot-DgtT8lCH.cjs → element-with-slot-C66Jck5e.cjs} +1 -1
- package/dist/{fileupload-BYVtnSaI.cjs → fileupload-3OtW4Xlo.cjs} +1 -1
- package/dist/{fileupload-CqxWUH0y.js → fileupload-PJWEBD88.js} +8 -8
- package/dist/{footer-YrlXRq7e.js → footer-ByAxlj9_.js} +4 -4
- package/dist/{footer-CLSihvYS.cjs → footer-Ca3DM544.cjs} +1 -1
- package/dist/{header-DsaPCLjJ.js → header-CxfDtS3Y.js} +11 -11
- package/dist/{header-DzVHM7gZ.cjs → header-D2SD2JLt.cjs} +1 -1
- package/dist/{header-menu-wcnhBSzs.cjs → header-menu-B9ABa0WW.cjs} +1 -1
- package/dist/{header-menu-DKAcUFzh.js → header-menu-CIuu8_UC.js} +4 -4
- package/dist/{heading-OfJKjUi2.cjs → heading-4ZHACOeL.cjs} +1 -1
- package/dist/{heading-C23xQS4l.js → heading-BHWdVbvN.js} +2 -2
- package/dist/{helptext-DyHzv2ZG.cjs → helptext-6HsH-I6a.cjs} +1 -1
- package/dist/{helptext-DqdOdYRj.js → helptext-BESB9Vlq.js} +4 -4
- package/dist/{icon-DBFTFd-J.cjs → icon-BzxBkRyE.cjs} +1 -1
- package/dist/{icon-jEFbKTB_.js → icon-DkVqRWbC.js} +1 -1
- package/dist/{if-defined-r8tKY4tG.js → if-defined-BILLSM7i.js} +1 -1
- package/dist/if-defined-BbUZeoaQ.cjs +1 -0
- package/dist/{input-element-kvI_5er5.cjs → input-element-DhIEFeuP.cjs} +1 -1
- package/dist/{input-element-BiUfK_gU.js → input-element-Qb0w3ChW.js} +2 -2
- package/dist/{input-wrapper-Bwh720ke.js → input-wrapper-Bg06k-cj.js} +6 -6
- package/dist/{input-wrapper-wyvCnWPp.cjs → input-wrapper-D64Qn-QH.cjs} +1 -1
- package/dist/{link-BWAhHUku.cjs → link-Bk7aDZCC.cjs} +1 -1
- package/dist/{link-BvjWWWlc.js → link-C7fOEv66.js} +4 -4
- package/dist/{linkcard-6CbGf4I_.js → linkcard-Bguo9oCv.js} +4 -4
- package/dist/{linkcard-D6fRaZSc.cjs → linkcard-rZpQuO1Z.cjs} +1 -1
- package/dist/{listbox-CsP0qbU1.js → listbox-CrQboPG6.js} +80 -63
- package/dist/listbox-kV4OO1Qa.cjs +111 -0
- package/dist/{loader-B5t78Yk7.cjs → loader-BAn68gBb.cjs} +1 -1
- package/dist/{loader-BS7EJqxD.js → loader-C3gwO0i3.js} +4 -4
- package/dist/{messagebox-Bt0IvUlD.js → messagebox-DAE59dMX.js} +4 -4
- package/dist/{messagebox-DBVUz-DN.cjs → messagebox-WH2HjT5I.cjs} +1 -1
- package/dist/{modal-C1g2fiUq.cjs → modal-DZm_fn3K.cjs} +1 -1
- package/dist/{modal-z5RzZ-fr.js → modal-DdC1_qT3.js} +5 -5
- 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-backlink.cjs +1 -1
- package/dist/pkt-backlink.js +1 -1
- package/dist/pkt-breadcrumbs.cjs +1 -1
- package/dist/pkt-breadcrumbs.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-card.cjs +1 -1
- package/dist/pkt-card.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 -1
- package/dist/pkt-footer.js +1 -1
- 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 +1 -1
- package/dist/pkt-heading.cjs +1 -1
- package/dist/pkt-heading.js +1 -1
- package/dist/pkt-helptext.cjs +1 -1
- package/dist/pkt-helptext.js +1 -1
- package/dist/pkt-icon.cjs +1 -1
- package/dist/pkt-icon.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +34 -34
- package/dist/pkt-input-wrapper.cjs +1 -1
- package/dist/pkt-input-wrapper.js +1 -1
- package/dist/pkt-link.cjs +1 -1
- package/dist/pkt-link.js +1 -1
- package/dist/pkt-linkcard.cjs +1 -1
- package/dist/pkt-linkcard.js +1 -1
- package/dist/pkt-listbox.cjs +1 -1
- package/dist/pkt-listbox.js +1 -1
- package/dist/pkt-loader.cjs +1 -1
- package/dist/pkt-loader.js +1 -1
- package/dist/pkt-messagebox.cjs +1 -1
- package/dist/pkt-messagebox.js +1 -1
- package/dist/pkt-modal.cjs +1 -1
- package/dist/pkt-modal.js +1 -1
- package/dist/{pkt-options-controller-AFYyv3-b.cjs → pkt-options-controller-DXAAVcBJ.cjs} +1 -1
- package/dist/{pkt-options-controller-DKkmDDzT.js → pkt-options-controller-XcIgx_v1.js} +1 -1
- package/dist/pkt-progressbar.cjs +1 -1
- package/dist/pkt-progressbar.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-tag.cjs +1 -1
- package/dist/pkt-tag.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/{progressbar-BPHLC7ce.js → progressbar-D1x6MwdY.js} +5 -5
- package/dist/{progressbar-BpcsDCnG.cjs → progressbar-DsE4-qaW.cjs} +1 -1
- package/dist/{radiobutton-B6pzZQZV.cjs → radiobutton-BEzjKx1J.cjs} +15 -7
- package/dist/{radiobutton-bGdAEz26.js → radiobutton-Cpwa5ezn.js} +19 -11
- package/dist/{ref-BkV4Cd8U.cjs → ref-AuNEkS5a.cjs} +1 -1
- package/dist/{ref-lE1mGUOL.js → ref-DNowxdYH.js} +2 -2
- package/dist/{repeat-uBgVG8Ch.cjs → repeat-B3ksuY2F.cjs} +1 -1
- package/dist/{repeat-CCnTfedv.js → repeat-CZ1oMXnW.js} +2 -2
- package/dist/{searchinput-DrUcPajl.js → searchinput-CmyWnNr6.js} +4 -4
- package/dist/{searchinput-CiPB8xvh.cjs → searchinput-vi7twt5b.cjs} +1 -1
- package/dist/{select-ay8WHI7p.cjs → select-TAwBNhhf.cjs} +1 -1
- package/dist/{select-D7r2s7Cw.js → select-c15YAQbr.js} +7 -7
- package/dist/shared-strings/nb.d.ts +1 -0
- package/dist/{tabs-DqzPOdi9.cjs → tabs-hiTrWsXq.cjs} +1 -1
- package/dist/{tabs-DHvCjUi4.js → tabs-kxsX1ZEf.js} +4 -4
- package/dist/{tag-Cojf4jgA.cjs → tag-CGlxtBOH.cjs} +1 -1
- package/dist/{tag-D2Xvsy1i.js → tag-bztxjcF0.js} +5 -5
- package/dist/{textarea-s4IDLsBi.cjs → textarea-BHGZhMN4.cjs} +1 -1
- package/dist/{textarea-CzAL8e6L.js → textarea-BPQVrOp9.js} +9 -9
- package/dist/{textinput-B9w4jhIP.cjs → textinput-Cv4tal5W.cjs} +1 -1
- package/dist/{textinput-DT3HR9eU.js → textinput-DHGUOsIA.js} +8 -8
- package/dist/{timepicker-C-Ip5Jgl.cjs → timepicker-BBUYnYzM.cjs} +1 -1
- package/dist/{timepicker-BE0UouVr.js → timepicker-C8DHbOpM.js} +8 -8
- package/package.json +2 -2
- package/src/components/checkbox/checkbox.test.ts +1 -1
- package/src/components/checkbox/checkbox.ts +32 -13
- package/src/components/combobox/combobox-base.ts +42 -28
- package/src/components/combobox/combobox-handlers.ts +11 -5
- package/src/components/combobox/combobox-utils.ts +9 -1
- package/src/components/combobox/combobox-value.ts +32 -16
- package/src/components/combobox/combobox.accessibility.test.ts +29 -3
- package/src/components/combobox/combobox.attributes.test.ts +82 -0
- package/src/components/combobox/combobox.core.test.ts +46 -1
- package/src/components/combobox/combobox.interaction.test.ts +167 -0
- package/src/components/combobox/combobox.preselection.test.ts +271 -0
- package/src/components/combobox/combobox.ts +143 -79
- package/src/components/combobox/index.ts +6 -1
- package/src/components/index.ts +1 -0
- package/src/components/listbox/listbox.ts +56 -29
- package/src/components/radiobutton/radiobutton.test.ts +1 -1
- package/src/components/radiobutton/radiobutton.ts +32 -13
- package/dist/combobox-BZm_12qR.cjs +0 -140
- package/dist/if-defined-BeVQnl3C.cjs +0 -1
- package/dist/listbox-D95b5zAd.cjs +0 -106
|
@@ -15,7 +15,9 @@ import { getSingleValueForInput } from 'shared-utils/combobox/input-utils'
|
|
|
15
15
|
import { slotUtils, optionStateUtils } from './combobox-utils'
|
|
16
16
|
import { slotContent } from '@/directives/slot-content'
|
|
17
17
|
import { ComboboxHandlers } from './combobox-handlers'
|
|
18
|
+
import { formatString } from 'shared-strings'
|
|
18
19
|
import type { IPktStringsProps } from 'shared-strings'
|
|
20
|
+
import type { Booleanish } from 'shared-types'
|
|
19
21
|
|
|
20
22
|
import '../input-wrapper'
|
|
21
23
|
import '../icon'
|
|
@@ -50,13 +52,42 @@ export interface IPktCombobox extends IPktStringsProps<'combobox' | 'listbox'> {
|
|
|
50
52
|
placeholder?: string | null
|
|
51
53
|
requiredTag?: boolean
|
|
52
54
|
requiredText?: string
|
|
53
|
-
searchPlaceholder?: string
|
|
55
|
+
searchPlaceholder?: string | null
|
|
54
56
|
tagPlacement?: TPktComboboxTagPlacement | null
|
|
55
57
|
tagText?: string | null
|
|
56
58
|
value?: string | string[]
|
|
59
|
+
defaultValue?: string | string[] | null
|
|
60
|
+
hasFieldset?: boolean | null
|
|
61
|
+
useWrapper?: Booleanish
|
|
57
62
|
isOpen?: boolean
|
|
58
63
|
}
|
|
59
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Attribute spellings for template use. TypeScript cannot see the `attribute`
|
|
67
|
+
* option on Lit's @property decorator, so markup like `allow-user-input` is
|
|
68
|
+
* otherwise reported as unknown.
|
|
69
|
+
*/
|
|
70
|
+
export interface IPktComboboxAttributes {
|
|
71
|
+
'allow-user-input'?: boolean
|
|
72
|
+
'default-options'?: IPktComboboxOption[]
|
|
73
|
+
'display-value-as'?: string
|
|
74
|
+
'include-search'?: boolean
|
|
75
|
+
'search-placeholder'?: string | null
|
|
76
|
+
'tag-placement'?: TPktComboboxTagPlacement | null
|
|
77
|
+
defaultvalue?: string | string[] | null
|
|
78
|
+
errormessage?: string
|
|
79
|
+
haserror?: boolean
|
|
80
|
+
helptextdropdown?: string | null
|
|
81
|
+
helptextdropdownbutton?: string | null
|
|
82
|
+
optionaltag?: boolean
|
|
83
|
+
optionaltext?: string
|
|
84
|
+
requiredtag?: boolean
|
|
85
|
+
requiredtext?: string
|
|
86
|
+
tagtext?: string | null
|
|
87
|
+
hasfieldset?: boolean | null
|
|
88
|
+
usewrapper?: Booleanish
|
|
89
|
+
}
|
|
90
|
+
|
|
60
91
|
declare global {
|
|
61
92
|
interface HTMLElementTagNameMap {
|
|
62
93
|
'pkt-combobox': PktCombobox & HTMLSelectElement
|
|
@@ -84,7 +115,7 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
84
115
|
if (this.defaultOptions && this.defaultOptions.length) {
|
|
85
116
|
const userAdded = this.options?.filter((opt) => opt.userAdded) || []
|
|
86
117
|
this.options = [...userAdded, ...JSON.parse(JSON.stringify(this.defaultOptions))]
|
|
87
|
-
this._options =
|
|
118
|
+
this._options = optionStateUtils.cloneOptions(this.options)
|
|
88
119
|
}
|
|
89
120
|
|
|
90
121
|
// If options are provided via the options slot, we need to extract them
|
|
@@ -99,22 +130,6 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
99
130
|
}
|
|
100
131
|
}
|
|
101
132
|
|
|
102
|
-
protected willUpdate(changedProperties: Map<PropertyKey, unknown>): void {
|
|
103
|
-
// Re-parse slot options when the controller detects mutations.
|
|
104
|
-
// The controller increments its generation counter on each mutation, but
|
|
105
|
-
// doesn't set any reactive properties — so we detect the change here.
|
|
106
|
-
if (this._optionsFromSlot && this.optionsController) {
|
|
107
|
-
const currentGen = this.optionsController.generation
|
|
108
|
-
if (currentGen !== this._lastSlotGeneration) {
|
|
109
|
-
this._lastSlotGeneration = currentGen
|
|
110
|
-
const parsedOptions = slotUtils.parseOptionsFromSlot(this.optionsController.nodes)
|
|
111
|
-
const userAdded = this._options.filter((o) => o.userAdded)
|
|
112
|
-
this.options = [...userAdded, ...parsedOptions]
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
super.willUpdate(changedProperties)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
133
|
disconnectedCallback(): void {
|
|
119
134
|
super.disconnectedCallback()
|
|
120
135
|
document?.body.removeEventListener('click', this.handleBodyClick)
|
|
@@ -130,53 +145,71 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
130
145
|
super.firstUpdated(changedProperties)
|
|
131
146
|
}
|
|
132
147
|
|
|
133
|
-
|
|
148
|
+
protected willUpdate(changedProperties: PropertyValues): void {
|
|
149
|
+
// Re-parse slot options when the controller detects mutations.
|
|
150
|
+
// The controller increments its generation counter on each mutation, but
|
|
151
|
+
// doesn't set any reactive properties — so we detect the change here.
|
|
152
|
+
if (this._optionsFromSlot && this.optionsController) {
|
|
153
|
+
const currentGen = this.optionsController.generation
|
|
154
|
+
if (currentGen !== this._lastSlotGeneration) {
|
|
155
|
+
this._lastSlotGeneration = currentGen
|
|
156
|
+
const parsedOptions = slotUtils.parseOptionsFromSlot(this.optionsController.nodes)
|
|
157
|
+
const userAdded = this._options.filter((o) => o.userAdded)
|
|
158
|
+
this.options = [...userAdded, ...parsedOptions]
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
super.willUpdate(changedProperties)
|
|
163
|
+
|
|
134
164
|
if (changedProperties.has('isOpen')) {
|
|
135
165
|
this._isOptionsOpen = this.isOpen
|
|
136
166
|
}
|
|
137
167
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
168
|
+
if (changedProperties.has('defaultOptions') && this.defaultOptions.length) {
|
|
169
|
+
const userAdded =
|
|
170
|
+
(Array.isArray(this.options) ? this.options : []).filter((opt) => opt.userAdded) || []
|
|
171
|
+
this.options = [...userAdded, ...JSON.parse(JSON.stringify(this.defaultOptions))]
|
|
172
|
+
this._options = optionStateUtils.cloneOptions(this.options)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// `value` is derived before options, so that options.selected can add to
|
|
176
|
+
// the result rather than being overwritten by an empty value attribute.
|
|
144
177
|
const valueChanged = changedProperties.has('value')
|
|
145
178
|
const internalChanged = changedProperties.has('_value')
|
|
146
|
-
const isInternalSync = valueChanged && this._internalValueSync
|
|
147
179
|
|
|
148
|
-
if (
|
|
149
|
-
this._internalValueSync = false
|
|
150
|
-
if (internalChanged) {
|
|
151
|
-
this.syncValueAndDispatch(changedProperties.get('_value') as string[])
|
|
152
|
-
}
|
|
153
|
-
} else if (valueChanged) {
|
|
180
|
+
if (valueChanged) {
|
|
154
181
|
const oldInternal = [...this._value]
|
|
155
182
|
const newInternal = this.parseValue()
|
|
156
183
|
if (newInternal.join(',') !== this._value.join(',')) {
|
|
157
184
|
this._value = newInternal
|
|
158
|
-
this.options.forEach((o) => (o.selected = newInternal.includes(o.value)))
|
|
159
185
|
this._options.forEach((o) => (o.selected = newInternal.includes(o.value)))
|
|
160
186
|
this._options = [...this._options]
|
|
161
187
|
}
|
|
162
|
-
this.
|
|
163
|
-
this.syncValueAndDispatch(oldInternal)
|
|
188
|
+
this._pendingDispatchFrom ??= oldInternal
|
|
164
189
|
} else if (internalChanged) {
|
|
165
|
-
this.
|
|
190
|
+
this._pendingDispatchFrom ??= (changedProperties.get('_value') as string[]) ?? []
|
|
166
191
|
}
|
|
167
192
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
193
|
+
// The base class applies defaultValue through valueChanged(), which combobox
|
|
194
|
+
// overrides as a no-op, so it is seeded here instead.
|
|
195
|
+
if (changedProperties.has('defaultValue') && this.defaultValue != null && !this._value.length) {
|
|
196
|
+
const seeded = this.parseToValues(this.defaultValue)
|
|
197
|
+
if (seeded.length) {
|
|
198
|
+
this._pendingDispatchFrom ??= [...this._value]
|
|
199
|
+
this._value = seeded
|
|
200
|
+
this._options.forEach((o) => (o.selected = seeded.includes(o.value)))
|
|
201
|
+
this._options = [...this._options]
|
|
202
|
+
}
|
|
174
203
|
}
|
|
175
204
|
|
|
176
205
|
if (changedProperties.has('options')) {
|
|
177
206
|
const prevOptions =
|
|
178
207
|
(changedProperties.get('options') as IPktComboboxOption[]) || this._options || []
|
|
179
|
-
const mergedOptions = optionStateUtils.mergeWithUserAdded(
|
|
208
|
+
const mergedOptions = optionStateUtils.mergeWithUserAdded(
|
|
209
|
+
optionStateUtils.cloneOptions(this.options),
|
|
210
|
+
prevOptions,
|
|
211
|
+
this._value,
|
|
212
|
+
)
|
|
180
213
|
this._options = mergedOptions
|
|
181
214
|
if (mergedOptions.length > this.options.length) {
|
|
182
215
|
this.options = mergedOptions
|
|
@@ -186,11 +219,32 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
186
219
|
this._options = syncResult.options
|
|
187
220
|
|
|
188
221
|
if (syncResult.newValues.length > this._value.length) {
|
|
189
|
-
|
|
222
|
+
this._pendingDispatchFrom ??= [...this._value]
|
|
190
223
|
this._value = syncResult.newValues
|
|
191
|
-
this.syncValueAndDispatch(oldValue)
|
|
192
224
|
}
|
|
193
225
|
}
|
|
226
|
+
|
|
227
|
+
if (
|
|
228
|
+
valueChanged ||
|
|
229
|
+
internalChanged ||
|
|
230
|
+
changedProperties.has('options') ||
|
|
231
|
+
changedProperties.has('defaultValue')
|
|
232
|
+
) {
|
|
233
|
+
this.updateMaxReached()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (this._pendingDispatchFrom) {
|
|
237
|
+
this.syncPublicValue()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
updated(changedProperties: PropertyValues): void {
|
|
242
|
+
if (this._pendingDispatchFrom) {
|
|
243
|
+
const from = this._pendingDispatchFrom
|
|
244
|
+
this._pendingDispatchFrom = null
|
|
245
|
+
this.dispatchValueChange(from)
|
|
246
|
+
}
|
|
247
|
+
|
|
194
248
|
if (changedProperties.has('_search')) {
|
|
195
249
|
this.dispatchEvent(
|
|
196
250
|
new CustomEvent('search', {
|
|
@@ -264,9 +318,9 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
264
318
|
|
|
265
319
|
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
|
|
266
320
|
// Don't set _value here for 'value' changes — this.value hasn't been updated yet
|
|
267
|
-
// (super.attributeChangedCallback does that). Let
|
|
321
|
+
// (super.attributeChangedCallback does that). Let willUpdate() handle the sync.
|
|
268
322
|
if (name === 'options') {
|
|
269
|
-
this._options =
|
|
323
|
+
this._options = optionStateUtils.cloneOptions(this.options)
|
|
270
324
|
const syncResult = optionStateUtils.syncOptionsWithValues(this._options, this._value)
|
|
271
325
|
this._options = syncResult.options
|
|
272
326
|
if (syncResult.newValues.length > this._value.length) {
|
|
@@ -330,6 +384,7 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
330
384
|
)}
|
|
331
385
|
aria-activedescendant=${ifDefined(
|
|
332
386
|
!this._hasTextInput &&
|
|
387
|
+
this._isOptionsOpen &&
|
|
333
388
|
this._value[0] &&
|
|
334
389
|
!!findOptionByValue(this.options, this._value[0])
|
|
335
390
|
? `${this.id}-listbox-${findOptionIndex(this._options, this._value[0])}`
|
|
@@ -339,18 +394,21 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
339
394
|
tabindex=${!this._hasTextInput ? (this.disabled ? '-1' : '0') : '-1'}
|
|
340
395
|
@click=${this.handleInputClick}
|
|
341
396
|
@keydown=${!this._hasTextInput ? this.handleSelectOnlyKeydown : nothing}
|
|
397
|
+
@blur=${this.restoreInputTab}
|
|
342
398
|
${ref(this.triggerRef)}
|
|
343
399
|
>
|
|
344
|
-
${
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
400
|
+
${
|
|
401
|
+
!this._hasTextInput &&
|
|
402
|
+
this.placeholder &&
|
|
403
|
+
(!this._value.length || (this.multiple && this.tagPlacement == 'outside')) &&
|
|
404
|
+
!this._inputFocus
|
|
405
|
+
? html`<span class="pkt-combobox__placeholder" @click=${this.handlePlaceholderClick}
|
|
406
|
+
>${this.placeholder}</span
|
|
407
|
+
>`
|
|
408
|
+
: this.tagPlacement !== 'outside'
|
|
409
|
+
? this.renderSingleOrMultipleValues()
|
|
410
|
+
: nothing
|
|
411
|
+
}
|
|
354
412
|
${this.renderInputField()}
|
|
355
413
|
<pkt-icon
|
|
356
414
|
class=${classMap({
|
|
@@ -368,18 +426,23 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
368
426
|
.isOpen=${this._isOptionsOpen}
|
|
369
427
|
.searchPlaceholder=${this.searchPlaceholder}
|
|
370
428
|
.label="Liste: ${this.label || ''}"
|
|
429
|
+
?disabled=${this.disabled}
|
|
371
430
|
?include-search=${this.includeSearch}
|
|
372
431
|
?is-multi-select=${this.multiple}
|
|
373
432
|
?allow-user-input=${this.allowUserInput && !this._maxIsReached}
|
|
374
433
|
?max-is-reached=${this._maxIsReached}
|
|
375
434
|
.customUserInput=${ifDefined(this._addValueText)}
|
|
376
|
-
.userMessage=${
|
|
377
|
-
|
|
378
|
-
|
|
435
|
+
.userMessage=${
|
|
436
|
+
this._userInfoMessage
|
|
437
|
+
? formatString(this.pktStrings.get('combobox')[this._userInfoMessage], {
|
|
438
|
+
value: this._messageValue ?? '',
|
|
439
|
+
})
|
|
440
|
+
: ''
|
|
441
|
+
}
|
|
379
442
|
@search=${this.handleSearch}
|
|
380
443
|
@option-toggle=${this.handleOptionToggled}
|
|
381
444
|
@select-all=${this.addAllOptions}
|
|
382
|
-
@close-options=${
|
|
445
|
+
@close-options=${this.handleCloseOptions}
|
|
383
446
|
@tab-close=${() => this.closeAndProcessInput()}
|
|
384
447
|
.searchValue=${this._search || null}
|
|
385
448
|
.maxLength=${this.maxlength || 0}
|
|
@@ -387,11 +450,13 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
387
450
|
></pkt-listbox>
|
|
388
451
|
</div>
|
|
389
452
|
|
|
390
|
-
${
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
453
|
+
${
|
|
454
|
+
this.tagPlacement === 'outside' && this.multiple
|
|
455
|
+
? html`<div class="pkt-combobox__tags-outside">
|
|
456
|
+
${this.renderSingleOrMultipleValues()}
|
|
457
|
+
</div>`
|
|
458
|
+
: nothing
|
|
459
|
+
}
|
|
395
460
|
</pkt-input-wrapper>
|
|
396
461
|
`
|
|
397
462
|
}
|
|
@@ -414,6 +479,7 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
414
479
|
e.stopPropagation()
|
|
415
480
|
e.stopImmediatePropagation()
|
|
416
481
|
}}
|
|
482
|
+
tabindex=${ifDefined(this._skipInputTab ? '-1' : undefined)}
|
|
417
483
|
@keydown=${this.handleInputKeydown}
|
|
418
484
|
@focus=${this.handleFocus}
|
|
419
485
|
@blur=${this.handleBlur}
|
|
@@ -424,7 +490,9 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
424
490
|
aria-autocomplete=${this.typeahead ? 'both' : this.allowUserInput ? 'list' : 'none'}
|
|
425
491
|
aria-controls="${this.id}-listbox"
|
|
426
492
|
aria-activedescendant=${ifDefined(
|
|
427
|
-
this.
|
|
493
|
+
this._isOptionsOpen &&
|
|
494
|
+
this._value[0] &&
|
|
495
|
+
!!findOptionByValue(this.options, this._value[0])
|
|
428
496
|
? `${this.id}-listbox-${findOptionIndex(this._options, this._value[0])}`
|
|
429
497
|
: undefined,
|
|
430
498
|
)}
|
|
@@ -474,9 +542,9 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
474
542
|
?closeTag=${!this.disabled}
|
|
475
543
|
.buttonTabindex=${isOutside ? undefined : -1}
|
|
476
544
|
@close=${() => this.handleTagRemove(value)}
|
|
477
|
-
@keydown=${
|
|
478
|
-
? nothing
|
|
479
|
-
|
|
545
|
+
@keydown=${
|
|
546
|
+
isOutside ? nothing : (e: KeyboardEvent) => this.handleTagKeydown(e, index)
|
|
547
|
+
}
|
|
480
548
|
>
|
|
481
549
|
${this.renderValueTag(option)}
|
|
482
550
|
</pkt-tag>
|
|
@@ -487,6 +555,8 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
487
555
|
</ul>
|
|
488
556
|
`
|
|
489
557
|
|
|
558
|
+
if (!isSingleValueDisplay && this._value.length === 0) return nothing
|
|
559
|
+
|
|
490
560
|
return isSingleValueDisplay ? singleValueContent : multipleValuesContent
|
|
491
561
|
}
|
|
492
562
|
|
|
@@ -495,18 +565,12 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
|
|
|
495
565
|
const displayAs = this.displayValueAs as TPktComboboxDisplayValue
|
|
496
566
|
switch (displayAs) {
|
|
497
567
|
case 'prefixAndValue':
|
|
498
|
-
return html`<span class="pkt-combobox__value"
|
|
499
|
-
>${option.prefix || ''} ${option.value}</span
|
|
500
|
-
>`
|
|
568
|
+
return html`<span class="pkt-combobox__value">${option.prefix || ''} ${option.value}</span>`
|
|
501
569
|
case 'value':
|
|
502
|
-
return html`<span class="pkt-combobox__value"
|
|
503
|
-
>${option.value}</span
|
|
504
|
-
>`
|
|
570
|
+
return html`<span class="pkt-combobox__value">${option.value}</span>`
|
|
505
571
|
case 'label':
|
|
506
572
|
default:
|
|
507
|
-
return html`<span class="pkt-combobox__value"
|
|
508
|
-
>${option.label || option.value}</span
|
|
509
|
-
>`
|
|
573
|
+
return html`<span class="pkt-combobox__value">${option.label || option.value}</span>`
|
|
510
574
|
}
|
|
511
575
|
}
|
|
512
576
|
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { PktCombobox } from './combobox'
|
|
2
2
|
|
|
3
|
-
export type {
|
|
3
|
+
export type {
|
|
4
|
+
IPktCombobox,
|
|
5
|
+
IPktComboboxAttributes,
|
|
6
|
+
IPktComboboxOption,
|
|
7
|
+
TPktComboboxTagPlacement,
|
|
8
|
+
} from './combobox'
|
|
4
9
|
|
|
5
10
|
export { PktCombobox }
|
|
6
11
|
export default PktCombobox
|
package/src/components/index.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface IPktListbox extends IPktStringsProps<'listbox'> {
|
|
|
46
46
|
|
|
47
47
|
export class PktListbox extends PktElement<IPktListbox> implements IPktListbox {
|
|
48
48
|
@property({ type: String }) id: string = uuidish()
|
|
49
|
+
|
|
50
|
+
@state() private _listboxId: string = uuidish()
|
|
49
51
|
@property({ type: String }) label: string | null = null
|
|
50
52
|
@property({ type: Array }) options: IPktComboboxOption[] = []
|
|
51
53
|
@property({ type: Boolean, reflect: true, attribute: 'is-open' }) isOpen: boolean = false
|
|
@@ -69,6 +71,12 @@ export class PktListbox extends PktElement<IPktListbox> implements IPktListbox {
|
|
|
69
71
|
// Lifecycle methods
|
|
70
72
|
connectedCallback(): void {
|
|
71
73
|
super.connectedCallback()
|
|
74
|
+
const hostId = this.getAttribute('id')
|
|
75
|
+
if (hostId) {
|
|
76
|
+
this.adoptHostId(hostId)
|
|
77
|
+
} else if (this.id) {
|
|
78
|
+
this._listboxId = this.id
|
|
79
|
+
}
|
|
72
80
|
if (this.includeSearch && !this.searchValue) {
|
|
73
81
|
this.searchValue = ''
|
|
74
82
|
}
|
|
@@ -84,18 +92,14 @@ export class PktListbox extends PktElement<IPktListbox> implements IPktListbox {
|
|
|
84
92
|
this.typeahead.reset()
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
updated(changedProperties: PropertyValues) {
|
|
88
|
-
if (changedProperties.has('options') || changedProperties.has('searchValue')) {
|
|
89
|
-
this.filterOptions()
|
|
90
|
-
}
|
|
91
|
-
super.updated(changedProperties)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
95
|
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
|
|
95
96
|
if (name === 'options' || name === 'search-value') {
|
|
96
97
|
this.filterOptions()
|
|
97
98
|
}
|
|
98
99
|
super.attributeChangedCallback(name, _old, value)
|
|
100
|
+
if (name === 'id' && value) {
|
|
101
|
+
this.adoptHostId(value)
|
|
102
|
+
}
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
// Render methods
|
|
@@ -103,9 +107,22 @@ export class PktListbox extends PktElement<IPktListbox> implements IPktListbox {
|
|
|
103
107
|
return this._filteredOptions.length > 0 || this.options.length > 0
|
|
104
108
|
}
|
|
105
109
|
|
|
110
|
+
private adoptHostId(hostId: string): void {
|
|
111
|
+
this._listboxId = hostId
|
|
112
|
+
this.removeAttribute('id')
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected willUpdate(changed: PropertyValues): void {
|
|
116
|
+
super.willUpdate(changed)
|
|
117
|
+
if (changed.has('options') || changed.has('searchValue')) {
|
|
118
|
+
this.filterOptions()
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
106
122
|
render() {
|
|
107
123
|
return html`
|
|
108
124
|
<div
|
|
125
|
+
id=${this._listboxId}
|
|
109
126
|
class=${classMap({
|
|
110
127
|
'pkt-listbox': true,
|
|
111
128
|
'pkt-listbox__open': this.isOpen,
|
|
@@ -129,20 +146,21 @@ export class PktListbox extends PktElement<IPktListbox> implements IPktListbox {
|
|
|
129
146
|
`
|
|
130
147
|
}
|
|
131
148
|
|
|
132
|
-
private renderCheckboxOrCheckIcon(option: IPktComboboxOption
|
|
149
|
+
private renderCheckboxOrCheckIcon(option: IPktComboboxOption) {
|
|
133
150
|
return this.isMultiSelect
|
|
134
151
|
? html`
|
|
135
152
|
<input
|
|
136
153
|
class="pkt-input-check__input-checkbox"
|
|
137
154
|
type="checkbox"
|
|
138
|
-
|
|
155
|
+
inert
|
|
156
|
+
aria-hidden="true"
|
|
139
157
|
tabindex="-1"
|
|
140
158
|
value=${option.value}
|
|
141
159
|
@change=${(e: Event) => {
|
|
142
160
|
e.stopPropagation()
|
|
143
161
|
}}
|
|
144
162
|
.checked=${option.selected}
|
|
145
|
-
|
|
163
|
+
?checked=${option.selected}
|
|
146
164
|
?disabled=${this.disabled || option.disabled || (this.maxIsReached && !option.selected)}
|
|
147
165
|
/>
|
|
148
166
|
`
|
|
@@ -186,29 +204,38 @@ export class PktListbox extends PktElement<IPktListbox> implements IPktListbox {
|
|
|
186
204
|
data-index=${index}
|
|
187
205
|
data-value=${option.value}
|
|
188
206
|
data-selected=${option.selected ? 'true' : 'false'}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
207
|
+
data-disabled=${ifDefined(
|
|
208
|
+
this.disabled || option.disabled || (this.maxIsReached && !option.selected)
|
|
209
|
+
? 'true'
|
|
210
|
+
: undefined,
|
|
211
|
+
)}
|
|
212
|
+
aria-disabled=${
|
|
213
|
+
this.disabled || option.disabled || (this.maxIsReached && !option.selected)
|
|
214
|
+
? 'true'
|
|
215
|
+
: 'false'
|
|
216
|
+
}
|
|
197
217
|
role="option"
|
|
198
|
-
id=${`${this.
|
|
218
|
+
id=${`${this._listboxId}-${index}`}
|
|
199
219
|
>
|
|
200
|
-
${this.renderCheckboxOrCheckIcon(option
|
|
201
|
-
<span
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
220
|
+
${this.renderCheckboxOrCheckIcon(option)}
|
|
221
|
+
<span
|
|
222
|
+
class="pkt-listbox__option-label"
|
|
223
|
+
id=${this._listboxId + '-option-label-' + index}
|
|
224
|
+
>
|
|
225
|
+
${
|
|
226
|
+
option.prefix
|
|
227
|
+
? html`<span class="pkt-listbox__option-prefix">${option.prefix}</span>`
|
|
228
|
+
: nothing
|
|
229
|
+
}
|
|
205
230
|
${option.label || option.value}
|
|
206
231
|
</span>
|
|
207
|
-
${
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
232
|
+
${
|
|
233
|
+
option.description
|
|
234
|
+
? html`<span class="pkt-listbox__option-description pkt-txt-14-light"
|
|
235
|
+
>${option.description}</span
|
|
236
|
+
>`
|
|
237
|
+
: nothing
|
|
238
|
+
}
|
|
212
239
|
</li>
|
|
213
240
|
`,
|
|
214
241
|
)}
|
|
@@ -159,7 +159,7 @@ describe('pkt-radiobutton', () => {
|
|
|
159
159
|
|
|
160
160
|
const helptext = radiobutton.querySelector('.pkt-input-check__input-helptext')
|
|
161
161
|
expect(helptext).toBeInTheDocument()
|
|
162
|
-
expect(helptext?.textContent).toBe('This is helpful information')
|
|
162
|
+
expect(helptext?.textContent?.trim()).toBe('This is helpful information')
|
|
163
163
|
})
|
|
164
164
|
|
|
165
165
|
test('handles error state correctly', async () => {
|
|
@@ -75,25 +75,42 @@ export class PktRadioButton extends PktInputElement<Props> {
|
|
|
75
75
|
|
|
76
76
|
const tags = () => {
|
|
77
77
|
return html`
|
|
78
|
-
${
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
${
|
|
79
|
+
this.tagText
|
|
80
|
+
? html`<span class=${tagClasses + ' pkt-tag--gray'}>${this.tagText}</span>`
|
|
81
|
+
: nothing
|
|
82
|
+
}
|
|
83
|
+
${
|
|
84
|
+
this.optionalTag
|
|
85
|
+
? html`<span class=${tagClasses + ' pkt-tag--blue-light'}
|
|
86
|
+
>${this.optionalText ?? formStrings.optional}</span
|
|
87
|
+
>`
|
|
88
|
+
: nothing
|
|
89
|
+
}
|
|
90
|
+
${
|
|
91
|
+
this.requiredTag
|
|
92
|
+
? html`<span class=${tagClasses + ' pkt-tag--beige'}
|
|
93
|
+
>${this.requiredText ?? formStrings.required}</span
|
|
94
|
+
>`
|
|
95
|
+
: nothing
|
|
96
|
+
}
|
|
87
97
|
`
|
|
88
98
|
}
|
|
89
99
|
|
|
100
|
+
const labelId = this.id + '-check-label'
|
|
101
|
+
const helptextId = this.id + '-check-helptext'
|
|
102
|
+
|
|
90
103
|
const labelAndHelptext = () => {
|
|
91
104
|
return html`
|
|
92
105
|
<label class=${labelClasses} for=${this.id + '-internal'}>
|
|
93
|
-
|
|
94
|
-
${
|
|
95
|
-
|
|
96
|
-
|
|
106
|
+
<span id=${labelId}>${this.label} ${tags()}</span>
|
|
107
|
+
${
|
|
108
|
+
this.checkHelptext
|
|
109
|
+
? html`<div id=${helptextId} class="pkt-input-check__input-helptext">
|
|
110
|
+
${this.checkHelptext}
|
|
111
|
+
</div>`
|
|
112
|
+
: nothing
|
|
113
|
+
}
|
|
97
114
|
</label>
|
|
98
115
|
`
|
|
99
116
|
}
|
|
@@ -114,6 +131,8 @@ export class PktRadioButton extends PktInputElement<Props> {
|
|
|
114
131
|
@blur=${this.onBlur}
|
|
115
132
|
@focus=${this.onFocus}
|
|
116
133
|
?checked=${this.checked}
|
|
134
|
+
aria-labelledby=${this.label ? labelId : nothing}
|
|
135
|
+
aria-describedby=${this.checkHelptext ? helptextId : nothing}
|
|
117
136
|
/>
|
|
118
137
|
${labelAndHelptext()}
|
|
119
138
|
</div>
|