@oslokommune/punkt-elements 17.1.2 → 18.0.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.
Files changed (249) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/{accordion-BeLvLPrB.js → accordion-1_pdxTgJ.js} +27 -27
  3. package/dist/{accordion-B3GBlvUC.cjs → accordion-DobbH-Sv.cjs} +5 -5
  4. package/dist/{alert-Cs4Rmq_J.cjs → alert-COeHz6o2.cjs} +6 -6
  5. package/dist/{alert-DKK_Tb9J.js → alert-xspFetQ8.js} +26 -26
  6. package/dist/{async-directive-Da7TJpnq.cjs → async-directive-B5pcIMfO.cjs} +1 -1
  7. package/dist/{async-directive-BPf6K-V2.js → async-directive-D2Sht790.js} +5 -5
  8. package/dist/{backlink-D1y26pj3.js → backlink-C0jZFnZ2.js} +8 -8
  9. package/dist/{backlink-CMmrKTe4.cjs → backlink-ZMhqHaVs.cjs} +3 -3
  10. package/dist/base-elements/element.d.ts +13 -1
  11. package/dist/base-elements/input-element.d.ts +4 -2
  12. package/dist/{breadcrumbs-B6JGhJgC.cjs → breadcrumbs-BS64xd1s.cjs} +7 -7
  13. package/dist/{breadcrumbs-CcTQ3Grm.js → breadcrumbs-BvMownZN.js} +16 -16
  14. package/dist/{button-2-OSPJkj.js → button-C44vNLFC.js} +28 -28
  15. package/dist/{button-CE97NQIk.cjs → button-CR4CXtnj.cjs} +7 -7
  16. package/dist/{calendar-kn4SrtJy.cjs → calendar-DQ4byeom.cjs} +28 -27
  17. package/dist/{calendar-pOW3R5Pw.js → calendar-DgjCKMMV.js} +82 -68
  18. package/dist/{card-zQ0eKRMV.js → card-DhHqC265.js} +44 -44
  19. package/dist/{card-CETDFkxR.cjs → card-Dpqi6HVt.cjs} +26 -26
  20. package/dist/{checkbox-CH7NpINF.js → checkbox-BQ1K57AB.js} +23 -23
  21. package/dist/{checkbox-CQNr_sEg.cjs → checkbox-CXQxbiaX.cjs} +11 -11
  22. package/dist/{class-map-BeimMvPv.cjs → class-map-CzCE5pqG.cjs} +1 -1
  23. package/dist/{class-map-CnWCnBSX.js → class-map-F1DJHyb7.js} +3 -3
  24. package/dist/{combobox-Bvg0UO9w.cjs → combobox-BV4oDiII.cjs} +19 -18
  25. package/dist/{combobox-CRdYYlxu.js → combobox-DPck39I2.js} +55 -54
  26. package/dist/components/calendar/calendar.d.ts +21 -6
  27. package/dist/components/checkbox/checkbox.d.ts +4 -2
  28. package/dist/components/combobox/combobox-base.d.ts +2 -1
  29. package/dist/components/consent/strings.d.ts +1 -85
  30. package/dist/components/datepicker/datepicker-base.d.ts +0 -3
  31. package/dist/components/datepicker/datepicker-multiple.d.ts +0 -2
  32. package/dist/components/datepicker/datepicker-range.d.ts +0 -2
  33. package/dist/components/datepicker/datepicker-single.d.ts +0 -2
  34. package/dist/components/datepicker/datepicker-utils.d.ts +2 -1
  35. package/dist/components/fileupload/fileupload-base.d.ts +3 -2
  36. package/dist/components/header/header-service.d.ts +3 -1
  37. package/dist/components/header/header.d.ts +2 -1
  38. package/dist/components/helptext/helptext.d.ts +2 -1
  39. package/dist/components/index.d.ts +2 -0
  40. package/dist/components/input-wrapper/input-wrapper.d.ts +4 -2
  41. package/dist/components/radiobutton/radiobutton.d.ts +4 -2
  42. package/dist/components/searchinput/searchinput.d.ts +2 -1
  43. package/dist/{consent-DZEkhgJ1.js → consent-BneInZov.js} +10 -10
  44. package/dist/{consent-CyY3RyoC.cjs → consent-_N9foluG.cjs} +5 -5
  45. package/dist/controllers/strings-controller.d.ts +27 -0
  46. package/dist/{datepicker-VTqSDQk5.cjs → datepicker-9NX5qt2z.cjs} +38 -37
  47. package/dist/{datepicker-ButIas85.js → datepicker-nSSSr1U7.js} +104 -104
  48. package/dist/{directive-helpers-CI8YZqku.js → directive-helpers-BARw3nEZ.js} +1 -1
  49. package/dist/{directive-helpers-BlAV5EQD.cjs → directive-helpers-D6AAMFKs.cjs} +1 -1
  50. package/dist/docs/components/fileupload.d.ts +1 -0
  51. package/dist/element-B8OGznK0.cjs +2 -0
  52. package/dist/{element-ChsFgr_B.js → element-BKSPVgs2.js} +240 -100
  53. package/dist/{element-with-slot-Bj7nm7fO.cjs → element-with-slot-DTxILHVv.cjs} +1 -1
  54. package/dist/{element-with-slot-BvG9ctC-.js → element-with-slot-YUAUFRqw.js} +4 -4
  55. package/dist/fileupload-D05VSbEP.cjs +402 -0
  56. package/dist/{fileupload-D5IQ9HDe.js → fileupload-E0LGGNgj.js} +370 -380
  57. package/dist/{footer-DbYQUYNR.cjs → footer-DBApIdGh.cjs} +32 -32
  58. package/dist/{footer-DrR64HiK.js → footer-DYilVBMC.js} +91 -91
  59. package/dist/format-B8meU8Tm.cjs +1 -0
  60. package/dist/format-BTeSVkED.js +18 -0
  61. package/dist/{header-ClgIac6n.cjs → header-B7tbX-Ok.cjs} +84 -84
  62. package/dist/{header-menu-BAsUNKSx.cjs → header-menu-1FFWhvNc.cjs} +24 -24
  63. package/dist/{header-menu-hVPXsb8g.js → header-menu-D522-VTV.js} +75 -75
  64. package/dist/{header-KvJ1mGwA.js → header-v3GWAzOK.js} +267 -264
  65. package/dist/{heading-CFmiSuCQ.cjs → heading-CvytvcL7.cjs} +1 -1
  66. package/dist/{heading-g-_IUB5T.js → heading-rx_oSCqe.js} +16 -16
  67. package/dist/helptext-DYFdcylX.cjs +27 -0
  68. package/dist/{helptext-DZng1D5i.js → helptext-LShGPm-b.js} +18 -19
  69. package/dist/{icon-9iwd2F2l.cjs → icon-BmMC09mi.cjs} +1 -1
  70. package/dist/{icon-D-LA22xD.js → icon-HYU1f54x.js} +17 -17
  71. package/dist/{if-defined-CGzIlZ9-.js → if-defined-BF8XcDi5.js} +1 -1
  72. package/dist/if-defined-CVYApOgK.cjs +1 -0
  73. package/dist/{input-element-DBMNBplu.js → input-element-D07DmR34.js} +121 -120
  74. package/dist/input-element-DumGj_oJ.cjs +1 -0
  75. package/dist/input-wrapper-Cci_wTvD.js +308 -0
  76. package/dist/input-wrapper-DDfapoRX.cjs +58 -0
  77. package/dist/{link-DK7gvmnj.cjs → link-11g2ZNin.cjs} +4 -4
  78. package/dist/{link-D-Eqm5KH.js → link-CQK0SH4H.js} +13 -13
  79. package/dist/{linkcard-zqbg7Oqm.js → linkcard-B4GlmjJ6.js} +16 -16
  80. package/dist/linkcard-DNYsVlV6.cjs +13 -0
  81. package/dist/{listbox-Dc4rGdPi.cjs → listbox-BlFRhyI7.cjs} +15 -15
  82. package/dist/{listbox-va_E8afs.js → listbox-wd_OofyF.js} +100 -99
  83. package/dist/{loader-D_RDICnw.js → loader-0C3qVk16.js} +14 -14
  84. package/dist/{loader-CMUkVJmE.cjs → loader-B9u2qUlR.cjs} +5 -5
  85. package/dist/{messagebox-Cv8LaSxv.cjs → messagebox-BD5tGmT-.cjs} +5 -5
  86. package/dist/{messagebox-Cw-OWDya.js → messagebox-C-y1ICmr.js} +15 -15
  87. package/dist/{modal-BtInv031.cjs → modal-BzPGQYCs.cjs} +8 -8
  88. package/dist/{modal-D0t_X2dX.js → modal-Cghs3w-d.js} +20 -20
  89. package/dist/pkt-accordion.cjs +1 -1
  90. package/dist/pkt-accordion.js +1 -1
  91. package/dist/pkt-alert.cjs +1 -1
  92. package/dist/pkt-alert.js +1 -1
  93. package/dist/pkt-backlink.cjs +1 -1
  94. package/dist/pkt-backlink.js +1 -1
  95. package/dist/pkt-breadcrumbs.cjs +1 -1
  96. package/dist/pkt-breadcrumbs.js +1 -1
  97. package/dist/pkt-button.cjs +1 -1
  98. package/dist/pkt-button.js +1 -1
  99. package/dist/pkt-calendar.cjs +1 -1
  100. package/dist/pkt-calendar.js +1 -1
  101. package/dist/pkt-card.cjs +1 -1
  102. package/dist/pkt-card.js +1 -1
  103. package/dist/pkt-checkbox.cjs +1 -1
  104. package/dist/pkt-checkbox.js +1 -1
  105. package/dist/pkt-combobox.cjs +1 -1
  106. package/dist/pkt-combobox.js +1 -1
  107. package/dist/pkt-consent.cjs +1 -1
  108. package/dist/pkt-consent.js +1 -1
  109. package/dist/pkt-datepicker.cjs +1 -1
  110. package/dist/pkt-datepicker.js +1 -1
  111. package/dist/pkt-fileupload.cjs +1 -1
  112. package/dist/pkt-fileupload.js +1 -1
  113. package/dist/pkt-footer.cjs +1 -1
  114. package/dist/pkt-footer.js +1 -1
  115. package/dist/pkt-header-menu.cjs +1 -1
  116. package/dist/pkt-header-menu.js +1 -1
  117. package/dist/pkt-header.cjs +1 -1
  118. package/dist/pkt-header.js +1 -1
  119. package/dist/pkt-heading.cjs +1 -1
  120. package/dist/pkt-heading.js +1 -1
  121. package/dist/pkt-helptext.cjs +1 -1
  122. package/dist/pkt-helptext.js +1 -1
  123. package/dist/pkt-icon.cjs +1 -1
  124. package/dist/pkt-icon.js +1 -1
  125. package/dist/pkt-index.cjs +1 -1
  126. package/dist/pkt-index.js +35 -34
  127. package/dist/pkt-input-wrapper.cjs +1 -1
  128. package/dist/pkt-input-wrapper.js +1 -1
  129. package/dist/pkt-link.cjs +1 -1
  130. package/dist/pkt-link.js +1 -1
  131. package/dist/pkt-linkcard.cjs +1 -1
  132. package/dist/pkt-linkcard.js +1 -1
  133. package/dist/pkt-listbox.cjs +1 -1
  134. package/dist/pkt-listbox.js +1 -1
  135. package/dist/pkt-loader.cjs +1 -1
  136. package/dist/pkt-loader.js +1 -1
  137. package/dist/pkt-messagebox.cjs +1 -1
  138. package/dist/pkt-messagebox.js +1 -1
  139. package/dist/pkt-modal.cjs +1 -1
  140. package/dist/pkt-modal.js +1 -1
  141. package/dist/{pkt-options-controller-C5EBX2T0.cjs → pkt-options-controller-CEEAqXaB.cjs} +1 -1
  142. package/dist/{pkt-options-controller-B77vH3PE.js → pkt-options-controller-PtjhmTGk.js} +1 -1
  143. package/dist/pkt-progressbar.cjs +1 -1
  144. package/dist/pkt-progressbar.js +1 -1
  145. package/dist/pkt-radiobutton.cjs +1 -1
  146. package/dist/pkt-radiobutton.js +1 -1
  147. package/dist/pkt-searchinput.cjs +1 -1
  148. package/dist/pkt-searchinput.js +1 -1
  149. package/dist/pkt-select.cjs +1 -1
  150. package/dist/pkt-select.js +1 -1
  151. package/dist/pkt-tabs.cjs +1 -1
  152. package/dist/pkt-tabs.js +1 -1
  153. package/dist/pkt-tag.cjs +1 -1
  154. package/dist/pkt-tag.js +1 -1
  155. package/dist/pkt-textarea.cjs +1 -1
  156. package/dist/pkt-textarea.js +1 -1
  157. package/dist/pkt-textinput.cjs +1 -1
  158. package/dist/pkt-textinput.js +1 -1
  159. package/dist/pkt-timepicker.cjs +1 -1
  160. package/dist/pkt-timepicker.js +1 -1
  161. package/dist/{progressbar-CbcfLerF.js → progressbar-BexQQw1o.js} +21 -21
  162. package/dist/{progressbar-D5MmnQId.cjs → progressbar-X9mSvpjw.cjs} +5 -5
  163. package/dist/{radiobutton-BUnf3EPi.js → radiobutton-CkhTasn4.js} +24 -24
  164. package/dist/{radiobutton-DpsO1F-8.cjs → radiobutton-ln29Ad3U.cjs} +8 -8
  165. package/dist/{ref-CQoEIIp0.cjs → ref-CHbJYtKE.cjs} +1 -1
  166. package/dist/{ref-CWD6PjVd.js → ref-Chc_DZAb.js} +2 -2
  167. package/dist/{repeat-sY6B7fAT.cjs → repeat-CHDs__rN.cjs} +1 -1
  168. package/dist/{repeat-C02g7d5S.js → repeat-P9SMlKd5.js} +4 -4
  169. package/dist/searchinput-CPgMEAZZ.cjs +60 -0
  170. package/dist/{searchinput-DwnjsvJ1.js → searchinput-Da7dKHBH.js} +43 -43
  171. package/dist/{select-BOJi6H6T.cjs → select-DCuvdAuK.cjs} +4 -3
  172. package/dist/{select-DMre80qs.js → select-jabMSjft.js} +21 -20
  173. package/dist/shared-strings/consent.d.ts +71 -0
  174. package/dist/shared-strings/format.d.ts +21 -0
  175. package/dist/shared-strings/index.d.ts +14 -0
  176. package/dist/shared-strings/nb.d.ts +118 -0
  177. package/dist/shared-strings/resolve.d.ts +12 -0
  178. package/dist/shared-strings/store.d.ts +27 -0
  179. package/dist/shared-strings/types.d.ts +29 -0
  180. package/dist/shared-types/fileupload.d.ts +0 -27
  181. package/dist/shared-types/index.d.ts +1 -2
  182. package/dist/shared-utils/combobox/index.d.ts +1 -0
  183. package/dist/shared-utils/combobox/input-utils.d.ts +2 -1
  184. package/dist/shared-utils/combobox/messages.d.ts +7 -0
  185. package/dist/shared-utils/combobox/option-utils.d.ts +2 -1
  186. package/dist/shared-utils/combobox/selection-manager.d.ts +3 -2
  187. package/dist/{tabs-Cf9ai0FD.cjs → tabs-DV5CQNDL.cjs} +7 -7
  188. package/dist/{tabs-TvxgbrCF.js → tabs-D_hoi1zu.js} +32 -32
  189. package/dist/tag-BxYulmZn.cjs +27 -0
  190. package/dist/{tag-Ds6n1LIs.js → tag-DK5SgKry.js} +33 -32
  191. package/dist/tests/strings-override.test.d.ts +0 -0
  192. package/dist/{textarea-vfzIcXhX.js → textarea-BR7Riace.js} +30 -29
  193. package/dist/{textarea-BydiLM9Z.cjs → textarea-ffBD_VHq.cjs} +3 -2
  194. package/dist/{textinput-CMP2jPP_.js → textinput-CRNrsRNV.js} +28 -27
  195. package/dist/{textinput-DGdicNSx.cjs → textinput-CtHFzAux.cjs} +13 -12
  196. package/dist/timepicker-AfgJipo6.cjs +196 -0
  197. package/dist/{timepicker-ClwgYywH.js → timepicker-ixs_E5RY.js} +67 -64
  198. package/package.json +3 -3
  199. package/src/components/backlink/backlink.ts +1 -1
  200. package/src/components/breadcrumbs/breadcrumbs.ts +1 -1
  201. package/src/components/calendar/calendar.ts +56 -26
  202. package/src/components/checkbox/checkbox.test.ts +0 -2
  203. package/src/components/checkbox/checkbox.ts +8 -4
  204. package/src/components/combobox/combobox-base.ts +2 -1
  205. package/src/components/combobox/combobox-value.ts +10 -9
  206. package/src/components/combobox/combobox.selection.test.ts +5 -1
  207. package/src/components/combobox/combobox.ts +4 -1
  208. package/src/components/consent/strings.ts +1 -125
  209. package/src/components/datepicker/date-tags.ts +4 -1
  210. package/src/components/datepicker/datepicker-base.ts +2 -6
  211. package/src/components/datepicker/datepicker-multiple.ts +0 -3
  212. package/src/components/datepicker/datepicker-range.ts +4 -7
  213. package/src/components/datepicker/datepicker-single.ts +0 -3
  214. package/src/components/datepicker/datepicker-utils.test.ts +5 -5
  215. package/src/components/datepicker/datepicker-utils.ts +6 -11
  216. package/src/components/datepicker/datepicker.ts +8 -1
  217. package/src/components/fileupload/fileupload-base.ts +25 -16
  218. package/src/components/fileupload/fileupload-renderers.ts +2 -1
  219. package/src/components/fileupload/fileupload.test.ts +16 -0
  220. package/src/components/fileupload/fileupload.ts +19 -15
  221. package/src/components/header/header-global.ts +3 -3
  222. package/src/components/header/header-service.ts +13 -8
  223. package/src/components/header/header.ts +2 -1
  224. package/src/components/helptext/helptext.test.ts +1 -1
  225. package/src/components/helptext/helptext.ts +3 -3
  226. package/src/components/index.ts +9 -0
  227. package/src/components/input-wrapper/input-wrapper.ts +18 -10
  228. package/src/components/listbox/listbox.ts +3 -3
  229. package/src/components/radiobutton/radiobutton.ts +8 -4
  230. package/src/components/searchinput/searchinput.ts +5 -3
  231. package/src/components/select/select.ts +1 -0
  232. package/src/components/tag/tag.ts +2 -1
  233. package/src/components/textarea/textarea.ts +1 -0
  234. package/src/components/textinput/textinput.ts +1 -0
  235. package/src/components/timepicker/timepicker.ts +23 -21
  236. package/dist/element-BA8v6S7z.cjs +0 -2
  237. package/dist/fileupload-BeWiTAoW.cjs +0 -401
  238. package/dist/helptext-CTtKJcML.cjs +0 -27
  239. package/dist/if-defined-B89_ZeCj.cjs +0 -1
  240. package/dist/input-element-DYmQw-fH.cjs +0 -1
  241. package/dist/input-wrapper-BmxNvznr.js +0 -128
  242. package/dist/input-wrapper-D-fPaTbY.js +0 -176
  243. package/dist/input-wrapper-D5PR0rJn.cjs +0 -1
  244. package/dist/input-wrapper-Q-B-aBeb.cjs +0 -54
  245. package/dist/linkcard-B29cmZqh.cjs +0 -13
  246. package/dist/searchinput-Bh975rjX.cjs +0 -60
  247. package/dist/tag-CzojNU7z.cjs +0 -27
  248. package/dist/timepicker-DYrnLHqN.cjs +0 -195
  249. package/dist/translations/no.json.d.ts +0 -94
