@oslokommune/punkt-react 17.1.3 → 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.
- package/CHANGELOG.md +18 -0
- package/dist/components/backlink/BackLink.d.ts +3 -0
- package/dist/components/breadcrumbs/Breadcrumbs.d.ts +3 -0
- 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 +3 -0
- 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/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 +2969 -2749
- 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 +118 -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 +29 -0
- package/dist/shared-types/fileupload.d.ts +0 -27
- package/dist/shared-types/index.d.ts +1 -2
- 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/backlink/BackLink.tsx +11 -2
- package/src/components/breadcrumbs/Breadcrumbs.tsx +8 -2
- 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 +41 -8
- 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/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,6 @@ import {
|
|
|
27
27
|
useState,
|
|
28
28
|
} from 'react'
|
|
29
29
|
|
|
30
|
-
import { defaultFileUploadStrings } from 'shared-types'
|
|
31
30
|
import {
|
|
32
31
|
countFileTransferStates,
|
|
33
32
|
resolveAcceptAttribute,
|
|
@@ -54,6 +53,8 @@ import {
|
|
|
54
53
|
TQueueItemOperation,
|
|
55
54
|
TUploadStrategy,
|
|
56
55
|
} from './types'
|
|
56
|
+
import { usePktStrings } from '../../hooks/usePktStrings'
|
|
57
|
+
import { formatString, plural, type TPktStringsOverride } from 'shared-strings'
|
|
57
58
|
|
|
58
59
|
// Re-export for external use
|
|
59
60
|
export { formatFileSize, parseFileSize } from 'shared-utils/fileupload'
|
|
@@ -135,6 +136,8 @@ interface IBaseFileUploadProps extends Omit<
|
|
|
135
136
|
tagText?: string | null
|
|
136
137
|
/** Marks the upload as required. Native validation is used in `form`; submit validation in `custom`. */
|
|
137
138
|
required?: boolean
|
|
139
|
+
/** Overstyrer tekster for denne komponenten. */
|
|
140
|
+
strings?: TPktStringsOverride
|
|
138
141
|
/** Enable image preview modal (only applies to thumbnail renderer). */
|
|
139
142
|
enableImagePreview?: boolean
|
|
140
143
|
}
|
|
@@ -193,6 +196,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
193
196
|
enableImagePreview = false,
|
|
194
197
|
['aria-describedby']: ariaDescribedByProp,
|
|
195
198
|
['aria-invalid']: ariaInvalidProp,
|
|
199
|
+
strings,
|
|
196
200
|
...props
|
|
197
201
|
}: IPktFileUpload,
|
|
198
202
|
forwardedRef,
|
|
@@ -203,6 +207,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
203
207
|
useImperativeHandle(forwardedRef, () => fileInputRef.current as HTMLInputElement, [])
|
|
204
208
|
|
|
205
209
|
const transfers = 'transfers' in props ? props.transfers : undefined
|
|
210
|
+
const fileuploadStrings = usePktStrings(['fileupload'], strings).fileupload
|
|
206
211
|
const [validationError, setValidationError] = useState<string | null>(null)
|
|
207
212
|
const [addedAnnouncement, setAddedAnnouncement] = useState('')
|
|
208
213
|
|
|
@@ -262,7 +267,10 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
262
267
|
uploadStrategy,
|
|
263
268
|
required,
|
|
264
269
|
currentFileCount: value.length,
|
|
265
|
-
onMissingFiles: useCallback(
|
|
270
|
+
onMissingFiles: useCallback(
|
|
271
|
+
() => setValidationError(fileuploadStrings.requiredMissing),
|
|
272
|
+
[fileuploadStrings],
|
|
273
|
+
),
|
|
266
274
|
})
|
|
267
275
|
|
|
268
276
|
const itemRenderer = typeof itemRendererProp === 'string' ? ItemRenderers[itemRendererProp] : itemRendererProp
|
|
@@ -293,8 +301,10 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
293
301
|
const acceptedItems: TFileItemList = multiple ? addedFileItems : [addedFileItems[0]]
|
|
294
302
|
setAddedAnnouncement(
|
|
295
303
|
acceptedItems.length === 1
|
|
296
|
-
?
|
|
297
|
-
|
|
304
|
+
? formatString(fileuploadStrings.srFileAdded, {
|
|
305
|
+
filename: acceptedItems[0].attributes.targetFilename,
|
|
306
|
+
})
|
|
307
|
+
: formatString(fileuploadStrings.srFilesAdded, { count: acceptedItems.length }),
|
|
298
308
|
)
|
|
299
309
|
|
|
300
310
|
if (!isControlled) setInternalValue(newValue)
|
|
@@ -306,7 +316,16 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
306
316
|
})
|
|
307
317
|
}
|
|
308
318
|
},
|
|
309
|
-
[
|
|
319
|
+
[
|
|
320
|
+
onFilesChanged,
|
|
321
|
+
value,
|
|
322
|
+
multiple,
|
|
323
|
+
uploadStrategy,
|
|
324
|
+
onFileUploadRequested,
|
|
325
|
+
validateFile,
|
|
326
|
+
isControlled,
|
|
327
|
+
fileuploadStrings,
|
|
328
|
+
],
|
|
310
329
|
)
|
|
311
330
|
|
|
312
331
|
const onFileUpdated = useCallback(
|
|
@@ -346,7 +365,11 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
346
365
|
[value, transfers],
|
|
347
366
|
)
|
|
348
367
|
|
|
349
|
-
const totalLabel =
|
|
368
|
+
const totalLabel = plural(
|
|
369
|
+
totalCount,
|
|
370
|
+
fileuploadStrings.fileLabelOne,
|
|
371
|
+
fileuploadStrings.fileLabelOther,
|
|
372
|
+
)
|
|
350
373
|
|
|
351
374
|
const fileUploadContent = (
|
|
352
375
|
<div
|
|
@@ -362,13 +385,21 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
362
385
|
<div className="pkt-sr-only" aria-live="polite" aria-atomic="true">
|
|
363
386
|
{addedAnnouncement ||
|
|
364
387
|
(totalCount > 0 && uploadedCount > 0
|
|
365
|
-
?
|
|
388
|
+
? formatString(fileuploadStrings.srFilesUploadedOfTotal, {
|
|
389
|
+
uploaded: uploadedCount,
|
|
390
|
+
total: totalCount,
|
|
391
|
+
label: totalLabel,
|
|
392
|
+
})
|
|
366
393
|
: '')}
|
|
367
394
|
</div>
|
|
368
395
|
<div className="pkt-sr-only" aria-live="assertive" aria-atomic="true">
|
|
369
396
|
{totalCount > 0 &&
|
|
370
397
|
failedCount > 0 &&
|
|
371
|
-
|
|
398
|
+
formatString(fileuploadStrings.srFilesFailedOfTotal, {
|
|
399
|
+
failed: failedCount,
|
|
400
|
+
total: totalCount,
|
|
401
|
+
label: totalLabel,
|
|
402
|
+
})}
|
|
372
403
|
</div>
|
|
373
404
|
|
|
374
405
|
<DropZone
|
|
@@ -387,6 +418,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
387
418
|
aria-invalid={isAriaInvalid ? 'true' : undefined}
|
|
388
419
|
aria-describedby={describedBy}
|
|
389
420
|
hasVisibleLabel={!!label}
|
|
421
|
+
strings={strings}
|
|
390
422
|
/>
|
|
391
423
|
{hasError && errorMessage && (
|
|
392
424
|
<PktAlert
|
|
@@ -423,6 +455,7 @@ export const PktFileUpload: FC<IPktFileUpload> = forwardRef<HTMLInputElement, IP
|
|
|
423
455
|
return (
|
|
424
456
|
<div className="pkt-fileupload-component">
|
|
425
457
|
<PktInputWrapper
|
|
458
|
+
strings={strings}
|
|
426
459
|
forId={id}
|
|
427
460
|
label={label}
|
|
428
461
|
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 } 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
|
}
|
|
@@ -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}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, type ReactNode } from 'react'
|
|
2
|
+
import type { TPktStringsOverride } from 'shared-strings'
|
|
3
|
+
|
|
4
|
+
export const PktStringsContext = createContext<TPktStringsOverride | undefined>(undefined)
|
|
5
|
+
|
|
6
|
+
export interface IPktStringsProvider {
|
|
7
|
+
/** Tekstene som skal gjelde for dette undertreet. */
|
|
8
|
+
strings: TPktStringsOverride
|
|
9
|
+
children?: ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Overstyrer Punkt-tekster for et undertre.
|
|
14
|
+
*
|
|
15
|
+
* Providere kan nestes — en indre provider merges oppå den ytre, og en `strings`-prop
|
|
16
|
+
* på en enkelt komponent vinner uansett over begge.
|
|
17
|
+
*
|
|
18
|
+
* ```tsx
|
|
19
|
+
* <PktStringsProvider strings={{ forms: { optional: 'Kan stå tomt' } }}>
|
|
20
|
+
* <PktTextinput id="navn" name="navn" label="Navn" />
|
|
21
|
+
* </PktStringsProvider>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const PktStringsProvider = ({ strings, children }: IPktStringsProvider) => {
|
|
25
|
+
const parent = useContext(PktStringsContext)
|
|
26
|
+
|
|
27
|
+
const value = useMemo(() => {
|
|
28
|
+
if (!parent) return strings
|
|
29
|
+
|
|
30
|
+
const merged: TPktStringsOverride = { ...parent }
|
|
31
|
+
for (const key of Object.keys(strings) as (keyof TPktStringsOverride)[]) {
|
|
32
|
+
merged[key] = { ...merged[key], ...strings[key] } as TPktStringsOverride[typeof key]
|
|
33
|
+
}
|
|
34
|
+
return merged
|
|
35
|
+
}, [parent, strings])
|
|
36
|
+
|
|
37
|
+
return <PktStringsContext.Provider value={value}>{children}</PktStringsContext.Provider>
|
|
38
|
+
}
|