@oslokommune/punkt-elements 17.0.9 → 17.1.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 +36 -0
- package/dist/{accordion-CbXDxOho.cjs → accordion-B3GBlvUC.cjs} +2 -2
- package/dist/{accordion-DEUHQ4JD.js → accordion-BeLvLPrB.js} +12 -11
- package/dist/{alert-CkUnbPdA.cjs → alert-Cs4Rmq_J.cjs} +1 -1
- package/dist/{alert-CbtVGrHD.js → alert-DKK_Tb9J.js} +5 -5
- package/dist/{async-directive-Cw_7VW_-.js → async-directive-BPf6K-V2.js} +9 -6
- package/dist/async-directive-Da7TJpnq.cjs +1 -0
- package/dist/{backlink-DDGlLzk9.cjs → backlink-CMmrKTe4.cjs} +1 -1
- package/dist/{backlink-wMNLuQ0X.js → backlink-D1y26pj3.js} +3 -3
- package/dist/base-elements/input-element.d.ts +9 -4
- package/dist/booleanish-BF8l4scU.cjs +1 -0
- package/dist/{booleanish-MfutDT_4.js → booleanish-OQJTYSrw.js} +8 -1
- package/dist/{breadcrumbs-D3tfWqvC.cjs → breadcrumbs-B6JGhJgC.cjs} +1 -1
- package/dist/{breadcrumbs-CVTLNNsL.js → breadcrumbs-CcTQ3Grm.js} +2 -2
- package/dist/{button-Cod17rKo.js → button-2-OSPJkj.js} +10 -10
- package/dist/{button-gvDXtBMC.cjs → button-CE97NQIk.cjs} +2 -2
- package/dist/{calendar-BVXW8DqC.cjs → calendar-kn4SrtJy.cjs} +1 -1
- package/dist/{calendar-CjC4qlh6.js → calendar-pOW3R5Pw.js} +4 -4
- package/dist/{card-sydjK3Bb.cjs → card-CETDFkxR.cjs} +3 -3
- package/dist/{card-sDOe54um.js → card-zQ0eKRMV.js} +9 -8
- package/dist/{checkbox-DJDphhqz.js → checkbox-CH7NpINF.js} +12 -12
- package/dist/{checkbox-8uxbeU30.cjs → checkbox-CQNr_sEg.cjs} +8 -8
- package/dist/{class-map-CCZN5uS3.cjs → class-map-BeimMvPv.cjs} +1 -1
- package/dist/{class-map-CAQCRB1W.js → class-map-CnWCnBSX.js} +2 -2
- package/dist/combobox-Bvg0UO9w.cjs +139 -0
- package/dist/{combobox-CzZIf5hd.js → combobox-CRdYYlxu.js} +68 -37
- package/dist/components/accordion/accordion.d.ts +1 -1
- package/dist/components/accordion/accordionitem.d.ts +1 -1
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/backlink/backlink.d.ts +1 -1
- package/dist/components/breadcrumbs/breadcrumbs.d.ts +1 -1
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/card/card.d.ts +9 -9
- package/dist/components/checkbox/checkbox.d.ts +1 -1
- package/dist/components/combobox/combobox.d.ts +1 -1
- package/dist/components/consent/consent.d.ts +1 -1
- package/dist/components/datepicker/date-tags.d.ts +1 -1
- package/dist/components/datepicker/datepicker-multiple.d.ts +1 -1
- package/dist/components/datepicker/datepicker-popup.d.ts +1 -1
- package/dist/components/datepicker/datepicker-range.d.ts +1 -1
- package/dist/components/datepicker/datepicker-single.d.ts +1 -1
- package/dist/components/datepicker/datepicker-utils.d.ts +2 -2
- package/dist/components/datepicker/datepicker.d.ts +5 -5
- package/dist/components/fileupload/fileupload-base.d.ts +1 -1
- package/dist/components/fileupload/fileupload-renderers.d.ts +2 -2
- package/dist/components/fileupload/fileupload.d.ts +3 -1
- package/dist/components/header/header-user-menu.d.ts +1 -1
- package/dist/components/header/header.d.ts +1 -1
- package/dist/components/helptext/helptext.d.ts +1 -1
- package/dist/components/icon/icon.d.ts +1 -1
- package/dist/components/input-wrapper/input-wrapper.d.ts +1 -1
- package/dist/components/input-wrapper/tri-state.test.d.ts +0 -0
- package/dist/components/input-wrapper/wrapper-parity.test.d.ts +1 -0
- package/dist/components/link/link.d.ts +1 -1
- package/dist/components/linkcard/linkcard.d.ts +1 -1
- package/dist/components/listbox/listbox.d.ts +1 -1
- package/dist/components/loader/loader.d.ts +1 -1
- package/dist/components/messagebox/messagebox.d.ts +1 -1
- package/dist/components/modal/modal.d.ts +1 -1
- package/dist/components/progressbar/progressbar.d.ts +1 -1
- package/dist/components/radiobutton/radiobutton.d.ts +1 -1
- package/dist/components/searchinput/searchinput.d.ts +1 -1
- package/dist/components/select/select.d.ts +1 -1
- package/dist/components/select/select.test.d.ts +2 -2
- package/dist/components/tabs/tabitem.d.ts +1 -1
- package/dist/components/tabs/tabs.d.ts +1 -1
- package/dist/components/tag/tag.d.ts +1 -1
- package/dist/components/textarea/textarea.d.ts +3 -2
- package/dist/components/textarea/textarea.test.d.ts +2 -2
- package/dist/components/textinput/textinput.d.ts +1 -1
- package/dist/components/textinput/textinput.test.d.ts +2 -2
- package/dist/components/timepicker/timepicker.d.ts +1 -1
- package/dist/{consent--Nw43WFU.cjs → consent-CyY3RyoC.cjs} +1 -1
- package/dist/{consent-B2hSTCGC.js → consent-DZEkhgJ1.js} +2 -2
- package/dist/{consume-BiOhrhEy.js → consume-BDnMAp2g.js} +6 -6
- package/dist/{consume-DKf5SmP9.cjs → consume-Le4HztPo.cjs} +1 -1
- package/dist/converters-BJ9Fe0kW.cjs +1 -0
- package/dist/{converters-BBfsEJV5.js → converters-CkjLSYpa.js} +200 -202
- package/dist/{datepicker-BMuplkDE.js → datepicker-ButIas85.js} +46 -22
- package/dist/{datepicker-DrB54Rhc.cjs → datepicker-VTqSDQk5.cjs} +7 -8
- package/dist/dialog-polyfill.esm-B-g35ove.cjs +1 -0
- package/dist/{dialog-polyfill.esm-ChZRzCVh.js → dialog-polyfill.esm-DHSvqor8.js} +2 -2
- package/dist/{directive-helpers-BshFvt7A.cjs → directive-helpers-BlAV5EQD.cjs} +1 -1
- package/dist/{directive-helpers-DIF5DwN4.js → directive-helpers-CI8YZqku.js} +2 -2
- package/dist/directives/slot-content.d.ts +1 -1
- package/dist/docs/backlink.d.ts +1 -1
- package/dist/docs/components/accordion.d.ts +1 -1
- package/dist/docs/components/alert.d.ts +1 -1
- package/dist/docs/components/backlink.d.ts +1 -1
- package/dist/docs/components/breadcrumbs.d.ts +1 -1
- package/dist/docs/components/button.d.ts +1 -1
- package/dist/docs/components/calendar.d.ts +1 -1
- package/dist/docs/components/card.d.ts +1 -1
- package/dist/docs/components/checkbox.d.ts +1 -1
- package/dist/docs/components/combobox.d.ts +1 -1
- package/dist/docs/components/comboboxdemo.d.ts +1 -1
- package/dist/docs/components/consent.d.ts +1 -1
- package/dist/docs/components/datepicker.d.ts +1 -1
- package/dist/docs/components/datepickermultiple.d.ts +1 -1
- package/dist/docs/components/event-log.d.ts +1 -1
- package/dist/docs/components/fileupload.d.ts +1 -1
- package/dist/docs/components/footer.d.ts +1 -1
- package/dist/docs/components/form-sizes.d.ts +1 -1
- package/dist/docs/components/form.d.ts +1 -1
- package/dist/docs/components/header-full.d.ts +1 -1
- package/dist/docs/components/header-global-full.d.ts +1 -1
- package/dist/docs/components/header-global.d.ts +1 -1
- package/dist/docs/components/header-menu.d.ts +1 -1
- package/dist/docs/components/header.d.ts +1 -1
- package/dist/docs/components/heading.d.ts +1 -1
- package/dist/docs/components/icon.d.ts +1 -1
- package/dist/docs/components/input-wrapper.d.ts +1 -1
- package/dist/docs/components/link.d.ts +1 -1
- package/dist/docs/components/linkcard.d.ts +1 -1
- package/dist/docs/components/loader.d.ts +1 -1
- package/dist/docs/components/messagebox.d.ts +1 -1
- package/dist/docs/components/modal.d.ts +1 -1
- package/dist/docs/components/progressbar.d.ts +1 -1
- package/dist/docs/components/radiobutton.d.ts +1 -1
- package/dist/docs/components/searchinput.d.ts +1 -1
- package/dist/docs/components/select.d.ts +1 -1
- package/dist/docs/components/slots.d.ts +1 -1
- package/dist/docs/components/tabs.d.ts +1 -1
- package/dist/docs/components/tag.d.ts +1 -1
- package/dist/docs/components/textarea.d.ts +1 -1
- package/dist/docs/components/textinput.d.ts +1 -1
- package/dist/docs/components/timepicker.d.ts +1 -1
- package/dist/docs/components/verifisering.d.ts +15 -0
- package/dist/docs/header.d.ts +1 -1
- package/dist/docs/punkt-docs.d.ts +1 -1
- package/dist/element-BA8v6S7z.cjs +2 -0
- package/dist/{element-BoJrqauR.js → element-ChsFgr_B.js} +19 -16
- package/dist/{element-with-slot-BY7Jc-q8.cjs → element-with-slot-Bj7nm7fO.cjs} +1 -1
- package/dist/{element-with-slot-mXLg8gg8.js → element-with-slot-BvG9ctC-.js} +2 -2
- package/dist/fetch-header-footer-data-CUKV0biS.cjs +1 -0
- package/dist/{fileupload-CGKUJrlj.cjs → fileupload-BeWiTAoW.cjs} +8 -8
- package/dist/{fileupload-D4gQN64T.js → fileupload-D5IQ9HDe.js} +25 -20
- package/dist/{footer-Ber-jqM1.cjs → footer-DbYQUYNR.cjs} +1 -1
- package/dist/{footer-BIrJeRuZ.js → footer-DrR64HiK.js} +6 -6
- package/dist/{header-BFk1an4z.cjs → header-ClgIac6n.cjs} +12 -12
- package/dist/{header-Deg_LGJo.js → header-KvJ1mGwA.js} +48 -38
- package/dist/{header-menu-BHqK4QA6.cjs → header-menu-BAsUNKSx.cjs} +3 -3
- package/dist/{header-menu-GFtJKM4j.js → header-menu-hVPXsb8g.js} +10 -8
- package/dist/{heading-DyJpBFT2.cjs → heading-CFmiSuCQ.cjs} +1 -1
- package/dist/{heading-CD0PhL8g.js → heading-g-_IUB5T.js} +2 -2
- package/dist/{helptext-Cq5rNaAC.cjs → helptext-CTtKJcML.cjs} +4 -1
- package/dist/{helptext-C_brWCIT.js → helptext-DZng1D5i.js} +18 -14
- package/dist/{icon-B8PVq2O6.cjs → icon-9iwd2F2l.cjs} +1 -1
- package/dist/{icon-DGFEIlco.js → icon-D-LA22xD.js} +2 -2
- package/dist/if-defined-B89_ZeCj.cjs +1 -0
- package/dist/if-defined-CGzIlZ9-.js +5 -0
- package/dist/{input-element-C0RnXi-J.js → input-element-DBMNBplu.js} +155 -129
- package/dist/{input-element-B0qLbEdF.cjs → input-element-DYmQw-fH.cjs} +1 -1
- package/dist/input-wrapper-BmxNvznr.js +128 -0
- package/dist/{input-wrapper-BweP6t3O.js → input-wrapper-D-fPaTbY.js} +2 -2
- package/dist/{input-wrapper-wh3fWIhk.cjs → input-wrapper-D5PR0rJn.cjs} +1 -1
- package/dist/input-wrapper-Q-B-aBeb.cjs +54 -0
- package/dist/{link-FmkQvSQL.js → link-D-Eqm5KH.js} +8 -7
- package/dist/{link-Geze7498.cjs → link-DK7gvmnj.cjs} +2 -2
- package/dist/{linkcard-6LCXRTR-.cjs → linkcard-B29cmZqh.cjs} +1 -1
- package/dist/{linkcard-BKdT0zU_.js → linkcard-zqbg7Oqm.js} +4 -4
- package/dist/{listbox-89wMuQVK.cjs → listbox-Dc4rGdPi.cjs} +2 -2
- package/dist/{listbox-LdyWPu7W.js → listbox-va_E8afs.js} +6 -10
- package/dist/{loader-CgW4Vst3.cjs → loader-CMUkVJmE.cjs} +1 -1
- package/dist/{loader-BA-i8CaQ.js → loader-D_RDICnw.js} +4 -4
- package/dist/{messagebox-VD5oEZ19.cjs → messagebox-Cv8LaSxv.cjs} +1 -1
- package/dist/{messagebox-fiaAyHLG.js → messagebox-Cw-OWDya.js} +7 -6
- package/dist/{modal-CPnFteYS.cjs → modal-BtInv031.cjs} +1 -1
- package/dist/{modal-xNK_wpHk.js → modal-D0t_X2dX.js} +6 -6
- 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-DMxYwnnj.js → pkt-options-controller-B77vH3PE.js} +1 -1
- package/dist/{pkt-options-controller-CPrfqepu.cjs → pkt-options-controller-C5EBX2T0.cjs} +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-mtjtbpCO.js → progressbar-CbcfLerF.js} +6 -6
- package/dist/progressbar-D5MmnQId.cjs +17 -0
- package/dist/{radiobutton-C6htwDrT.js → radiobutton-BUnf3EPi.js} +8 -8
- package/dist/{radiobutton-DjWBoSHv.cjs → radiobutton-DpsO1F-8.cjs} +4 -4
- package/dist/ref-CQoEIIp0.cjs +1 -0
- package/dist/{ref-WGS0u7i2.js → ref-CWD6PjVd.js} +9 -7
- package/dist/{repeat-BH0cL82q.js → repeat-C02g7d5S.js} +13 -12
- package/dist/{repeat-DRnLAg0O.cjs → repeat-sY6B7fAT.cjs} +1 -1
- package/dist/{searchinput-DGjeN1YI.cjs → searchinput-Bh975rjX.cjs} +1 -1
- package/dist/{searchinput-CfUVfpqL.js → searchinput-DwnjsvJ1.js} +4 -4
- package/dist/select-BOJi6H6T.cjs +49 -0
- package/dist/{select-CsBXXbLP.js → select-DMre80qs.js} +24 -20
- package/dist/shared-types/booleanish.d.ts +11 -0
- package/dist/shared-types/index.d.ts +1 -1
- package/dist/shared-utils/date-utils.d.ts +16 -16
- package/dist/shared-utils/datepicker-utils.d.ts +11 -11
- package/dist/shared-utils/device-utils.d.ts +1 -1
- package/dist/shared-utils/forms/described-by.d.ts +24 -0
- package/dist/shared-utils/utils.d.ts +2 -2
- package/dist/shared-utils/value-utils.d.ts +2 -2
- package/dist/{tabs-q62MK_-r.cjs → tabs-Cf9ai0FD.cjs} +2 -2
- package/dist/{tabs-BqrKLCjd.js → tabs-TvxgbrCF.js} +7 -7
- package/dist/{tag-XE-VRxT2.cjs → tag-CzojNU7z.cjs} +1 -1
- package/dist/{tag-BTiTXyf5.js → tag-Ds6n1LIs.js} +5 -5
- package/dist/textarea-BydiLM9Z.cjs +49 -0
- package/dist/{textarea-Cm392CEI.js → textarea-vfzIcXhX.js} +36 -29
- package/dist/{textinput-TSPcBcih.js → textinput-CMP2jPP_.js} +30 -25
- package/dist/{textinput-DqJbTglM.cjs → textinput-DGdicNSx.cjs} +10 -10
- package/dist/{timepicker-DWbgTDKq.js → timepicker-ClwgYywH.js} +19 -14
- package/dist/{timepicker-CqFYMV8q.cjs → timepicker-DYrnLHqN.cjs} +5 -2
- package/dist/translations/no.json.d.ts +2 -1
- package/package.json +7 -4
- package/src/components/combobox/combobox.core.test.ts +2 -2
- package/src/components/combobox/combobox.ts +7 -1
- package/src/components/datepicker/datepicker-single.ts +1 -1
- package/src/components/datepicker/datepicker.ts +5 -3
- package/src/components/fileupload/fileupload.ts +6 -1
- package/src/components/helptext/helptext.ts +3 -0
- package/src/components/input-wrapper/input-wrapper.test.ts +3 -3
- package/src/components/input-wrapper/input-wrapper.ts +31 -29
- package/src/components/input-wrapper/tri-state.test.ts +91 -0
- package/src/components/input-wrapper/wrapper-parity.test.ts +141 -0
- package/src/components/select/select.test.ts +8 -7
- package/src/components/select/select.ts +13 -5
- package/src/components/textarea/textarea.test.ts +6 -6
- package/src/components/textarea/textarea.ts +20 -11
- package/src/components/textinput/textinput.test.ts +6 -6
- package/src/components/textinput/textinput.ts +17 -11
- package/src/components/timepicker/timepicker.ts +12 -5
- package/dist/async-directive-HLXOyE4A.cjs +0 -1
- package/dist/booleanish-B9Kf0nES.cjs +0 -1
- package/dist/combobox-BH9lym0-.cjs +0 -136
- package/dist/converters-Cd0fPEly.cjs +0 -1
- package/dist/dialog-polyfill.esm-J-GKtwrS.cjs +0 -1
- package/dist/element-DJzurxgt.cjs +0 -2
- package/dist/fetch-header-footer-data-xohWFmYC.cjs +0 -1
- package/dist/if-defined-D0bnetLJ.js +0 -5
- package/dist/if-defined-DrwbxQOj.cjs +0 -1
- package/dist/input-wrapper-DPkkiG7S.cjs +0 -57
- package/dist/input-wrapper-fWV5P4MN.js +0 -126
- package/dist/progressbar-BN5y20CC.cjs +0 -17
- package/dist/ref-DO7TzO4O.cjs +0 -1
- package/dist/select-CZvAW7W2.cjs +0 -50
- package/dist/textarea-sTcZxOIB.cjs +0 -47
|
@@ -47,6 +47,8 @@ export class PktHelptext extends PktElementWithSlot {
|
|
|
47
47
|
<button
|
|
48
48
|
class="pkt-link pkt-link--icon-right pkt-btn pkt-btn--small pkt-btn--tertiary pkt-btn--icon-right"
|
|
49
49
|
type="button"
|
|
50
|
+
aria-expanded=${this.isHelpTextOpen ? 'true' : 'false'}
|
|
51
|
+
aria-controls="${this.forId}-helptext-expandable"
|
|
50
52
|
@click=${toggleDropdown}
|
|
51
53
|
>
|
|
52
54
|
<pkt-icon
|
|
@@ -56,6 +58,7 @@ export class PktHelptext extends PktElementWithSlot {
|
|
|
56
58
|
<span class="pkt-btn__text">${unsafeHTML(this.helptextDropdownButton)}</span>
|
|
57
59
|
</button>
|
|
58
60
|
<div
|
|
61
|
+
id="${this.forId}-helptext-expandable"
|
|
59
62
|
class="${classMap({
|
|
60
63
|
'pkt-inputwrapper__helptext': true,
|
|
61
64
|
'pkt-inputwrapper__helptext-expandable-open': this.isHelpTextOpen,
|
|
@@ -322,7 +322,7 @@ describe('PktInputWrapper', () => {
|
|
|
322
322
|
|
|
323
323
|
expect(wrapper.hasError).toBe(true)
|
|
324
324
|
// Check for error-related classes based on actual component implementation
|
|
325
|
-
const inputWrapper = wrapper
|
|
325
|
+
const inputWrapper = wrapper
|
|
326
326
|
expect(inputWrapper?.classList.contains('pkt-inputwrapper--error')).toBe(true)
|
|
327
327
|
})
|
|
328
328
|
|
|
@@ -333,7 +333,7 @@ describe('PktInputWrapper', () => {
|
|
|
333
333
|
wrapper.size = 'small'
|
|
334
334
|
await wrapper.updateComplete
|
|
335
335
|
|
|
336
|
-
const inputWrapper = wrapper
|
|
336
|
+
const inputWrapper = wrapper
|
|
337
337
|
expect(inputWrapper?.classList.contains('pkt-inputwrapper--small')).toBe(true)
|
|
338
338
|
})
|
|
339
339
|
|
|
@@ -344,7 +344,7 @@ describe('PktInputWrapper', () => {
|
|
|
344
344
|
wrapper.size = 'xsmall'
|
|
345
345
|
await wrapper.updateComplete
|
|
346
346
|
|
|
347
|
-
const inputWrapper = wrapper
|
|
347
|
+
const inputWrapper = wrapper
|
|
348
348
|
expect(inputWrapper?.classList.contains('pkt-inputwrapper--xsmall')).toBe(true)
|
|
349
349
|
expect(inputWrapper?.classList.contains('pkt-inputwrapper--small')).toBe(false)
|
|
350
350
|
})
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { PktElementWithSlot } from '@/base-elements/element-with-slot'
|
|
2
2
|
import { slotContent } from '@/directives/slot-content'
|
|
3
|
+
import { counterStatusId, isCounterExceeded } from 'shared-utils/forms/described-by'
|
|
4
|
+
import strings from '@/translations/no.json'
|
|
3
5
|
import { ifDefined } from 'lit/directives/if-defined.js'
|
|
4
6
|
import { html, nothing, PropertyValues } from 'lit'
|
|
5
7
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js'
|
|
6
|
-
import { classMap } from 'lit/directives/class-map.js'
|
|
7
8
|
import { customElement, property } from 'lit/decorators.js'
|
|
8
9
|
import { ElementProps } from '@/types/typeUtils'
|
|
9
10
|
import { uuidish } from 'shared-utils/utils'
|
|
@@ -67,7 +68,7 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
67
68
|
@property({ type: Boolean }) inline: boolean = specs.props.inline.default
|
|
68
69
|
@property({ type: String }) ariaDescribedby: string | undefined = undefined
|
|
69
70
|
@property({ type: Boolean }) hasFieldset: boolean = specs.props.hasFieldset.default
|
|
70
|
-
@property({ type: String, reflect: true }) role: string | null =
|
|
71
|
+
@property({ type: String, reflect: true }) role: string | null = null
|
|
71
72
|
@property({ type: Boolean, reflect: true, converter: booleanishConverter })
|
|
72
73
|
useWrapper: Booleanish = specs.props.useWrapper.default
|
|
73
74
|
@property({ type: String, attribute: 'input-wrapper-size' })
|
|
@@ -75,24 +76,24 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
75
76
|
|
|
76
77
|
protected updated(changedProperties: PropertyValues): void {
|
|
77
78
|
super.updated(changedProperties)
|
|
79
|
+
this.classList.add('pkt-inputwrapper')
|
|
80
|
+
this.classList.toggle('pkt-inputwrapper--error', this.hasError)
|
|
81
|
+
this.classList.toggle('pkt-inputwrapper--disabled', this.disabled)
|
|
82
|
+
this.classList.toggle('pkt-inputwrapper--inline', this.inline)
|
|
83
|
+
this.classList.toggle('pkt-inputwrapper--medium', this.size === 'medium')
|
|
84
|
+
this.classList.toggle('pkt-inputwrapper--small', this.size === 'small')
|
|
85
|
+
this.classList.toggle('pkt-inputwrapper--xsmall', this.size === 'xsmall')
|
|
86
|
+
if (this.role) this.setAttribute('aria-labelledby', `${this.forId}-label`)
|
|
87
|
+
else this.removeAttribute('aria-labelledby')
|
|
78
88
|
}
|
|
79
89
|
|
|
80
|
-
// Speiler navigeringsmodus til
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
focusModality = new FocusModalityController(this
|
|
90
|
+
// Speiler navigeringsmodus til verten som data-navtype="pointer" (eller
|
|
91
|
+
// fjerner attributtet) så CSS kan skille dempet fokus (klikk) fra kraftig
|
|
92
|
+
// fokus (tab). Controlleren registrerer seg selv på hosten i konstruktøren,
|
|
93
|
+
// så vi trenger ikke beholde referansen.
|
|
94
|
+
focusModality = new FocusModalityController(this)
|
|
85
95
|
|
|
86
96
|
render() {
|
|
87
|
-
const classes = {
|
|
88
|
-
'pkt-inputwrapper': true,
|
|
89
|
-
'pkt-inputwrapper--error': this.hasError,
|
|
90
|
-
'pkt-inputwrapper--disabled': this.disabled,
|
|
91
|
-
'pkt-inputwrapper--inline': this.inline,
|
|
92
|
-
'pkt-inputwrapper--small': this.size === 'small',
|
|
93
|
-
'pkt-inputwrapper--xsmall': this.size === 'xsmall',
|
|
94
|
-
}
|
|
95
|
-
|
|
96
97
|
const tagClasses = 'pkt-tag pkt-tag--small pkt-tag--thin-text'
|
|
97
98
|
|
|
98
99
|
const describedBy = this.ariaDescribedby
|
|
@@ -129,19 +130,13 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
129
130
|
return html`<label
|
|
130
131
|
class="pkt-inputwrapper__label"
|
|
131
132
|
for="${this.forId}"
|
|
132
|
-
aria-describedby="${describedBy}"
|
|
133
133
|
id="${this.forId}-label"
|
|
134
134
|
@click=${this.handleLabelClick}
|
|
135
135
|
>${this.label}${tagElement()}</label
|
|
136
136
|
>`
|
|
137
137
|
}
|
|
138
138
|
} else {
|
|
139
|
-
return html`<label
|
|
140
|
-
for="${this.forId}"
|
|
141
|
-
class="pkt-sr-only"
|
|
142
|
-
aria-describedby="${describedBy}"
|
|
143
|
-
id="${this.forId}-label"
|
|
144
|
-
>
|
|
139
|
+
return html`<label for="${this.forId}" class="pkt-sr-only" id="${this.forId}-label">
|
|
145
140
|
${this.label}
|
|
146
141
|
</label>`
|
|
147
142
|
}
|
|
@@ -165,14 +160,21 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
165
160
|
}
|
|
166
161
|
|
|
167
162
|
const counterElement = () => {
|
|
168
|
-
if (this.counter)
|
|
169
|
-
|
|
163
|
+
if (!this.counter) return nothing
|
|
164
|
+
// Den synlige telleren er aria-hidden. Statusregionen under holdes tom
|
|
165
|
+
// til grensa er brutt, så skjermlesere bare varsler når det faktisk er noe å si.
|
|
166
|
+
const exceeded = isCounterExceeded(this.counterCurrent, this.counterMaxLength)
|
|
167
|
+
return html`<div class="pkt-input__counter" aria-hidden="true">
|
|
170
168
|
${this.counterError ? this.counterError : nothing} ${this.counterCurrent || 0}
|
|
171
169
|
${this.counterMaxLength ? `/${this.counterMaxLength}` : nothing}
|
|
170
|
+
</div>
|
|
171
|
+
<div class="pkt-sr-only" id=${counterStatusId(this.forId)} aria-live="polite">
|
|
172
|
+
${exceeded
|
|
173
|
+
? strings.forms.messages.counterExceeded
|
|
174
|
+
.replace('{current}', String(this.counterCurrent ?? 0))
|
|
175
|
+
.replace('{max}', String(this.counterMaxLength))
|
|
176
|
+
: ''}
|
|
172
177
|
</div>`
|
|
173
|
-
} else {
|
|
174
|
-
return nothing
|
|
175
|
-
}
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
const errorElement = () => {
|
|
@@ -214,7 +216,7 @@ export class PktInputWrapper extends PktElementWithSlot<Props> {
|
|
|
214
216
|
: html`<div class="pkt-inputwrapper__fieldset">${inputContent()}</div>`
|
|
215
217
|
}
|
|
216
218
|
|
|
217
|
-
return
|
|
219
|
+
return wrapperElement()
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
private toggleDropdown(e: CustomEvent) {
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { describe, expect, test } from 'vitest'
|
|
3
|
+
import { createElementTest } from '../../tests/test-framework'
|
|
4
|
+
import { CustomElementFor } from '../../tests/component-registry'
|
|
5
|
+
import '../textinput'
|
|
6
|
+
import '../combobox'
|
|
7
|
+
import '../timepicker'
|
|
8
|
+
|
|
9
|
+
const make = async (tag: any, config: Record<string, unknown>) => {
|
|
10
|
+
const { element } = await createElementTest<CustomElementFor<'pkt-textinput'>, never>(
|
|
11
|
+
tag,
|
|
12
|
+
|
|
13
|
+
config as any,
|
|
14
|
+
)
|
|
15
|
+
return element
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const counterOf = (el: Element) => el.querySelector('.pkt-input__counter')
|
|
19
|
+
const legendOf = (el: Element) => el.querySelector('.pkt-inputwrapper__legend')
|
|
20
|
+
const labelOf = (el: Element) => el.querySelector('.pkt-inputwrapper__label')
|
|
21
|
+
|
|
22
|
+
describe('counter: tri-state', () => {
|
|
23
|
+
test('vises av seg selv når det finnes en grense å telle mot', async () => {
|
|
24
|
+
const el = await make('pkt-textinput', { label: 'Etikett', counterMaxLength: 20 })
|
|
25
|
+
expect(counterOf(el)).not.toBeNull()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('vises ikke uten grense', async () => {
|
|
29
|
+
const el = await make('pkt-textinput', { label: 'Etikett' })
|
|
30
|
+
expect(counterOf(el)).toBeNull()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('kan slås av eksplisitt selv om det finnes en grense', async () => {
|
|
34
|
+
const el = await make('pkt-textinput', {
|
|
35
|
+
label: 'Etikett',
|
|
36
|
+
counterMaxLength: 20,
|
|
37
|
+
counter: 'false',
|
|
38
|
+
})
|
|
39
|
+
expect(counterOf(el)).toBeNull()
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test('kan slås på eksplisitt', async () => {
|
|
43
|
+
const el = await make('pkt-textinput', { label: 'Etikett', counter: '' })
|
|
44
|
+
expect(counterOf(el)).not.toBeNull()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('combobox: utledes fra multiple + maxlength', async () => {
|
|
48
|
+
const auto = await make('pkt-combobox', { label: 'Etikett', multiple: '', maxlength: 3 })
|
|
49
|
+
expect(counterOf(auto)).not.toBeNull()
|
|
50
|
+
|
|
51
|
+
const off = await make('pkt-combobox', {
|
|
52
|
+
label: 'Etikett',
|
|
53
|
+
multiple: '',
|
|
54
|
+
maxlength: 3,
|
|
55
|
+
counter: 'false',
|
|
56
|
+
})
|
|
57
|
+
expect(counterOf(off)).toBeNull()
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
describe('hasFieldset: tri-state', () => {
|
|
62
|
+
test('label som standard', async () => {
|
|
63
|
+
const el = await make('pkt-textinput', { label: 'Etikett' })
|
|
64
|
+
expect(labelOf(el)).not.toBeNull()
|
|
65
|
+
expect(legendOf(el)).toBeNull()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test('legend når den settes eksplisitt', async () => {
|
|
69
|
+
const el = await make('pkt-textinput', { label: 'Etikett', hasFieldset: '' })
|
|
70
|
+
expect(legendOf(el)).not.toBeNull()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('combobox uten tekstfelt utleder fieldset', async () => {
|
|
74
|
+
const el = await make('pkt-combobox', { label: 'Etikett' })
|
|
75
|
+
expect(legendOf(el)).not.toBeNull()
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('timepicker: label som standard, legend når satt', async () => {
|
|
79
|
+
const standard = await make('pkt-timepicker', { label: 'Etikett' })
|
|
80
|
+
expect(legendOf(standard)).toBeNull()
|
|
81
|
+
|
|
82
|
+
const medFieldset = await make('pkt-timepicker', { label: 'Etikett', hasFieldset: '' })
|
|
83
|
+
expect(legendOf(medFieldset)).not.toBeNull()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('combobox kan overstyre utledningen til label', async () => {
|
|
87
|
+
const el = await make('pkt-combobox', { label: 'Etikett', hasFieldset: 'false' })
|
|
88
|
+
expect(legendOf(el)).toBeNull()
|
|
89
|
+
expect(labelOf(el)).not.toBeNull()
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { describe, expect, test } from 'vitest'
|
|
4
|
+
import wrapperSpec from 'componentSpecs/input-wrapper.json'
|
|
5
|
+
|
|
6
|
+
const ROOT = join(__dirname, '../../../../..')
|
|
7
|
+
|
|
8
|
+
// OBS! Denne testen sjekker også React-komponentene.
|
|
9
|
+
|
|
10
|
+
const COMPONENTS = {
|
|
11
|
+
textinput: {
|
|
12
|
+
elements: 'packages/elements/src/components/textinput/textinput.ts',
|
|
13
|
+
react: 'packages/react/src/components/textinput/Textinput.tsx',
|
|
14
|
+
},
|
|
15
|
+
textarea: {
|
|
16
|
+
elements: 'packages/elements/src/components/textarea/textarea.ts',
|
|
17
|
+
react: 'packages/react/src/components/textarea/Textarea.tsx',
|
|
18
|
+
},
|
|
19
|
+
select: {
|
|
20
|
+
elements: 'packages/elements/src/components/select/select.ts',
|
|
21
|
+
react: 'packages/react/src/components/select/Select.tsx',
|
|
22
|
+
},
|
|
23
|
+
combobox: {
|
|
24
|
+
elements: 'packages/elements/src/components/combobox/combobox.ts',
|
|
25
|
+
react: 'packages/react/src/components/combobox/Combobox.tsx',
|
|
26
|
+
},
|
|
27
|
+
datepicker: {
|
|
28
|
+
elements: 'packages/elements/src/components/datepicker/datepicker.ts',
|
|
29
|
+
react: 'packages/react/src/components/datepicker/Datepicker.tsx',
|
|
30
|
+
},
|
|
31
|
+
timepicker: {
|
|
32
|
+
elements: 'packages/elements/src/components/timepicker/timepicker.ts',
|
|
33
|
+
react: 'packages/react/src/components/timepicker/Timepicker.tsx',
|
|
34
|
+
},
|
|
35
|
+
fileupload: {
|
|
36
|
+
elements: 'packages/elements/src/components/fileupload/fileupload.ts',
|
|
37
|
+
react: 'packages/react/src/components/fileupload/FileUpload.tsx',
|
|
38
|
+
},
|
|
39
|
+
} as const
|
|
40
|
+
|
|
41
|
+
// Props som med vilje ikke videresendes, med begrunnelse.
|
|
42
|
+
const OMITTED: Record<string, Partial<Record<keyof typeof COMPONENTS, string>>> = {
|
|
43
|
+
counter: {
|
|
44
|
+
select: 'ingenting å telle i en select',
|
|
45
|
+
timepicker: 'ingenting å telle',
|
|
46
|
+
fileupload: 'ingenting å telle',
|
|
47
|
+
},
|
|
48
|
+
counterCurrent: {
|
|
49
|
+
select: 'se counter',
|
|
50
|
+
timepicker: 'se counter',
|
|
51
|
+
fileupload: 'se counter',
|
|
52
|
+
},
|
|
53
|
+
counterMaxLength: {
|
|
54
|
+
select: 'se counter',
|
|
55
|
+
timepicker: 'se counter',
|
|
56
|
+
fileupload: 'se counter',
|
|
57
|
+
},
|
|
58
|
+
counterPosition: {
|
|
59
|
+
textinput: 'teller over feltet er aldri støttet',
|
|
60
|
+
textarea: 'teller over feltet er aldri støttet',
|
|
61
|
+
select: 'teller over feltet er aldri støttet',
|
|
62
|
+
combobox: 'teller over feltet er aldri støttet',
|
|
63
|
+
datepicker: 'teller over feltet er aldri støttet',
|
|
64
|
+
timepicker: 'teller over feltet er aldri støttet',
|
|
65
|
+
fileupload: 'teller over feltet er aldri støttet',
|
|
66
|
+
},
|
|
67
|
+
hasFieldset: {
|
|
68
|
+
fileupload: 'ikke støttet',
|
|
69
|
+
},
|
|
70
|
+
inline: { fileupload: 'ikke et enkelt felt på en linje' },
|
|
71
|
+
useWrapper: { fileupload: 'wrapperen rendres bare når label er satt' },
|
|
72
|
+
ariaDescribedby: { combobox: 'ikke implementert ennå', fileupload: 'ikke implementert ennå' },
|
|
73
|
+
errorMessage: { fileupload: 'egen feilvisning' },
|
|
74
|
+
size: { fileupload: 'har ikke størrelsesvarianter' },
|
|
75
|
+
role: {},
|
|
76
|
+
forId: {},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Navn som ikke er egne props på wrapper-kallet, men beregnes eller heter noe annet.
|
|
80
|
+
const NOT_A_FORWARDED_PROP = new Set(['forId', 'role'])
|
|
81
|
+
|
|
82
|
+
const read = (rel: string) => readFileSync(join(ROOT, rel), 'utf8')
|
|
83
|
+
|
|
84
|
+
/** Henter ut wrapper-kallet fra kildekoden, der propsene videresendes. */
|
|
85
|
+
const wrapperCall = (source: string, framework: 'elements' | 'react') => {
|
|
86
|
+
const open = framework === 'elements' ? '<pkt-input-wrapper' : '<PktInputWrapper'
|
|
87
|
+
const start = source.indexOf(open)
|
|
88
|
+
if (start < 0) return ''
|
|
89
|
+
// Kallet slutter ved første '>' som avslutter åpningstaggen
|
|
90
|
+
const rest = source.slice(start)
|
|
91
|
+
const end = rest.search(/\n\s*>/)
|
|
92
|
+
return end < 0 ? rest.slice(0, 4000) : rest.slice(0, end)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const forwards = (block: string, prop: string) =>
|
|
96
|
+
new RegExp(`[.?]?${prop}\\s*=`).test(block) || new RegExp(`\\b${prop}\\b`).test(block)
|
|
97
|
+
|
|
98
|
+
const wrapperProps = Object.keys(wrapperSpec.props).filter((p) => !NOT_A_FORWARDED_PROP.has(p))
|
|
99
|
+
|
|
100
|
+
describe('input-wrapper: paritet mellom rammeverk', () => {
|
|
101
|
+
for (const [name, paths] of Object.entries(COMPONENTS)) {
|
|
102
|
+
describe(name, () => {
|
|
103
|
+
const elements = wrapperCall(read(paths.elements), 'elements')
|
|
104
|
+
const react = wrapperCall(read(paths.react), 'react')
|
|
105
|
+
|
|
106
|
+
test('begge rammeverk kaller input-wrapper', () => {
|
|
107
|
+
expect(elements, `${paths.elements} mangler <pkt-input-wrapper`).not.toBe('')
|
|
108
|
+
expect(react, `${paths.react} mangler <PktInputWrapper`).not.toBe('')
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
for (const prop of wrapperProps) {
|
|
112
|
+
const omitted = OMITTED[prop]?.[name as keyof typeof COMPONENTS]
|
|
113
|
+
|
|
114
|
+
test(`${prop}${omitted ? ' (utelatt med vilje)' : ''}`, () => {
|
|
115
|
+
const inElements = forwards(elements, prop)
|
|
116
|
+
const inReact = forwards(react, prop)
|
|
117
|
+
|
|
118
|
+
if (omitted) {
|
|
119
|
+
expect(
|
|
120
|
+
inElements || inReact,
|
|
121
|
+
`${prop} er listet som utelatt for ${name} (${omitted}), men videresendes likevel i ` +
|
|
122
|
+
`${inElements ? 'Elements' : 'React'}. Fjern den fra OMITTED hvis den skal støttes.`,
|
|
123
|
+
).toBe(false)
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
expect(
|
|
128
|
+
inElements,
|
|
129
|
+
`Elements' ${name} videresender ikke ${prop} til input-wrapper. Legg den til, eller ` +
|
|
130
|
+
`dokumentér utelatelsen i OMITTED.`,
|
|
131
|
+
).toBe(true)
|
|
132
|
+
expect(
|
|
133
|
+
inReact,
|
|
134
|
+
`React-versjonen av ${name} videresender ikke ${prop} til input-wrapper. Legg den til, ` +
|
|
135
|
+
`eller dokumentér utelatelsen i OMITTED.`,
|
|
136
|
+
).toBe(true)
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
})
|
|
@@ -5,6 +5,7 @@ import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
|
|
|
5
5
|
import { CustomElementFor } from '../../tests/component-registry'
|
|
6
6
|
import { type IPktSelect, type TSelectOption } from './select'
|
|
7
7
|
import './select'
|
|
8
|
+
import { PktInputWrapper } from '@/components/input-wrapper/input-wrapper'
|
|
8
9
|
|
|
9
10
|
export interface SelectTestConfig extends Partial<IPktSelect>, BaseTestConfig {
|
|
10
11
|
id?: string
|
|
@@ -15,8 +16,8 @@ export interface SelectTestConfig extends Partial<IPktSelect>, BaseTestConfig {
|
|
|
15
16
|
errorMessage?: string
|
|
16
17
|
helptext?: string
|
|
17
18
|
fullwidth?: boolean
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
'aria-labelledby'?: string | null
|
|
20
|
+
'aria-describedby'?: string | null
|
|
20
21
|
counter?: boolean
|
|
21
22
|
inline?: boolean
|
|
22
23
|
useWrapper?: boolean | string
|
|
@@ -202,15 +203,15 @@ describe('pkt-select', () => {
|
|
|
202
203
|
|
|
203
204
|
test('passes through accessibility attributes', async () => {
|
|
204
205
|
const { select } = await createSelectTest({
|
|
205
|
-
|
|
206
|
-
|
|
206
|
+
'aria-labelledby': 'external-label',
|
|
207
|
+
'aria-describedby': 'external-description',
|
|
207
208
|
})
|
|
208
209
|
|
|
209
210
|
const selectElement = select.querySelector('select')
|
|
210
211
|
expect(selectElement?.getAttribute('aria-labelledby')).toBe('external-label')
|
|
211
212
|
|
|
212
|
-
const inputWrapper = select.querySelector('pkt-input-wrapper')
|
|
213
|
-
expect(inputWrapper?.
|
|
213
|
+
const inputWrapper = select.querySelector('pkt-input-wrapper') as PktInputWrapper | null
|
|
214
|
+
expect(inputWrapper?.ariaDescribedby).toBe('external-description')
|
|
214
215
|
})
|
|
215
216
|
|
|
216
217
|
test('handles input wrapper properties', async () => {
|
|
@@ -220,8 +221,8 @@ describe('pkt-select', () => {
|
|
|
220
221
|
})
|
|
221
222
|
|
|
222
223
|
const inputWrapper = select.querySelector('pkt-input-wrapper')
|
|
223
|
-
expect(inputWrapper?.hasAttribute('counter')).toBe(true)
|
|
224
224
|
expect(inputWrapper?.hasAttribute('inline')).toBe(true)
|
|
225
|
+
expect(inputWrapper?.hasAttribute('counter')).toBe(false)
|
|
225
226
|
})
|
|
226
227
|
|
|
227
228
|
test('handles useWrapper false', async () => {
|
|
@@ -7,6 +7,7 @@ import { PktOptionsSlotController } from '@/controllers/pkt-options-controller'
|
|
|
7
7
|
import { slotContent } from '@/directives/slot-content'
|
|
8
8
|
import type { TSelectOption } from 'shared-types/select'
|
|
9
9
|
import '@/components/input-wrapper'
|
|
10
|
+
import { describedByIds } from 'shared-utils/forms/described-by'
|
|
10
11
|
|
|
11
12
|
export type { TSelectOption } from 'shared-types/select'
|
|
12
13
|
|
|
@@ -162,6 +163,7 @@ export class PktSelect extends PktOptionsInputElement<{}, TSelectOption> impleme
|
|
|
162
163
|
|
|
163
164
|
protected firstUpdated(_changedProperties: PropertyValues): void {
|
|
164
165
|
super.firstUpdated(_changedProperties)
|
|
166
|
+
this.classList.add('pkt-select')
|
|
165
167
|
if (this._optionsProp.length) {
|
|
166
168
|
this._options = this._optionsProp
|
|
167
169
|
}
|
|
@@ -181,16 +183,14 @@ export class PktSelect extends PktOptionsInputElement<{}, TSelectOption> impleme
|
|
|
181
183
|
return html`
|
|
182
184
|
<pkt-input-wrapper
|
|
183
185
|
.size=${this.inputSize}
|
|
184
|
-
?counter=${this.counter}
|
|
185
186
|
?disabled=${this.disabled}
|
|
186
187
|
?hasError=${this.hasError}
|
|
187
|
-
?hasFieldset=${this.
|
|
188
|
+
?hasFieldset=${this.effectiveHasFieldset(false)}
|
|
188
189
|
?inline=${this.inline}
|
|
189
190
|
?optionalTag=${this.optionalTag}
|
|
190
191
|
?requiredTag=${this.requiredTag}
|
|
191
192
|
useWrapper=${this.useWrapper}
|
|
192
|
-
|
|
193
|
-
class="pkt-select"
|
|
193
|
+
.ariaDescribedby=${this.ariaDescribedby}
|
|
194
194
|
errorMessage=${ifDefined(this.errorMessage)}
|
|
195
195
|
forId=${this.id + '-input'}
|
|
196
196
|
helptext=${ifDefined(this.helptext)}
|
|
@@ -204,7 +204,15 @@ export class PktSelect extends PktOptionsInputElement<{}, TSelectOption> impleme
|
|
|
204
204
|
<select
|
|
205
205
|
class=${inputClass}
|
|
206
206
|
aria-invalid=${this.hasError}
|
|
207
|
-
aria-errormessage=${`${this.id}-error`}
|
|
207
|
+
aria-errormessage=${ifDefined(this.hasError ? `${this.id}-input-error` : undefined)}
|
|
208
|
+
aria-describedby=${ifDefined(
|
|
209
|
+
describedByIds({
|
|
210
|
+
id: this.id + '-input',
|
|
211
|
+
hasHelptext: !!this.helptext,
|
|
212
|
+
hasCounter: false,
|
|
213
|
+
ariaDescribedby: this.ariaDescribedby,
|
|
214
|
+
}),
|
|
215
|
+
)}
|
|
208
216
|
aria-labelledby=${ifDefined(this.ariaLabelledby)}
|
|
209
217
|
?disabled=${this.disabled}
|
|
210
218
|
id=${this.id + '-input'}
|
|
@@ -28,8 +28,8 @@ export interface TextareaTestConfig extends BaseTestConfig {
|
|
|
28
28
|
fullwidth?: boolean
|
|
29
29
|
counter?: boolean
|
|
30
30
|
inline?: boolean
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
'aria-labelledby'?: string | null
|
|
32
|
+
'aria-describedby'?: string | null
|
|
33
33
|
inputSize?: TPktInputSize
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -246,15 +246,15 @@ describe('PktTextarea', () => {
|
|
|
246
246
|
describe('Accessibility', () => {
|
|
247
247
|
test('passes through accessibility attributes', async () => {
|
|
248
248
|
const { textarea } = await createTextareaTest({
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
'aria-labelledby': 'external-label',
|
|
250
|
+
'aria-describedby': 'external-description',
|
|
251
251
|
})
|
|
252
252
|
|
|
253
253
|
const textareaElement = textarea.querySelector('textarea')
|
|
254
254
|
expect(textareaElement?.getAttribute('aria-labelledby')).toBe('external-label')
|
|
255
255
|
|
|
256
|
-
//
|
|
257
|
-
expect(textarea.
|
|
256
|
+
// ariaDescribedby videresendes som property til input-wrapper
|
|
257
|
+
expect(textarea.ariaDescribedby).toBe('external-description')
|
|
258
258
|
})
|
|
259
259
|
|
|
260
260
|
test('textarea is accessible', async () => {
|
|
@@ -10,8 +10,9 @@ import { slotContent } from '@/directives/slot-content'
|
|
|
10
10
|
import '@/components/input-wrapper'
|
|
11
11
|
import '@/components/icon'
|
|
12
12
|
import { ElementProps } from '@/types/typeUtils'
|
|
13
|
+
import { describedByIds } from 'shared-utils/forms/described-by'
|
|
13
14
|
|
|
14
|
-
type Props = ElementProps<PktTextarea, 'autocomplete' | 'rows'>
|
|
15
|
+
type Props = ElementProps<PktTextarea, 'autocomplete' | 'rows' | 'cols'>
|
|
15
16
|
|
|
16
17
|
export class PktTextarea extends PktInputElement<Props> {
|
|
17
18
|
inputRef: Ref<HTMLTextAreaElement> = createRef()
|
|
@@ -19,11 +20,11 @@ export class PktTextarea extends PktInputElement<Props> {
|
|
|
19
20
|
@property({ type: String, reflect: true }) value: string = ''
|
|
20
21
|
@property({ type: String }) autocomplete: string = 'off'
|
|
21
22
|
@property({ type: Number }) rows: number | null = null
|
|
23
|
+
@property({ type: Number }) cols: number | null = null
|
|
22
24
|
|
|
23
25
|
@state() counterCurrent = 0
|
|
24
26
|
|
|
25
27
|
attributeChangedCallback(name: string, _old: string | null, value: string | null): void {
|
|
26
|
-
// Only sync the counter here.
|
|
27
28
|
if (name === 'value' && this.value !== _old) {
|
|
28
29
|
this.counterCurrent = value ? value.length : 0
|
|
29
30
|
}
|
|
@@ -32,6 +33,7 @@ export class PktTextarea extends PktInputElement<Props> {
|
|
|
32
33
|
|
|
33
34
|
updated(changedProperties: PropertyValues) {
|
|
34
35
|
super.updated(changedProperties)
|
|
36
|
+
this.classList.add('pkt-textinput', 'pkt-textarea')
|
|
35
37
|
if (changedProperties.has('value')) {
|
|
36
38
|
this.counterCurrent = this.value?.length || 0
|
|
37
39
|
this.valueChanged(this.value, changedProperties.get('value'))
|
|
@@ -42,21 +44,20 @@ export class PktTextarea extends PktInputElement<Props> {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
render() {
|
|
47
|
+
const showCounter = this.effectiveCounter(this.counterMaxLength > 0)
|
|
45
48
|
const inputClasses = classMap({
|
|
46
49
|
'pkt-input': true,
|
|
47
50
|
[`pkt-input--${this.inputSize}`]: true,
|
|
48
51
|
'pkt-input--fullwidth': this.fullwidth,
|
|
49
52
|
'pkt-input--counter-error':
|
|
50
|
-
this.
|
|
51
|
-
this.counterMaxLength &&
|
|
52
|
-
this.value.length &&
|
|
53
|
-
this.value.length > this.counterMaxLength,
|
|
53
|
+
showCounter && this.counterMaxLength > 0 && this.value.length > this.counterMaxLength,
|
|
54
54
|
})
|
|
55
55
|
const labelledBy = this.ariaLabelledby || `${this.id}-input-label`
|
|
56
56
|
|
|
57
57
|
return html`<pkt-input-wrapper
|
|
58
58
|
label=${this.label}
|
|
59
|
-
?counter=${
|
|
59
|
+
?counter=${showCounter}
|
|
60
|
+
?hasFieldset=${this.effectiveHasFieldset(false)}
|
|
60
61
|
?disabled=${this.disabled}
|
|
61
62
|
?hasError=${this.hasError}
|
|
62
63
|
?inline=${this.inline}
|
|
@@ -64,8 +65,8 @@ export class PktTextarea extends PktInputElement<Props> {
|
|
|
64
65
|
?required=${this.required}
|
|
65
66
|
?requiredTag=${this.requiredTag}
|
|
66
67
|
useWrapper=${this.useWrapper}
|
|
67
|
-
.
|
|
68
|
-
.counterCurrent=${this.
|
|
68
|
+
.ariaDescribedby=${this.ariaDescribedby}
|
|
69
|
+
.counterCurrent=${this.value?.length ?? 0}
|
|
69
70
|
.counterMaxLength=${this.counterMaxLength}
|
|
70
71
|
.errorMessage=${this.errorMessage}
|
|
71
72
|
.forId="${this.id + '-input'}"
|
|
@@ -76,7 +77,6 @@ export class PktTextarea extends PktInputElement<Props> {
|
|
|
76
77
|
.requiredText=${this.requiredText}
|
|
77
78
|
.tagText=${this.tagText}
|
|
78
79
|
.size=${this.inputSize}
|
|
79
|
-
class="pkt-textarea"
|
|
80
80
|
>
|
|
81
81
|
<div class="pkt-contents" slot="helptext">${slotContent(this, 'helptext')}</div>
|
|
82
82
|
<textarea
|
|
@@ -92,8 +92,17 @@ export class PktTextarea extends PktInputElement<Props> {
|
|
|
92
92
|
autocomplete=${this.autocomplete}
|
|
93
93
|
aria-labelledby=${labelledBy}
|
|
94
94
|
aria-invalid=${this.hasError}
|
|
95
|
-
aria-errormessage=${`${this.id}-error`}
|
|
95
|
+
aria-errormessage=${ifDefined(this.hasError ? `${this.id}-input-error` : undefined)}
|
|
96
|
+
aria-describedby=${ifDefined(
|
|
97
|
+
describedByIds({
|
|
98
|
+
id: this.id + '-input',
|
|
99
|
+
hasHelptext: !!this.helptext,
|
|
100
|
+
hasCounter: showCounter,
|
|
101
|
+
ariaDescribedby: this.ariaDescribedby,
|
|
102
|
+
}),
|
|
103
|
+
)}
|
|
96
104
|
rows=${ifDefined(this.rows ?? undefined)}
|
|
105
|
+
cols=${ifDefined(this.cols ?? undefined)}
|
|
97
106
|
?disabled=${this.disabled}
|
|
98
107
|
@input=${(e: InputEvent) => {
|
|
99
108
|
this.value = (e.target as HTMLInputElement).value
|
|
@@ -33,8 +33,8 @@ export interface TextinputTestConfig extends BaseTestConfig {
|
|
|
33
33
|
fullwidth?: boolean
|
|
34
34
|
counter?: boolean
|
|
35
35
|
inline?: boolean
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
'aria-labelledby'?: string | null
|
|
37
|
+
'aria-describedby'?: string | null
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
// Use shared framework
|
|
@@ -387,15 +387,15 @@ describe('PktTextinput', () => {
|
|
|
387
387
|
describe('Accessibility', () => {
|
|
388
388
|
test('passes through accessibility attributes', async () => {
|
|
389
389
|
const { textinput } = await createTextinputTest({
|
|
390
|
-
|
|
391
|
-
|
|
390
|
+
'aria-labelledby': 'external-label',
|
|
391
|
+
'aria-describedby': 'external-description',
|
|
392
392
|
})
|
|
393
393
|
|
|
394
394
|
const inputElement = textinput.querySelector('input')
|
|
395
395
|
expect(inputElement?.getAttribute('aria-labelledby')).toBe('external-label')
|
|
396
396
|
|
|
397
|
-
//
|
|
398
|
-
expect(textinput.
|
|
397
|
+
// ariaDescribedby videresendes som property til input-wrapper
|
|
398
|
+
expect(textinput.ariaDescribedby).toBe('external-description')
|
|
399
399
|
})
|
|
400
400
|
|
|
401
401
|
test('textinput is accessible', async () => {
|