@@ -3,6 +3,7 @@ import { findOptionByValue, isMaxSelectionReached } from 'shared-utils/combobox/
3
3
  import { getSingleValueForInput } from 'shared-utils/combobox/input-utils'
4
4
  import { selectionMutators } from './combobox-utils'
5
5
  import { ComboboxBase } from './combobox-base'
6
+ import type { TComboboxMessageKey } from 'shared-utils/combobox'
6
7
 
7
8
  /**
8
9
  * Value management layer for PktCombobox.
@@ -27,7 +28,7 @@ export class ComboboxValue extends ComboboxBase {
27
28
 
28
29
  let shouldOptionsBeOpen: boolean = false
29
30
  let shouldResetInput: boolean = true
30
- let userInfoMessage: string | null = ''
31
+ let userInfoMessage: TComboboxMessageKey = ''
31
32
  let searchValue: string | null = ''
32
33
 
33
34
  if (isDisabled) return
@@ -35,7 +36,7 @@ export class ComboboxValue extends ComboboxBase {
35
36
  // Not in option list and allowUserInput is true
36
37
  if (!isInOption && this.allowUserInput && !isEmpty) {
37
38
  this.addNewUserValue(value)
38
- userInfoMessage = 'Ny verdi lagt til'
39
+ userInfoMessage = 'valueAdded'
39
40
  shouldOptionsBeOpen = isMultiple
40
41
  }
41
42
 
@@ -46,7 +47,7 @@ export class ComboboxValue extends ComboboxBase {
46
47
  }
47
48
  shouldResetInput = false
48
49
  shouldOptionsBeOpen = true
49
- userInfoMessage = 'Ingen treff i søket'
50
+ userInfoMessage = 'noResults'
50
51
  }
51
52
 
52
53
  // Value is already selected — deselect it
@@ -85,7 +86,7 @@ export class ComboboxValue extends ComboboxBase {
85
86
 
86
87
  // Multi-select with max selections reached
87
88
  else if (isMultiple && isMaxItemsReached) {
88
- userInfoMessage = 'Maks antall valg nådd'
89
+ userInfoMessage = 'maxSelected'
89
90
  shouldResetInput = false
90
91
  searchValue = value
91
92
  }
@@ -93,7 +94,7 @@ export class ComboboxValue extends ComboboxBase {
93
94
  // No matching condition — fallback
94
95
  else {
95
96
  isSingle && this.removeAllSelected()
96
- userInfoMessage = 'Ingen gyldig verdi valgt'
97
+ userInfoMessage = 'noValidValue'
97
98
  shouldResetInput = false
98
99
  shouldOptionsBeOpen = true
99
100
  searchValue = value
@@ -121,7 +122,7 @@ export class ComboboxValue extends ComboboxBase {
121
122
  if (this._value.includes(value as string)) return
122
123
 
123
124
  if (this.multiple && isMaxSelectionReached(this._value.length, this.maxlength)) {
124
- this._userInfoMessage = 'Maks antall valg nådd'
125
+ this._userInfoMessage = 'maxSelected'
125
126
  return
126
127
  }
127
128
 
@@ -152,7 +153,7 @@ export class ComboboxValue extends ComboboxBase {
152
153
  protected addAllOptions(): void {
153
154
  if (!this.multiple) return
154
155
  if (this.maxlength && this._options.length > this.maxlength) {
155
- this._userInfoMessage = 'For mange valgt'
156
+ this._userInfoMessage = 'tooManySelected'
156
157
  return
157
158
  }
158
159
 
@@ -173,10 +174,10 @@ export class ComboboxValue extends ComboboxBase {
173
174
  this._search = input
174
175
 
175
176
  // If the typed value is already selected, don't toggle (which would deselect).
176
- // Just reset the input and keep the "Verdien er allerede valgt" message.
177
+ // Just reset the input and keep the "alreadySelected" message.
177
178
  if (input && this._value.includes(input)) {
178
179
  this.resetComboboxInput(true)
179
- this._userInfoMessage = 'Verdien er allerede valgt'
180
+ this._userInfoMessage = 'alreadySelected'
180
181
  return
181
182
  }
182
183
 
@@ -269,7 +269,11 @@ describe('PktCombobox', () => {
269
269
  ;(combobox as any).toggleValue('cherry')
270
270
  await combobox.updateComplete
271
271
 
272
- expect(combobox['_userInfoMessage']).toBe('Maks antall valg nådd')
272
+ expect(combobox['_userInfoMessage']).toBe('maxSelected')
273
+ const listbox = container.querySelector('pkt-listbox') as HTMLElement & {
274
+ userMessage: string
275
+ }
276
+ expect(listbox.userMessage).toBe('Maks antall valg nådd')
273
277
  })
274
278
 
275
279
  test('allows deselection when at maxlength', async () => {
@@ -281,6 +281,7 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
281
281
  render() {
282
282
  return html`
283
283
  <pkt-input-wrapper
284
+ .strings=${this.strings}
284
285
  .size=${this.inputSize}
285
286
  .label=${this.label}
286
287
  .helptext=${this.helptext}
@@ -371,7 +372,9 @@ export class PktCombobox extends ComboboxHandlers implements IPktCombobox {
371
372
  ?allow-user-input=${this.allowUserInput && !this._maxIsReached}
372
373
  ?max-is-reached=${this._maxIsReached}
373
374
  .customUserInput=${ifDefined(this._addValueText)}
374
- .userMessage=${this._userInfoMessage}
375
+ .userMessage=${this._userInfoMessage
376
+ ? this.pktStrings.get('combobox')[this._userInfoMessage]
377
+ : ''}
375
378
  @search=${this.handleSearch}
376
379
  @option-toggle=${this.handleOptionToggled}
377
380
  @select-all=${this.addAllOptions}
@@ -1,125 +1 @@
1
- export const consentStrings = {
2
- i18n: {
3
- nb: {
4
- contentPresentation: {
5
- title: 'Oslo kommune bruker informasjonskapsler',
6
- description: [
7
- 'For at nettstedet skal fungere og være trygt, bruker Oslo kommune informasjonskapsler. Noen er teknisk nødvendige, mens andre sikrer ulik funksjonalitet.',
8
- 'Godtar du alle informasjonskapsler, tillater du også at vi samler inn data om statistikk og brukeradferd. Da hjelper du oss med å lage et bedre nettsted uten at du trenger å dele noe personlig informasjon med oss.',
9
- ],
10
- buttons: {
11
- accept: 'Godta alle',
12
- reject: 'Kun nødvendige',
13
- settings: 'Innstillinger for informasjonskapsler',
14
- },
15
- },
16
- contentSettings: {
17
- title: 'Innstillinger for informasjonskapsler',
18
- description: [
19
- 'Her kan du velge hvilke typer informasjonskapsler du vil tillate. Tillatelsen gjelder i 90 dager. Husk at nødvendige informasjonskapsler ikke kan velges bort.',
20
- 'Du kan når som helst endre innstillingene og finne mer informasjon nederst på nettstedet under «Innstillinger for informasjonskapsler» og «Personvern og informasjonskapsler».',
21
- ],
22
- buttons: {
23
- back: 'Tilbake',
24
- save: 'Lagre innstillinger',
25
- },
26
- },
27
- },
28
- en: {
29
- contentPresentation: {
30
- title: 'Before you visit Oslo kommune ...',
31
- description: [
32
- 'This website uses cookies to make improvements. In this context, we need your consent to measure the traffic on the website in relation to statistics and feedback.',
33
- 'To read more about what we use cookies for, go to our privacy declaration which you will find at the bottom of our websites.',
34
- ],
35
- buttons: {
36
- accept: 'Yes, I accept',
37
- reject: 'Only necessary',
38
- settings: 'Go to settings',
39
- },
40
- },
41
- contentSettings: {
42
- title: 'Her kan du aktivt velge mellom ulike informasjonskapsler',
43
- description: [
44
- 'For å lese mer om hva vi bruker informasjonskapsler til gå til vår personvernserklering som du finner på våre nettsider',
45
- ],
46
- buttons: {
47
- back: 'Back',
48
- save: 'Save settings',
49
- },
50
- },
51
- },
52
- },
53
- cookies: [
54
- {
55
- name: 'statistics',
56
- defaultValue: null,
57
- i18n: {
58
- nb: {
59
- title: 'Statistikk og analyse',
60
- description:
61
- 'Jeg godtar at Oslo kommune bruker informasjonskapsler til å innhente data om statistikk og brukeradferd.',
62
- buttons: {
63
- yes: 'Ja',
64
- no: 'Nei',
65
- },
66
- },
67
- en: {
68
- title: 'Statistics and analytics',
69
- description: 'Select your preference for statistics and analytics',
70
- buttons: {
71
- yes: 'Yes',
72
- no: 'No',
73
- },
74
- },
75
- },
76
- },
77
- {
78
- name: 'survey',
79
- defaultValue: null,
80
- i18n: {
81
- nb: {
82
- title: 'Skriftlige tilbakemeldinger',
83
- description:
84
- 'Jeg godtar at Oslo kommune bruker informasjonskapsler til å vise responsfelt og registrere tilbakemeldinger.',
85
- buttons: {
86
- yes: 'Ja',
87
- no: 'Nei',
88
- },
89
- },
90
- en: {
91
- title: 'Survey',
92
- description: 'Select your preference for survey',
93
- buttons: {
94
- yes: 'Yes',
95
- no: 'No',
96
- },
97
- },
98
- },
99
- },
100
- {
101
- name: 'functional',
102
- defaultValue: true,
103
- i18n: {
104
- nb: {
105
- title: 'Funksjonelle informasjonskapsler',
106
- description:
107
- 'Jeg godtar at Oslo kommune bruker informasjonskapsler for å gjøre ulike funksjoner på nettstedet mulige (for eksempel skjemaer og barnehagevelger).',
108
- buttons: {
109
- yes: 'Ja',
110
- no: 'Nei',
111
- },
112
- },
113
- en: {
114
- title: 'Functional cookies',
115
- description:
116
- 'For the website to function optimally, we recommend that you accept these cookies',
117
- buttons: {
118
- yes: 'Yes',
119
- no: 'No',
120
- },
121
- },
122
- },
123
- },
124
- ],
125
- }
1
+ export { consentStrings } from 'shared-strings/consent'
@@ -47,7 +47,10 @@ export class PktDateTags extends PktElement {
47
47
  @close=${() => {
48
48
  this.dispatchEvent(new CustomEvent('date-tag-removed', { detail: date }))
49
49
  }}
50
- .ariaLabel=${`${this.strings?.calendar.deleteDate} ${fromISOtoLocal(date, this.dateformat)}`}
50
+ .ariaLabel=${`${this.pktStrings.get('datepicker').deleteDate} ${fromISOtoLocal(
51
+ date,
52
+ this.dateformat,
53
+ )}`}
51
54
  >
52
55
  <time datetime=${date}>${fromISOtoLocal(date, this.dateformat)}</time>
53
56
  </pkt-tag>`,
@@ -4,7 +4,7 @@ import { Ref, createRef } from 'lit/directives/ref.js'
4
4
  import { classMap } from 'lit/directives/class-map.js'
5
5
  import { PktElement } from '@/base-elements/element'
6
6
  import { cssUtils } from './datepicker-utils'
7
- import { IDatepickerStrings } from './datepicker-types'
7
+
8
8
  import type { TPktInputSize } from 'shared-types/size'
9
9
  import '@/components/icon'
10
10
 
@@ -15,7 +15,6 @@ import '@/components/icon'
15
15
  * used by all three datepicker input types (single, multiple, range).
16
16
  *
17
17
  * Subclasses must implement:
18
- * - `strings` property with component-specific defaults
19
18
  * - `render()` method with component-specific template
20
19
  */
