@oslokommune/punkt-react 17.1.3 → 18.1.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 +35 -0
- package/dist/components/accordion/Accordion.d.ts +2 -12
- package/dist/components/accordion/AccordionItem.d.ts +2 -8
- package/dist/components/alert/Alert.d.ts +4 -3
- package/dist/components/backlink/BackLink.d.ts +3 -10
- package/dist/components/breadcrumbs/Breadcrumbs.d.ts +7 -13
- package/dist/components/calendar/types.d.ts +14 -12
- package/dist/components/checkbox/Checkbox.d.ts +5 -0
- package/dist/components/combobox/types.d.ts +5 -0
- package/dist/components/consent/strings.d.ts +1 -85
- package/dist/components/datepicker/DateTags.d.ts +2 -2
- package/dist/components/datepicker/types.d.ts +6 -3
- package/dist/components/fileupload/DropZone.d.ts +3 -0
- package/dist/components/fileupload/FileUpload.d.ts +9 -61
- package/dist/components/header/types.d.ts +5 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/inputwrapper/InputWrapper.d.ts +6 -0
- package/dist/components/link/Link.d.ts +2 -6
- package/dist/components/radio/RadioButton.d.ts +5 -0
- package/dist/components/searchinput/SearchInput.d.ts +4 -0
- package/dist/components/select/Select.d.ts +3 -0
- package/dist/components/strings/PktStringsProvider.d.ts +21 -0
- package/dist/components/strings/strings-override.test.d.ts +1 -0
- package/dist/components/tag/Tag.d.ts +3 -0
- package/dist/components/textarea/Textarea.d.ts +3 -0
- package/dist/components/textinput/Textinput.d.ts +3 -0
- package/dist/components/timepicker/types.d.ts +3 -2
- package/dist/components/timepicker/useTimepickerState.d.ts +9 -1
- package/dist/punkt-react.cjs +1 -1
- package/dist/punkt-react.js +2982 -2757
- package/dist/shared-strings/consent.d.ts +71 -0
- package/dist/shared-strings/format.d.ts +21 -0
- package/dist/shared-strings/index.d.ts +14 -0
- package/dist/shared-strings/nb.d.ts +121 -0
- package/dist/shared-strings/resolve.d.ts +12 -0
- package/dist/shared-strings/store.d.ts +27 -0
- package/dist/shared-strings/types.d.ts +40 -0
- package/dist/shared-types/accordion.d.ts +58 -0
- package/dist/shared-types/alert.d.ts +20 -0
- package/dist/shared-types/aria.d.ts +2 -1
- package/dist/shared-types/backlink.d.ts +23 -0
- package/dist/shared-types/breadcrumbs.d.ts +33 -0
- package/dist/shared-types/fileupload.d.ts +55 -28
- package/dist/shared-types/footer.d.ts +10 -0
- package/dist/shared-types/form-field.d.ts +45 -0
- package/dist/shared-types/header-footer.d.ts +2 -0
- package/dist/shared-types/index.d.ts +15 -6
- package/dist/shared-types/render-link.d.ts +22 -0
- package/dist/shared-types/skin.d.ts +2 -9
- package/dist/shared-utils/combobox/index.d.ts +1 -0
- package/dist/shared-utils/combobox/input-utils.d.ts +2 -1
- package/dist/shared-utils/combobox/messages.d.ts +7 -0
- package/dist/shared-utils/combobox/option-utils.d.ts +2 -1
- package/dist/shared-utils/combobox/selection-manager.d.ts +3 -2
- package/package.json +4 -4
- package/src/components/accordion/Accordion.tsx +2 -12
- package/src/components/accordion/AccordionItem.tsx +2 -8
- package/src/components/alert/Alert.tsx +12 -5
- package/src/components/backlink/BackLink.tsx +12 -12
- package/src/components/breadcrumbs/Breadcrumbs.tsx +19 -15
- package/src/components/calendar/Calendar.core.test.tsx +7 -5
- package/src/components/calendar/types.ts +14 -34
- package/src/components/calendar/useCalendarState.ts +16 -2
- package/src/components/checkbox/Checkbox.tsx +20 -4
- package/src/components/combobox/Combobox.tsx +1 -0
- package/src/components/combobox/Listbox.tsx +4 -2
- package/src/components/combobox/types.ts +6 -0
- package/src/components/combobox/useComboboxState.ts +15 -8
- package/src/components/consent/strings.ts +1 -125
- package/src/components/datepicker/DateTags.tsx +4 -3
- package/src/components/datepicker/Datepicker.accessibility.test.tsx +2 -2
- package/src/components/datepicker/Datepicker.tsx +1 -0
- package/src/components/datepicker/DatepickerInputs.tsx +6 -6
- package/src/components/datepicker/types.ts +6 -3
- package/src/components/datepicker/useDatepickerState.ts +4 -5
- package/src/components/fileupload/DropZone.tsx +20 -15
- package/src/components/fileupload/FileUpload.test.tsx +37 -0
- package/src/components/fileupload/FileUpload.tsx +63 -73
- package/src/components/header/HeaderGlobal.tsx +5 -2
- package/src/components/header/HeaderService.tsx +12 -7
- package/src/components/header/types.ts +6 -0
- package/src/components/index.ts +10 -0
- package/src/components/inputwrapper/InputWrapper.tsx +28 -6
- package/src/components/link/Link.tsx +2 -6
- package/src/components/radio/RadioButton.tsx +18 -2
- package/src/components/searchinput/SearchInput.tsx +13 -3
- package/src/components/select/Select.tsx +5 -0
- package/src/components/strings/PktStringsProvider.tsx +38 -0
- package/src/components/strings/strings-override.test.tsx +130 -0
- package/src/components/tag/Tag.tsx +13 -2
- package/src/components/textarea/Textarea.tsx +5 -0
- package/src/components/textinput/Textinput.tsx +5 -0
- package/src/components/timepicker/Timepicker.tsx +1 -0
- package/src/components/timepicker/types.ts +3 -2
- package/src/components/timepicker/useTimepickerState.ts +20 -22
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
useState,
|
|
28
28
|
} from 'react'
|
|
29
29
|
|
|
30
|
-
import {
|
|
30
|
+
import { type IPktFileUploadBase } from 'shared-types'
|
|
31
31
|
import {
|
|
32
32
|
countFileTransferStates,
|
|
33
33
|
resolveAcceptAttribute,
|
|
@@ -52,91 +52,51 @@ import {
|
|
|
52
52
|
TItemRenderer,
|
|
53
53
|
PktFileUploadContext,
|
|
54
54
|
TQueueItemOperation,
|
|
55
|
-
TUploadStrategy,
|
|
56
55
|
} from './types'
|
|
56
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
57
|
+
import { formatString, plural } from 'shared-strings'
|
|
57
58
|
|
|
58
|
-
// Re-export for external use
|
|
59
59
|
export { formatFileSize, parseFileSize } from 'shared-utils/fileupload'
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* Shared props for `PktFileUpload` (both `form` and `custom` strategies).
|
|
63
63
|
*/
|
|
64
|
-
interface IBaseFileUploadProps
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
interface IBaseFileUploadProps
|
|
65
|
+
extends
|
|
66
|
+
Omit<
|
|
67
|
+
IPktFileUploadBase,
|
|
68
|
+
| 'helptext'
|
|
69
|
+
| 'helptextDropdown'
|
|
70
|
+
| 'itemRenderer'
|
|
71
|
+
| 'extraOperations'
|
|
72
|
+
| 'value'
|
|
73
|
+
| 'defaultValue'
|
|
74
|
+
| 'onFilesChanged'
|
|
75
|
+
| 'onFileUploadRequested'
|
|
76
|
+
>,
|
|
77
|
+
Omit<
|
|
78
|
+
InputHTMLAttributes<HTMLInputElement>,
|
|
79
|
+
'checked' | 'size' | 'type' | 'value' | 'onChange' | 'width' | 'defaultValue' | 'name'
|
|
80
|
+
> {
|
|
68
81
|
/** Called with the next file list when files are added/removed/updated. */
|
|
69
82
|
onFilesChanged?: (files: TFileItemList) => void
|
|
70
|
-
/** Allow selecting/dropping multiple files. */
|
|
71
|
-
multiple?: boolean
|
|
72
|
-
/** Controlled value (recommended). */
|
|
73
|
-
value?: TFileItemList
|
|
74
|
-
/** Upload mode. Defaults to `"form"`. */
|
|
75
|
-
uploadStrategy?: TUploadStrategy
|
|
76
|
-
/** Field name. Used for native input (`form`) or hidden ID inputs (`custom`). */
|
|
77
|
-
name: string
|
|
78
|
-
/** Enable comment operation (disabled automatically in thumbnail view). */
|
|
79
|
-
addCommentsEnabled?: boolean
|
|
80
|
-
/** Enable rename operation (disabled automatically in thumbnail view). */
|
|
81
|
-
renameFilesEnabled?: boolean
|
|
82
|
-
/** Renderer for queue items (`filename`, `thumbnail`, or custom function). */
|
|
83
|
-
itemRenderer?: keyof typeof ItemRenderers | TItemRenderer
|
|
84
|
-
/** Number of trailing characters to keep when truncating long filenames. */
|
|
85
|
-
truncateTail?: number
|
|
86
83
|
/**
|
|
87
84
|
* Called immediately when a file is added in `uploadStrategy="custom"`.
|
|
88
85
|
* Use it to start uploading the file and update `transfers`.
|
|
89
86
|
*/
|
|
90
87
|
onFileUploadRequested?: (fileItem: FileItem) => void
|
|
88
|
+
/** Controlled value (recommended). */
|
|
89
|
+
value?: TFileItemList
|
|
91
90
|
/** Uncontrolled initial value (use either `value` or `defaultValue`, not both). */
|
|
92
91
|
defaultValue?: TFileItemList
|
|
93
|
-
/**
|
|
94
|
-
|
|
95
|
-
/** Stretch to full container width (drop zone + queue). */
|
|
96
|
-
fullwidth?: boolean
|
|
97
|
-
/** Allowed formats for built-in validation (extensions like `pdf`, or MIME patterns like `image/*`). */
|
|
98
|
-
allowedFormats?: string[]
|
|
99
|
-
/** Custom message for invalid format. Use `{formats}` placeholder. */
|
|
100
|
-
formatErrorMessage?: string
|
|
101
|
-
/** Max file size (e.g. `"5MB"` or bytes). */
|
|
102
|
-
maxFileSize?: string | number
|
|
103
|
-
/** Custom message for size validation. Use `{maxSize}` placeholder. */
|
|
104
|
-
sizeErrorMessage?: string
|
|
105
|
-
/** Optional additional validation hook (runs after built-in format/size checks). Return string to block. */
|
|
106
|
-
onFileValidation?: (file: File) => string | null
|
|
107
|
-
/**
|
|
108
|
-
* Low-level validation escape hatch — Lit parity with the `file-validate` CustomEvent.
|
|
109
|
-
* Receives a detail object whose `errorMessage` can be mutated; set it to reject the file.
|
|
110
|
-
*/
|
|
111
|
-
onFileValidate?: (detail: TFileValidateDetail) => void
|
|
112
|
-
/** External/programmatic error message shown under the component. */
|
|
113
|
-
errorMessage?: string
|
|
114
|
-
/** External error flag (combined with internal validation errors). */
|
|
115
|
-
hasError?: boolean
|
|
116
|
-
/** Disable the whole component (no interaction). */
|
|
117
|
-
disabled?: boolean
|
|
118
|
-
/** Optional label/title (wraps the component in `PktInputWrapper`). */
|
|
119
|
-
label?: string
|
|
92
|
+
/** Renderer for queue items (`filename`, `thumbnail`, or custom function). */
|
|
93
|
+
itemRenderer?: keyof typeof ItemRenderers | TItemRenderer
|
|
120
94
|
/** Help text under the label. */
|
|
121
95
|
helptext?: string | ReactNode
|
|
122
96
|
/** Expandable help text (dropdown). */
|
|
123
97
|
helptextDropdown?: string | ReactNode
|
|
124
|
-
/**
|
|
125
|
-
|
|
126
|
-
/** Shows an "optional" tag. */
|
|
127
|
-
optionalTag?: boolean
|
|
128
|
-
/** Label text for the optional tag. */
|
|
129
|
-
optionalText?: string
|
|
130
|
-
/** Shows a "required" tag. */
|
|
131
|
-
requiredTag?: boolean
|
|
132
|
-
/** Label text for the required tag. */
|
|
133
|
-
requiredText?: string
|
|
134
|
-
/** Extra tag next to the label. */
|
|
135
|
-
tagText?: string | null
|
|
136
|
-
/** Marks the upload as required. Native validation is used in `form`; submit validation in `custom`. */
|
|
137
|
-
required?: boolean
|
|
138
|
-
/** Enable image preview modal (only applies to thumbnail renderer). */
|
|
139
|
-
enableImagePreview?: boolean
|
|
98
|
+
/** Extra operations appended after built-ins (rename/comment). */
|
|
99
|
+
extraOperations?: Array<TQueueItemOperation>
|
|
140
100
|
}
|
|
141
101
|
|
|
142
102
|
interface IFileInputFileUploadProps
|
|
@@ -193,6 +153,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
193
153
|
enableImagePreview = false,
|
|
194
154
|
['aria-describedby']: ariaDescribedByProp,
|
|
195
155
|
['aria-invalid']: ariaInvalidProp,
|
|
156
|
+
strings,
|
|
196
157
|
...props
|
|
197
158
|
}: IPktFileUpload,
|
|
198
159
|
forwardedRef,
|
|
@@ -203,6 +164,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
203
164
|
useImperativeHandle(forwardedRef, () => fileInputRef.current as HTMLInputElement, [])
|
|
204
165
|
|
|
205
166
|
const transfers = 'transfers' in props ? props.transfers : undefined
|
|
167
|
+
const fileuploadStrings = usePktStrings(['fileupload'], strings).fileupload
|
|
206
168
|
const [validationError, setValidationError] = useState<string | null>(null)
|
|
207
169
|
const [addedAnnouncement, setAddedAnnouncement] = useState('')
|
|
208
170
|
|
|
@@ -262,7 +224,10 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
262
224
|
uploadStrategy,
|
|
263
225
|
required,
|
|
264
226
|
currentFileCount: value.length,
|
|
265
|
-
onMissingFiles: useCallback(
|
|
227
|
+
onMissingFiles: useCallback(
|
|
228
|
+
() => setValidationError(fileuploadStrings.requiredMissing),
|
|
229
|
+
[fileuploadStrings],
|
|
230
|
+
),
|
|
266
231
|
})
|
|
267
232
|
|
|
268
233
|
const itemRenderer = typeof itemRendererProp === 'string' ? ItemRenderers[itemRendererProp] : itemRendererProp
|
|
@@ -293,8 +258,10 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
293
258
|
const acceptedItems: TFileItemList = multiple ? addedFileItems : [addedFileItems[0]]
|
|
294
259
|
setAddedAnnouncement(
|
|
295
260
|
acceptedItems.length === 1
|
|
296
|
-
?
|
|
297
|
-
|
|
261
|
+
? formatString(fileuploadStrings.srFileAdded, {
|
|
262
|
+
filename: acceptedItems[0].attributes.targetFilename,
|
|
263
|
+
})
|
|
264
|
+
: formatString(fileuploadStrings.srFilesAdded, { count: acceptedItems.length }),
|
|
298
265
|
)
|
|
299
266
|
|
|
300
267
|
if (!isControlled) setInternalValue(newValue)
|
|
@@ -306,7 +273,16 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
306
273
|
})
|
|
307
274
|
}
|
|
308
275
|
},
|
|
309
|
-
[
|
|
276
|
+
[
|
|
277
|
+
onFilesChanged,
|
|
278
|
+
value,
|
|
279
|
+
multiple,
|
|
280
|
+
uploadStrategy,
|
|
281
|
+
onFileUploadRequested,
|
|
282
|
+
validateFile,
|
|
283
|
+
isControlled,
|
|
284
|
+
fileuploadStrings,
|
|
285
|
+
],
|
|
310
286
|
)
|
|
311
287
|
|
|
312
288
|
const onFileUpdated = useCallback(
|
|
@@ -346,7 +322,11 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
346
322
|
[value, transfers],
|
|
347
323
|
)
|
|
348
324
|
|
|
349
|
-
const totalLabel =
|
|
325
|
+
const totalLabel = plural(
|
|
326
|
+
totalCount,
|
|
327
|
+
fileuploadStrings.fileLabelOne,
|
|
328
|
+
fileuploadStrings.fileLabelOther,
|
|
329
|
+
)
|
|
350
330
|
|
|
351
331
|
const fileUploadContent = (
|
|
352
332
|
<div
|
|
@@ -362,13 +342,21 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
362
342
|
<div className="pkt-sr-only" aria-live="polite" aria-atomic="true">
|
|
363
343
|
{addedAnnouncement ||
|
|
364
344
|
(totalCount > 0 && uploadedCount > 0
|
|
365
|
-
?
|
|
345
|
+
? formatString(fileuploadStrings.srFilesUploadedOfTotal, {
|
|
346
|
+
uploaded: uploadedCount,
|
|
347
|
+
total: totalCount,
|
|
348
|
+
label: totalLabel,
|
|
349
|
+
})
|
|
366
350
|
: '')}
|
|
367
351
|
</div>
|
|
368
352
|
<div className="pkt-sr-only" aria-live="assertive" aria-atomic="true">
|
|
369
353
|
{totalCount > 0 &&
|
|
370
354
|
failedCount > 0 &&
|
|
371
|
-
|
|
355
|
+
formatString(fileuploadStrings.srFilesFailedOfTotal, {
|
|
356
|
+
failed: failedCount,
|
|
357
|
+
total: totalCount,
|
|
358
|
+
label: totalLabel,
|
|
359
|
+
})}
|
|
372
360
|
</div>
|
|
373
361
|
|
|
374
362
|
<DropZone
|
|
@@ -387,6 +375,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
387
375
|
aria-invalid={isAriaInvalid ? 'true' : undefined}
|
|
388
376
|
aria-describedby={describedBy}
|
|
389
377
|
hasVisibleLabel={!!label}
|
|
378
|
+
strings={strings}
|
|
390
379
|
/>
|
|
391
380
|
{hasError && errorMessage && (
|
|
392
381
|
<PktAlert
|
|
@@ -423,6 +412,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
423
412
|
return (
|
|
424
413
|
<div className="pkt-fileupload-component">
|
|
425
414
|
<PktInputWrapper
|
|
415
|
+
strings={strings}
|
|
426
416
|
forId={id}
|
|
427
417
|
label={label}
|
|
428
418
|
helptext={helptext}
|
|
@@ -14,6 +14,7 @@ import { PktHeaderUserMenu } from '../headerUserMenu/UserMenu'
|
|
|
14
14
|
import { PktIcon } from '../icon/Icon'
|
|
15
15
|
import { PktSearchInput } from '../searchinput/SearchInput'
|
|
16
16
|
import { IPktHeaderGlobal } from './types'
|
|
17
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
17
18
|
|
|
18
19
|
const defaultLogoPath = 'https://punkt-cdn.oslo.kommune.no/latest/logos/'
|
|
19
20
|
|
|
@@ -56,6 +57,7 @@ export const PktHeaderGlobal = forwardRef(
|
|
|
56
57
|
tabletBreakpoint = 1024,
|
|
57
58
|
onDataLoaded,
|
|
58
59
|
onDataError,
|
|
60
|
+
strings,
|
|
59
61
|
}: IPktHeaderGlobal,
|
|
60
62
|
ref: ForwardedRef<HTMLElement>,
|
|
61
63
|
) => {
|
|
@@ -251,10 +253,11 @@ export const PktHeaderGlobal = forwardRef(
|
|
|
251
253
|
|
|
252
254
|
const showLogoutInUserMenu = logOutButtonPlacement === 'userMenu' || logOutButtonPlacement === 'both'
|
|
253
255
|
|
|
256
|
+
const headerStrings = usePktStrings(['header'], strings).header
|
|
254
257
|
const search = localeData?.search
|
|
255
258
|
const contact = localeData?.header?.contact
|
|
256
|
-
const menuText = localeData?.i18n?.menu ||
|
|
257
|
-
const searchText = localeData?.i18n?.search ||
|
|
259
|
+
const menuText = localeData?.i18n?.menu || headerStrings.menu
|
|
260
|
+
const searchText = localeData?.i18n?.search || headerStrings.search
|
|
258
261
|
const isMenuOpen = openMenu === 'menu'
|
|
259
262
|
const isUserOpen = openMenu === 'user'
|
|
260
263
|
const isSearchOpen = openMenu === 'search'
|
|
@@ -26,6 +26,7 @@ import { PktTextinput } from '../textinput/Textinput'
|
|
|
26
26
|
import { DEFAULT_HEADER_LOGO_LINK } from 'shared-types'
|
|
27
27
|
|
|
28
28
|
import { IPktHeader, Representing, THeaderMenu, User, UserMenuFooterItem, UserMenuItem } from './types'
|
|
29
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
29
30
|
|
|
30
31
|
export type { Representing, THeaderMenu, User, UserMenuFooterItem, UserMenuItem }
|
|
31
32
|
export type IPktHeaderService = IPktHeader
|
|
@@ -58,7 +59,7 @@ export const PktHeaderService = forwardRef(
|
|
|
58
59
|
onSearch,
|
|
59
60
|
onSearchChange,
|
|
60
61
|
searchValue,
|
|
61
|
-
searchPlaceholder
|
|
62
|
+
searchPlaceholder,
|
|
62
63
|
representing,
|
|
63
64
|
serviceLink,
|
|
64
65
|
serviceClick,
|
|
@@ -77,6 +78,7 @@ export const PktHeaderService = forwardRef(
|
|
|
77
78
|
scrollBehavior = 'hide',
|
|
78
79
|
slotMenuVariant = 'icon-only',
|
|
79
80
|
slotMenuText = 'Meny',
|
|
81
|
+
strings,
|
|
80
82
|
}: IPktHeaderService,
|
|
81
83
|
ref: ForwardedRef<HTMLDivElement>,
|
|
82
84
|
) => {
|
|
@@ -92,6 +94,9 @@ export const PktHeaderService = forwardRef(
|
|
|
92
94
|
}
|
|
93
95
|
}, [userMenuFooter])
|
|
94
96
|
|
|
97
|
+
const headerStrings = usePktStrings(['header'], strings).header
|
|
98
|
+
const resolvedSearchPlaceholder = searchPlaceholder ?? headerStrings.search
|
|
99
|
+
|
|
95
100
|
// Warning for userOptions - no longer available
|
|
96
101
|
useEffect(() => {
|
|
97
102
|
if (userOptions) {
|
|
@@ -341,7 +346,7 @@ export const PktHeaderService = forwardRef(
|
|
|
341
346
|
onClick={() => handleMenuToggle('slot')}
|
|
342
347
|
aria-expanded={openMenu === 'slot'}
|
|
343
348
|
aria-controls="mobile-slot-menu"
|
|
344
|
-
aria-label=
|
|
349
|
+
aria-label={headerStrings.openMenu}
|
|
345
350
|
>
|
|
346
351
|
{slotMenuText}
|
|
347
352
|
</PktButton>
|
|
@@ -375,7 +380,7 @@ export const PktHeaderService = forwardRef(
|
|
|
375
380
|
state={openMenu === 'search' ? 'active' : 'normal'}
|
|
376
381
|
aria-expanded={openMenu === 'search'}
|
|
377
382
|
aria-controls="mobile-search-menu"
|
|
378
|
-
aria-label=
|
|
383
|
+
aria-label={headerStrings.openSearch}
|
|
379
384
|
>
|
|
380
385
|
Søk
|
|
381
386
|
</PktButton>
|
|
@@ -392,9 +397,9 @@ export const PktHeaderService = forwardRef(
|
|
|
392
397
|
id="mobile-search-menu"
|
|
393
398
|
className="pkt-header-service__search-input"
|
|
394
399
|
type="search"
|
|
395
|
-
label=
|
|
400
|
+
label={headerStrings.search}
|
|
396
401
|
useWrapper={false}
|
|
397
|
-
placeholder={
|
|
402
|
+
placeholder={resolvedSearchPlaceholder}
|
|
398
403
|
value={searchValue}
|
|
399
404
|
autoFocus
|
|
400
405
|
fullwidth={true}
|
|
@@ -413,9 +418,9 @@ export const PktHeaderService = forwardRef(
|
|
|
413
418
|
id="header-service-search"
|
|
414
419
|
className="pkt-header-service__search-input"
|
|
415
420
|
type="search"
|
|
416
|
-
label=
|
|
421
|
+
label={headerStrings.search}
|
|
417
422
|
useWrapper={false}
|
|
418
|
-
placeholder={
|
|
423
|
+
placeholder={resolvedSearchPlaceholder}
|
|
419
424
|
value={searchValue}
|
|
420
425
|
onChange={(e: ChangeEvent<HTMLInputElement>) => onSearchChange?.(e.target.value)}
|
|
421
426
|
onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
THeaderMenuLocale,
|
|
10
10
|
IPktHeaderGlobal as ISharedPktHeaderGlobal,
|
|
11
11
|
} from 'shared-types'
|
|
12
|
+
import type { TPktStringsOverride } from 'shared-strings'
|
|
12
13
|
|
|
13
14
|
export type {
|
|
14
15
|
THeaderMenu,
|
|
@@ -53,6 +54,9 @@ export interface UserMenuFooterItem {
|
|
|
53
54
|
}
|
|
54
55
|
|
|
55
56
|
export interface IPktHeader {
|
|
57
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
58
|
+
strings?: TPktStringsOverride
|
|
59
|
+
|
|
56
60
|
children?: ReactNode
|
|
57
61
|
/** Additional CSS class name(s) to apply to the header */
|
|
58
62
|
className?: string
|
|
@@ -127,6 +131,8 @@ export interface IPktHeader {
|
|
|
127
131
|
* (in shared-types) with React-specific callbacks and DOM props.
|
|
128
132
|
*/
|
|
129
133
|
export interface IPktHeaderGlobal extends ISharedPktHeaderGlobal {
|
|
134
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
135
|
+
strings?: TPktStringsOverride
|
|
130
136
|
/** Additional CSS class name(s) to apply to the header */
|
|
131
137
|
className?: string
|
|
132
138
|
/** Set dark mode on the header */
|
package/src/components/index.ts
CHANGED
|
@@ -52,3 +52,13 @@ export { PktTextinput } from './textinput/Textinput'
|
|
|
52
52
|
export { PktTimepicker } from './timepicker/Timepicker'
|
|
53
53
|
export * from './interfaces'
|
|
54
54
|
export * from './types'
|
|
55
|
+
export { PktStringsProvider } from './strings/PktStringsProvider'
|
|
56
|
+
export type { IPktStringsProvider } from './strings/PktStringsProvider'
|
|
57
|
+
export {
|
|
58
|
+
getPktStrings,
|
|
59
|
+
nb as pktStringsNb,
|
|
60
|
+
resetPktStrings,
|
|
61
|
+
setPktStrings,
|
|
62
|
+
subscribePktStrings,
|
|
63
|
+
} from 'shared-strings'
|
|
64
|
+
export type { TPktStrings, TPktStringsNamespace, TPktStringsOverride, IPktStringsProps } from 'shared-strings'
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { ForwardedRef, forwardRef, type ReactNode, RefAttributes } from 'react'
|
|
4
4
|
|
|
5
5
|
import { useFocusModality } from '../../hooks/useFocusModality'
|
|
6
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
7
|
+
import { formatString, type TPktStringsOverride } from 'shared-strings'
|
|
6
8
|
import { counterStatusId, isCounterExceeded } from 'shared-utils/forms/described-by'
|
|
7
9
|
import { PktAlert } from '../alert/Alert'
|
|
8
10
|
import { PktHelptext } from '../helptext/Helptext'
|
|
@@ -12,13 +14,16 @@ export interface IPktInputWrapper extends RefAttributes<HTMLElement> {
|
|
|
12
14
|
label: string
|
|
13
15
|
helptext?: string | ReactNode
|
|
14
16
|
helptextDropdown?: string | ReactNode
|
|
17
|
+
/** @deprecated Bruk `strings={{ forms: { readMore: '…' } }}` eller `setPktStrings()`. */
|
|
15
18
|
helptextDropdownButton?: string
|
|
16
19
|
counter?: boolean
|
|
17
20
|
counterCurrent?: number
|
|
18
21
|
counterMaxLength?: number
|
|
19
22
|
optionalTag?: boolean
|
|
23
|
+
/** @deprecated Bruk `strings={{ forms: { optional: '…' } }}` eller `setPktStrings()`. */
|
|
20
24
|
optionalText?: string
|
|
21
25
|
requiredTag?: boolean
|
|
26
|
+
/** @deprecated Bruk `strings={{ forms: { required: '…' } }}` eller `setPktStrings()`. */
|
|
22
27
|
requiredText?: string
|
|
23
28
|
tagText?: string | null
|
|
24
29
|
hasError?: boolean
|
|
@@ -33,6 +38,8 @@ export interface IPktInputWrapper extends RefAttributes<HTMLElement> {
|
|
|
33
38
|
role?: string
|
|
34
39
|
counterPosition?: 'top' | 'bottom'
|
|
35
40
|
size?: 'small' | 'medium' | 'xsmall'
|
|
41
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
42
|
+
strings?: TPktStringsOverride
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
export const PktInputWrapper = forwardRef(
|
|
@@ -47,9 +54,9 @@ export const PktInputWrapper = forwardRef(
|
|
|
47
54
|
counterCurrent = 0,
|
|
48
55
|
counterMaxLength,
|
|
49
56
|
optionalTag = false,
|
|
50
|
-
optionalText
|
|
57
|
+
optionalText,
|
|
51
58
|
requiredTag = false,
|
|
52
|
-
requiredText
|
|
59
|
+
requiredText,
|
|
53
60
|
tagText = null,
|
|
54
61
|
hasError = false,
|
|
55
62
|
errorMessage,
|
|
@@ -63,10 +70,12 @@ export const PktInputWrapper = forwardRef(
|
|
|
63
70
|
role,
|
|
64
71
|
counterPosition = 'bottom',
|
|
65
72
|
size = 'medium',
|
|
73
|
+
strings,
|
|
66
74
|
}: IPktInputWrapper,
|
|
67
75
|
ref: ForwardedRef<HTMLDivElement>,
|
|
68
76
|
) => {
|
|
69
77
|
const focusModality = useFocusModality<HTMLDivElement>(ref)
|
|
78
|
+
const s = usePktStrings(['forms', 'validation'], strings)
|
|
70
79
|
|
|
71
80
|
const describedBy = ariaDescribedby || (helptext ? `${forId}-helptext` : undefined)
|
|
72
81
|
const showCounter = !!counter
|
|
@@ -95,7 +104,12 @@ export const PktInputWrapper = forwardRef(
|
|
|
95
104
|
{counterMaxLength ? `/${counterMaxLength}` : ''}
|
|
96
105
|
</div>
|
|
97
106
|
<div className="pkt-sr-only" id={counterStatusId(forId)} aria-live="polite">
|
|
98
|
-
{counterExceeded
|
|
107
|
+
{counterExceeded
|
|
108
|
+
? formatString(s.validation.counterExceeded, {
|
|
109
|
+
current: counterCurrent,
|
|
110
|
+
max: counterMaxLength ?? 0,
|
|
111
|
+
})
|
|
112
|
+
: ''}
|
|
99
113
|
</div>
|
|
100
114
|
</>
|
|
101
115
|
) : null
|
|
@@ -104,8 +118,16 @@ export const PktInputWrapper = forwardRef(
|
|
|
104
118
|
return (
|
|
105
119
|
<>
|
|
106
120
|
{tagText && <span className={tagClasses + ` pkt-tag--gray`}>{tagText}</span>}
|
|
107
|
-
{optionalTag &&
|
|
108
|
-
|
|
121
|
+
{optionalTag && (
|
|
122
|
+
<span className={tagClasses + ` pkt-tag--blue-light`}>
|
|
123
|
+
{optionalText ?? s.forms.optional}
|
|
124
|
+
</span>
|
|
125
|
+
)}
|
|
126
|
+
{requiredTag && (
|
|
127
|
+
<span className={tagClasses + ` pkt-tag--beige`}>
|
|
128
|
+
{requiredText ?? s.forms.required}
|
|
129
|
+
</span>
|
|
130
|
+
)}
|
|
109
131
|
</>
|
|
110
132
|
)
|
|
111
133
|
}
|
|
@@ -148,7 +170,7 @@ export const PktInputWrapper = forwardRef(
|
|
|
148
170
|
forId={forId}
|
|
149
171
|
helptext={visibleHelptext}
|
|
150
172
|
helptextDropdown={helptextDropdown}
|
|
151
|
-
helptextDropdownButton={helptextDropdownButton}
|
|
173
|
+
helptextDropdownButton={helptextDropdownButton ?? s.forms.readMore}
|
|
152
174
|
/>
|
|
153
175
|
)
|
|
154
176
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import classNames from 'classnames'
|
|
4
4
|
import { AnchorHTMLAttributes, FC, LinkHTMLAttributes, ReactNode } from 'react'
|
|
5
|
+
import type { TRenderLink } from 'shared-types'
|
|
5
6
|
|
|
6
7
|
import { PktIcon } from '../icon/Icon'
|
|
7
8
|
|
|
@@ -12,12 +13,7 @@ interface IPktLink extends LinkHTMLAttributes<HTMLAnchorElement> {
|
|
|
12
13
|
iconPosition?: string | undefined
|
|
13
14
|
external?: boolean
|
|
14
15
|
target?: string | undefined
|
|
15
|
-
renderLink?:
|
|
16
|
-
href: string
|
|
17
|
-
className: string
|
|
18
|
-
children: ReactNode
|
|
19
|
-
props: AnchorHTMLAttributes<HTMLAnchorElement>
|
|
20
|
-
}) => ReactNode
|
|
16
|
+
renderLink?: TRenderLink<ReactNode, AnchorHTMLAttributes<HTMLAnchorElement>>
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
export const PktLink: FC<IPktLink> = ({
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
ReactElement,
|
|
7
7
|
ReactNode,
|
|
8
8
|
} from 'react'
|
|
9
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
10
|
+
import type { TPktStringsOverride } from 'shared-strings'
|
|
9
11
|
|
|
10
12
|
export interface IPktRadioButton extends InputHTMLAttributes<HTMLInputElement> {
|
|
11
13
|
id: string
|
|
@@ -19,9 +21,13 @@ export interface IPktRadioButton extends InputHTMLAttributes<HTMLInputElement> {
|
|
|
19
21
|
hasError?: boolean
|
|
20
22
|
value?: string
|
|
21
23
|
optionalTag?: boolean
|
|
24
|
+
/** @deprecated Bruk `strings={{ forms: { optional: '…' } }}` eller `setPktStrings()`. */
|
|
22
25
|
optionalText?: string
|
|
23
26
|
requiredTag?: boolean
|
|
27
|
+
/** @deprecated Bruk `strings={{ forms: { required: '…' } }}` eller `setPktStrings()`. */
|
|
24
28
|
requiredText?: string
|
|
29
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
30
|
+
strings?: TPktStringsOverride
|
|
25
31
|
tagText?: string | null
|
|
26
32
|
onChange?: ChangeEventHandler<HTMLInputElement>
|
|
27
33
|
}
|
|
@@ -43,11 +49,13 @@ export const PktRadioButton = forwardRef(
|
|
|
43
49
|
optionalText,
|
|
44
50
|
requiredTag,
|
|
45
51
|
requiredText,
|
|
52
|
+
strings,
|
|
46
53
|
tagText,
|
|
47
54
|
...props
|
|
48
55
|
}: IPktRadioButton,
|
|
49
56
|
ref: ForwardedRef<HTMLInputElement>,
|
|
50
57
|
): ReactElement => {
|
|
58
|
+
const s = usePktStrings(['forms'], strings)
|
|
51
59
|
const classes = [className, 'pkt-input-check'].filter(Boolean).join(' ')
|
|
52
60
|
|
|
53
61
|
const inputTileClasses = [
|
|
@@ -75,8 +83,16 @@ export const PktRadioButton = forwardRef(
|
|
|
75
83
|
return (
|
|
76
84
|
<>
|
|
77
85
|
{tagText && <span className={tagClasses + ` pkt-tag--gray`}>{tagText}</span>}
|
|
78
|
-
{optionalTag &&
|
|
79
|
-
|
|
86
|
+
{optionalTag && (
|
|
87
|
+
<span className={tagClasses + ` pkt-tag--blue-light`}>
|
|
88
|
+
{optionalText ?? s.forms.optional}
|
|
89
|
+
</span>
|
|
90
|
+
)}
|
|
91
|
+
{requiredTag && (
|
|
92
|
+
<span className={tagClasses + ` pkt-tag--beige`}>
|
|
93
|
+
{requiredText ?? s.forms.required}
|
|
94
|
+
</span>
|
|
95
|
+
)}
|
|
80
96
|
</>
|
|
81
97
|
)
|
|
82
98
|
}
|
|
@@ -6,6 +6,8 @@ import type { IPktSearchInputSuggestion } from 'shared-types/searchinput'
|
|
|
6
6
|
|
|
7
7
|
import { useFocusModality } from '../../hooks/useFocusModality'
|
|
8
8
|
import { PktButton } from '../button/Button'
|
|
9
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
10
|
+
import type { TPktStringsOverride } from 'shared-strings'
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* React suggestion item: portable fields from {@link IPktSearchInputSuggestion}
|
|
@@ -18,6 +20,9 @@ export type PktSearchInputSuggestion = Omit<IPktSearchInputSuggestion, 'title' |
|
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
interface ISearch extends HTMLProps<HTMLInputElement> {
|
|
23
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
24
|
+
strings?: TPktStringsOverride
|
|
25
|
+
|
|
21
26
|
appearance?: 'local' | 'local-with-button' | 'global'
|
|
22
27
|
inputSize?: 'small' | 'medium' | 'xsmall'
|
|
23
28
|
disabled?: boolean
|
|
@@ -25,6 +30,7 @@ interface ISearch extends HTMLProps<HTMLInputElement> {
|
|
|
25
30
|
id: string
|
|
26
31
|
label?: string
|
|
27
32
|
name?: string
|
|
33
|
+
/** @deprecated Bruk `strings={{ searchinput: { placeholder: '…' } }}` eller `setPktStrings()`. */
|
|
28
34
|
placeholder?: string
|
|
29
35
|
suggestions?: PktSearchInputSuggestion[]
|
|
30
36
|
value?: string | undefined
|
|
@@ -54,7 +60,7 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
|
|
|
54
60
|
id,
|
|
55
61
|
label,
|
|
56
62
|
name,
|
|
57
|
-
placeholder
|
|
63
|
+
placeholder,
|
|
58
64
|
suggestions,
|
|
59
65
|
value = '',
|
|
60
66
|
action,
|
|
@@ -62,10 +68,14 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
|
|
|
62
68
|
onChange,
|
|
63
69
|
onSearch,
|
|
64
70
|
onSuggestionClick,
|
|
71
|
+
strings,
|
|
65
72
|
...props
|
|
66
73
|
},
|
|
67
74
|
ref,
|
|
68
75
|
) => {
|
|
76
|
+
const s = usePktStrings(['searchinput'], strings)
|
|
77
|
+
const resolvedPlaceholder = placeholder ?? s.searchinput.placeholder
|
|
78
|
+
|
|
69
79
|
const handleSuggestionActivate = (cb: (() => void) | undefined, index: number) => {
|
|
70
80
|
if (cb) {
|
|
71
81
|
cb()
|
|
@@ -130,7 +140,7 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
|
|
|
130
140
|
type="search"
|
|
131
141
|
name={name || id}
|
|
132
142
|
id={id}
|
|
133
|
-
placeholder={
|
|
143
|
+
placeholder={resolvedPlaceholder}
|
|
134
144
|
defaultValue={value}
|
|
135
145
|
disabled={disabled}
|
|
136
146
|
autoComplete="off"
|
|
@@ -167,7 +177,7 @@ export const PktSearchInput = forwardRef<HTMLInputElement, ISearchInput | ISearc
|
|
|
167
177
|
})
|
|
168
178
|
}
|
|
169
179
|
>
|
|
170
|
-
{label ||
|
|
180
|
+
{label || resolvedPlaceholder}
|
|
171
181
|
</PktButton>
|
|
172
182
|
</div>
|
|
173
183
|
|
|
@@ -4,10 +4,13 @@ import { ForwardedRef, forwardRef, ReactNode, SelectHTMLAttributes } from 'react
|
|
|
4
4
|
|
|
5
5
|
import { PktInputWrapper } from '../inputwrapper/InputWrapper'
|
|
6
6
|
import { describedByIds } from 'shared-utils/forms/described-by'
|
|
7
|
+
import type { TPktStringsOverride } from 'shared-strings'
|
|
7
8
|
|
|
8
9
|
export type { IPktSelectOption, TSelectOption } from 'shared-types/select'
|
|
9
10
|
|
|
10
11
|
export interface IPktSelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
|
|
12
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
13
|
+
strings?: TPktStringsOverride
|
|
11
14
|
ariaDescribedby?: string
|
|
12
15
|
ariaLabelledby?: string
|
|
13
16
|
children?: ReactNode | ReactNode[]
|
|
@@ -60,6 +63,7 @@ export const PktSelect = forwardRef(
|
|
|
60
63
|
useWrapper = true,
|
|
61
64
|
hasFieldset,
|
|
62
65
|
inputSize = 'medium',
|
|
66
|
+
strings,
|
|
63
67
|
...props
|
|
64
68
|
}: IPktSelectProps,
|
|
65
69
|
ref: ForwardedRef<HTMLSelectElement>,
|
|
@@ -69,6 +73,7 @@ export const PktSelect = forwardRef(
|
|
|
69
73
|
return (
|
|
70
74
|
<div className={outerClasses}>
|
|
71
75
|
<PktInputWrapper
|
|
76
|
+
strings={strings}
|
|
72
77
|
forId={`${id}-input`}
|
|
73
78
|
label={label}
|
|
74
79
|
helptext={helptext}
|