21
20
  export abstract class PktDatepickerBase extends PktElement {
@@ -50,9 +49,6 @@ export abstract class PktDatepickerBase extends PktElement {
50
49
  @property({ type: Object })
51
50
  internals?: ElementInternals
52
51
 
53
- // Abstract property - must be implemented by subclasses
54
- abstract strings: IDatepickerStrings
55
-
56
52
  // Shared refs
57
53
  inputRef: Ref<HTMLInputElement> = createRef()
58
54
  btnRef: Ref<HTMLButtonElement> = createRef()
@@ -140,7 +136,7 @@ export abstract class PktDatepickerBase extends PktElement {
140
136
  ${this.btnRef}
141
137
  >
142
138
  <pkt-icon name="calendar"></pkt-icon>
143
- <span class="pkt-btn__text">${this.strings.calendar?.buttonAltText || 'Åpne kalender'}</span>
139
+ <span class="pkt-btn__text">${this.pktStrings.get('datepicker').openPicker}</span>
144
140
  </button>
145
141
  `
146
142
  }
@@ -5,7 +5,6 @@ import { ifDefined } from 'lit/directives/if-defined.js'
5
5
  import { ref } from 'lit/directives/ref.js'
6
6
  import { PktDatepickerBase } from './datepicker-base'
7
7
  import { keyboardUtils, formUtils } from './datepicker-utils'
8
- import { IDatepickerStrings, defaultSingleStrings } from './datepicker-types'
9
8
  import { isIOS } from 'shared-utils/device-utils'
10
9
  import '@/components/icon'
11
10
 
@@ -16,8 +15,6 @@ export class PktDatepickerMultiple extends PktDatepickerBase {
16
15
  @property({ type: Number })
17
16
  maxlength?: number
18
17
 
19
- @property({ type: Object })
20
- strings: IDatepickerStrings = defaultSingleStrings
21
18
 
22
19
  get isInputDisabled(): boolean {
23
20
  return (
@@ -5,7 +5,6 @@ import { ifDefined } from 'lit/directives/if-defined.js'
5
5
  import { Ref, createRef, ref } from 'lit/directives/ref.js'
6
6
  import { PktDatepickerBase } from './datepicker-base'
7
7
  import { keyboardUtils, formUtils, cssUtils } from './datepicker-utils'
8
- import { IDatepickerStrings, defaultRangeStrings } from './datepicker-types'
9
8
  import { isIOS } from 'shared-utils/device-utils'
10
9
  import '@/components/icon'
11
10
 
@@ -19,8 +18,6 @@ export class PktDatepickerRange extends PktDatepickerBase {
19
18
  @property({ type: Boolean })
20
19
  showRangeLabels: boolean = false
21
20
 
22
- @property({ type: Object })
23
- strings: IDatepickerStrings = defaultRangeStrings
24
21
 
25
22
  inputRefTo: Ref<HTMLInputElement> = createRef()
26
23
 
@@ -34,13 +31,13 @@ export class PktDatepickerRange extends PktDatepickerBase {
34
31
  return html`
35
32
  <div class="pkt-input__container">
36
33
  ${this.showRangeLabels
37
- ? html` <div class="pkt-input-prefix">${this.strings.generic?.from}</div> `
34
+ ? html` <div class="pkt-input-prefix">${this.pktStrings.get('generic').from}</div> `
38
35
  : nothing}
39
36
  <input
40
37
  class=${classMap(this.inputClasses)}
41
38
  .type=${this.inputType}
42
39
  id="${this.id}-input"
43
- aria-label="${this.label} ${this.strings.generic?.from ?? 'Fra'}"
40
+ aria-label="${this.label} ${this.pktStrings.get('generic').from}"
44
41
  .value=${this.value[0] ?? ''}
45
42
  min=${ifDefined(this.min)}
46
43
  max=${ifDefined(this.max)}
@@ -97,14 +94,14 @@ export class PktDatepickerRange extends PktDatepickerBase {
97
94
  ${ref(this.inputRef)}
98
95
  />
99
96
  <div class="${classMap(rangeLabelClasses)}" id="${this.id}-to-label">
100
- ${this.strings.generic?.to}
97
+ ${this.pktStrings.get('generic').to}
101
98
  </div>
102
99
  ${!this.showRangeLabels ? html` <div class="pkt-input-separator">–</div> ` : nothing}
103
100
  <input
104
101
  class=${classMap(this.inputClasses)}
105
102
  .type=${this.inputType}
106
103
  id="${this.id}-to"
107
- aria-label="${this.label} ${this.strings.generic?.to ?? 'Til'}"
104
+ aria-label="${this.label} ${this.pktStrings.get('generic').to}"
108
105
  .value=${this.value[1] ?? ''}
109
106
  min=${ifDefined(this.min)}
110
107
  max=${ifDefined(this.max)}
@@ -5,15 +5,12 @@ import { ifDefined } from 'lit/directives/if-defined.js'
5
5
  import { ref } from 'lit/directives/ref.js'
6
6
  import { PktDatepickerBase } from './datepicker-base'
7
7
  import { keyboardUtils, formUtils } from './datepicker-utils'
8
- import { IDatepickerStrings, defaultSingleStrings } from './datepicker-types'
9
8
  import { isIOS } from 'shared-utils/device-utils'
10
9
 
11
10
  export class PktDatepickerSingle extends PktDatepickerBase {
12
11
  @property({ type: String })
13
12
  value: string = ''
14
13
 
15
- @property({ type: Object })
16
- strings: IDatepickerStrings = defaultSingleStrings
17
14
 
18
15
  private dispatchManageValidity(input: HTMLInputElement) {
19
16
  this.dispatchEvent(
@@ -144,9 +144,9 @@ describe('datepicker-utils', () => {
144
144
  it('should set rangeUnderflow validity when value is below minimum', () => {
145
145
  const input = { value: '2024-01-01' } as HTMLInputElement
146
146
  const internals = { setValidity: vi.fn() }
147
- const strings = { forms: { messages: { rangeUnderflow: 'Too early' } } }
147
+ const messages = { dateUnderflow: 'Too early' }
148
148
 
149
- formUtils.validateDateInput(input, internals, '2024-01-10', null, strings)
149
+ formUtils.validateDateInput(input, internals, '2024-01-10', null, messages)
150
150
  expect(internals.setValidity).toHaveBeenCalledWith(
151
151
  { rangeUnderflow: true },
152
152
  'Too early',
@@ -157,9 +157,9 @@ describe('datepicker-utils', () => {
157
157
  it('should set rangeOverflow validity when value is above maximum', () => {
158
158
  const input = { value: '2024-01-31' } as HTMLInputElement
159
159
  const internals = { setValidity: vi.fn() }
160
- const strings = { forms: { messages: { rangeOverflow: 'Too late' } } }
160
+ const messages = { dateOverflow: 'Too late' }
161
161
 
162
- formUtils.validateDateInput(input, internals, null, '2024-01-20', strings)
162
+ formUtils.validateDateInput(input, internals, null, '2024-01-20', messages)
163
163
  expect(internals.setValidity).toHaveBeenCalledWith(
164
164
  { rangeOverflow: true },
165
165
  'Too late',
@@ -174,7 +174,7 @@ describe('datepicker-utils', () => {
174
174
  formUtils.validateDateInput(input, internals, '2024-01-10', null)
175
175
  expect(internals.setValidity).toHaveBeenCalledWith(
176
176
  { rangeUnderflow: true },
177
- 'Value is below minimum',
177
+ 'For tidlig dato',
178
178
  input,
179
179
  )
180
180
  })
@@ -1,6 +1,7 @@
1
1
  import { fromISOToDate, newDate } from 'shared-utils/date-utils'
2
2
  import { Ref } from 'lit/directives/ref.js'
3
3
  import { PktCalendar } from '@/components/calendar/calendar'
4
+ import { nb, type TPktStrings } from 'shared-strings'
4
5
  import {
5
6
  validateRangeOrder,
6
7
  sortDates,
@@ -66,23 +67,17 @@ export const formUtils = {
66
67
  internals: any,
67
68
  min?: string | null,
68
69
  max?: string | null,
69
- strings?: any,
70
+ messages?: Partial<TPktStrings['validation']>,
70
71
  ): void {
71
72
  const value = input.value
72
73
  if (!value) return
73
74
 
75
+ const validation = { ...nb.validation, ...messages }
76
+
74
77
  if (min && min > value) {
75
- internals.setValidity(
76
- { rangeUnderflow: true },
77
- strings?.forms?.messages?.rangeUnderflow || 'Value is below minimum',
78
- input,
79
- )
78
+ internals.setValidity({ rangeUnderflow: true }, validation.dateUnderflow, input)
80
79
  } else if (max && max < value) {
81
- internals.setValidity(
82
- { rangeOverflow: true },
83
- strings?.forms?.messages?.rangeOverflow || 'Value is above maximum',
84
- input,
85
- )
80
+ internals.setValidity({ rangeOverflow: true }, validation.dateOverflow, input)
86
81
  }
87
82
  },
88
83
  }
@@ -391,7 +391,13 @@ export class PktDatepicker extends PktInputElement<Props> {
391
391
  }}
392
392
  @manage-validity=${(e: CustomEvent) => this.manageValidity(e.detail)}
393
393
  @validate-date-input=${(e: CustomEvent) => {
394
- formUtils.validateDateInput(e.detail, this.internals, this.min, this.max, this.strings)
394
+ formUtils.validateDateInput(
395
+ e.detail,
396
+ this.internals,
397
+ this.min,
398
+ this.max,
399
+ this.pktStrings.get('validation'),
400
+ )
395
401
  }}
396
402
  @handle-date-select=${(e: CustomEvent) => {
397
403
  const date = fromISOToDate(e.detail)
@@ -497,6 +503,7 @@ export class PktDatepicker extends PktInputElement<Props> {
497
503
 
498
504
  return html`
499
505
  <pkt-input-wrapper
506
+ .strings=${this.strings}
500
507
  label="${this.label}"
501
508
  forId="${this.id}-input"
502
509
  ?counter=${this.multiple && !!this.maxlength}
@@ -1,7 +1,6 @@
1
1
  import { PropertyValues } from 'lit'
2
2
  import { property, state } from 'lit/decorators.js'
3
3
 
4
- import { defaultFileUploadStrings } from 'shared-types'
5
4
  import {
6
5
  countFileTransferStates,
7
6
  formatFileSize,
@@ -29,6 +28,7 @@ import type {
29
28
  TUploadStrategy,
30
29
  } from './fileupload-types'
31
30
  import { uuidish } from 'shared-utils/utils'
31
+ import { formatString, plural } from 'shared-strings'
32
32
 
33
33
  export abstract class PktFileUploadBase
34
34
  extends PktElement<IPktFileUpload>
@@ -92,8 +92,9 @@ export abstract class PktFileUploadBase
92
92
  /** Show "Valgfritt" tag in the input wrapper. */
93
93
  @property({ type: Boolean, attribute: 'optional-tag' }) optionalTag = false
94
94
  /** Label text for the optional tag. */
95
+ /** @deprecated Bruk `strings={{ forms: { optional: '…' } }}` eller `setPktStrings()`. */
95
96
  @property({ type: String, attribute: 'optional-text' })
96
- optionalText: string = specs.props.optionalText.default
97
+ optionalText?: string
97
98
  /** Show "Må fylles ut" tag in the input wrapper. */
98
99
  @property({ type: Boolean, attribute: 'required-tag' }) requiredTag = false
99
100
  /** Label text for the required tag. */
@@ -119,7 +120,9 @@ export abstract class PktFileUploadBase
119
120
  private thumbnailFileById = new Map<string, File>()
120
121
  private connectedForm: HTMLFormElement | null = null
121
122
  private addedAnnouncementTimer: ReturnType<typeof setTimeout> | null = null
122
- private readonly requiredSelectionMessage = defaultFileUploadStrings.requiredMissing
123
+ private get requiredSelectionMessage() {
124
+ return this.pktStrings.get('fileupload').requiredMissing
125
+ }
123
126
 
124
127
  protected get isControlled() {
125
128
  return this.value !== undefined
@@ -297,10 +300,13 @@ export abstract class PktFileUploadBase
297
300
  },
298
301
  }))
299
302
 
303
+ const fileuploadStrings = this.pktStrings.get('fileupload')
300
304
  this.setAddedAnnouncement(
301
305
  newItems.length === 1
302
- ? defaultFileUploadStrings.srFileAdded(newItems[0].attributes.targetFilename)
303
- : defaultFileUploadStrings.srFilesAdded(newItems.length),
306
+ ? formatString(fileuploadStrings.srFileAdded, {
307
+ filename: newItems[0].attributes.targetFilename,
308
+ })
309
+ : formatString(fileuploadStrings.srFilesAdded, { count: newItems.length }),
304
310
  )
305
311
 
306
312
  const currentFiles = this.getCurrentFiles()
@@ -448,7 +454,8 @@ export abstract class PktFileUploadBase
448
454
  this.dispatchEvent(validationEvent)
449
455
 
450
456
  if (eventDetail.errorMessage) return eventDetail.errorMessage
451
- if (validationEvent.defaultPrevented) return defaultFileUploadStrings.genericValidationRejection
457
+ if (validationEvent.defaultPrevented)
458
+ return this.pktStrings.get('fileupload').genericValidationRejection
452
459
  return null
453
460
  }
454
461
 
@@ -523,11 +530,12 @@ export abstract class PktFileUploadBase
523
530
  this.transfers,
524
531
  )
525
532
  if (totalCount === 0 || uploadedCount === 0) return ''
526
- return defaultFileUploadStrings.srFilesUploadedOfTotal(
527
- uploadedCount,
528
- totalCount,
529
- defaultFileUploadStrings.fileLabel(totalCount),
530
- )
533
+ const fileuploadStrings = this.pktStrings.get('fileupload')
534
+ return formatString(fileuploadStrings.srFilesUploadedOfTotal, {
535
+ uploaded: uploadedCount,
536
+ total: totalCount,
537
+ label: plural(totalCount, fileuploadStrings.fileLabelOne, fileuploadStrings.fileLabelOther),
538
+ })
531
539
  }
532
540
 
533
541
  /** Assertive summary for transfer errors (parity with React `FileUpload`). */
@@ -537,11 +545,12 @@ export abstract class PktFileUploadBase
537
545
  this.transfers,
538
546
  )
539
547
  if (totalCount === 0 || failedCount === 0) return ''
540
- return defaultFileUploadStrings.srFilesFailedOfTotal(
541
- failedCount,
542
- totalCount,
543
- defaultFileUploadStrings.fileLabel(totalCount),
544
- )
548
+ const fileuploadStrings = this.pktStrings.get('fileupload')
549
+ return formatString(fileuploadStrings.srFilesFailedOfTotal, {
550
+ failed: failedCount,
551
+ total: totalCount,
552
+ label: plural(totalCount, fileuploadStrings.fileLabelOne, fileuploadStrings.fileLabelOther),
553
+ })
545
554
  }
546
555
 
547
556
  private syncFormSubmitListener() {
@@ -10,6 +10,7 @@ import type {
10
10
  TQueueOperationContext,
11
11
  TTransferProgress,
12
12
  } from './fileupload-types'
13
+ import { formatString, nb } from 'shared-strings'
13
14
 
14
15
  type TRenderQueueItemProps = {
15
16
  file: FileItem
@@ -449,7 +450,7 @@ const renderThumbnailIdleMain = ({
449
450
  <button
450
451
  type="button"
451
452
  class="pkt-fileupload__queue-display__item__thumbnail__image-wrapper pkt-btn pkt-btn--medium pkt-btn--secondary pkt-btn--label-only"
452
- aria-label=${`Forhåndsvis bilde ${filename}`}
453
+ aria-label=${formatString(nb.fileupload.previewImage, { filename })}
453
454
  ?disabled=${!isInteractive}
454
455
  @click=${() => {
455
456
  if (!isInteractive || !onOpenPreview) return
@@ -368,6 +368,22 @@ describe('PktFileUpload', () => {
368
368
  expect(withAcceptText).toContain('Format: PNG, application/pdf')
369
369
  })
370
370
 
371
+ test('uses drop zone text from the strings property', async () => {
372
+ const { fileupload } = await createFileUploadTest({ multiple: true })
373
+ fileupload.strings = {
374
+ fileupload: {
375
+ dropZoneDragMultiple: 'Dra dokumentene hit eller',
376
+ dropZoneOpenFileDialogMultiple: 'bla i filene',
377
+ },
378
+ }
379
+ await fileupload.updateComplete
380
+
381
+ const title = fileupload.querySelector('.pkt-fileupload__drop-zone__placeholder__title')
382
+ expect(title?.textContent).toContain('Dra dokumentene hit eller')
383
+ expect(title?.textContent).toContain('bla i filene')
384
+ expect(title?.textContent).not.toContain('velg filer')
385
+ })
386
+
371
387
  test('sets required only on native file input in form strategy', async () => {
372
388
  const { fileupload: formUpload } = await createFileUploadTest({
373
389
  required: true,