@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.
Files changed (95) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/components/accordion/Accordion.d.ts +2 -12
  3. package/dist/components/accordion/AccordionItem.d.ts +2 -8
  4. package/dist/components/alert/Alert.d.ts +4 -3
  5. package/dist/components/backlink/BackLink.d.ts +3 -10
  6. package/dist/components/breadcrumbs/Breadcrumbs.d.ts +7 -13
  7. package/dist/components/calendar/types.d.ts +14 -12
  8. package/dist/components/checkbox/Checkbox.d.ts +5 -0
  9. package/dist/components/combobox/types.d.ts +5 -0
  10. package/dist/components/consent/strings.d.ts +1 -85
  11. package/dist/components/datepicker/DateTags.d.ts +2 -2
  12. package/dist/components/datepicker/types.d.ts +6 -3
  13. package/dist/components/fileupload/DropZone.d.ts +3 -0
  14. package/dist/components/fileupload/FileUpload.d.ts +9 -61
  15. package/dist/components/header/types.d.ts +5 -0
  16. package/dist/components/index.d.ts +4 -0
  17. package/dist/components/inputwrapper/InputWrapper.d.ts +6 -0
  18. package/dist/components/link/Link.d.ts +2 -6
  19. package/dist/components/radio/RadioButton.d.ts +5 -0
  20. package/dist/components/searchinput/SearchInput.d.ts +4 -0
  21. package/dist/components/select/Select.d.ts +3 -0
  22. package/dist/components/strings/PktStringsProvider.d.ts +21 -0
  23. package/dist/components/strings/strings-override.test.d.ts +1 -0
  24. package/dist/components/tag/Tag.d.ts +3 -0
  25. package/dist/components/textarea/Textarea.d.ts +3 -0
  26. package/dist/components/textinput/Textinput.d.ts +3 -0
  27. package/dist/components/timepicker/types.d.ts +3 -2
  28. package/dist/components/timepicker/useTimepickerState.d.ts +9 -1
  29. package/dist/punkt-react.cjs +1 -1
  30. package/dist/punkt-react.js +2982 -2757
  31. package/dist/shared-strings/consent.d.ts +71 -0
  32. package/dist/shared-strings/format.d.ts +21 -0
  33. package/dist/shared-strings/index.d.ts +14 -0
  34. package/dist/shared-strings/nb.d.ts +121 -0
  35. package/dist/shared-strings/resolve.d.ts +12 -0
  36. package/dist/shared-strings/store.d.ts +27 -0
  37. package/dist/shared-strings/types.d.ts +40 -0
  38. package/dist/shared-types/accordion.d.ts +58 -0
  39. package/dist/shared-types/alert.d.ts +20 -0
  40. package/dist/shared-types/aria.d.ts +2 -1
  41. package/dist/shared-types/backlink.d.ts +23 -0
  42. package/dist/shared-types/breadcrumbs.d.ts +33 -0
  43. package/dist/shared-types/fileupload.d.ts +55 -28
  44. package/dist/shared-types/footer.d.ts +10 -0
  45. package/dist/shared-types/form-field.d.ts +45 -0
  46. package/dist/shared-types/header-footer.d.ts +2 -0
  47. package/dist/shared-types/index.d.ts +15 -6
  48. package/dist/shared-types/render-link.d.ts +22 -0
  49. package/dist/shared-types/skin.d.ts +2 -9
  50. package/dist/shared-utils/combobox/index.d.ts +1 -0
  51. package/dist/shared-utils/combobox/input-utils.d.ts +2 -1
  52. package/dist/shared-utils/combobox/messages.d.ts +7 -0
  53. package/dist/shared-utils/combobox/option-utils.d.ts +2 -1
  54. package/dist/shared-utils/combobox/selection-manager.d.ts +3 -2
  55. package/package.json +4 -4
  56. package/src/components/accordion/Accordion.tsx +2 -12
  57. package/src/components/accordion/AccordionItem.tsx +2 -8
  58. package/src/components/alert/Alert.tsx +12 -5
  59. package/src/components/backlink/BackLink.tsx +12 -12
  60. package/src/components/breadcrumbs/Breadcrumbs.tsx +19 -15
  61. package/src/components/calendar/Calendar.core.test.tsx +7 -5
  62. package/src/components/calendar/types.ts +14 -34
  63. package/src/components/calendar/useCalendarState.ts +16 -2
  64. package/src/components/checkbox/Checkbox.tsx +20 -4
  65. package/src/components/combobox/Combobox.tsx +1 -0
  66. package/src/components/combobox/Listbox.tsx +4 -2
  67. package/src/components/combobox/types.ts +6 -0
  68. package/src/components/combobox/useComboboxState.ts +15 -8
  69. package/src/components/consent/strings.ts +1 -125
  70. package/src/components/datepicker/DateTags.tsx +4 -3
  71. package/src/components/datepicker/Datepicker.accessibility.test.tsx +2 -2
  72. package/src/components/datepicker/Datepicker.tsx +1 -0
  73. package/src/components/datepicker/DatepickerInputs.tsx +6 -6
  74. package/src/components/datepicker/types.ts +6 -3
  75. package/src/components/datepicker/useDatepickerState.ts +4 -5
  76. package/src/components/fileupload/DropZone.tsx +20 -15
  77. package/src/components/fileupload/FileUpload.test.tsx +37 -0
  78. package/src/components/fileupload/FileUpload.tsx +63 -73
  79. package/src/components/header/HeaderGlobal.tsx +5 -2
  80. package/src/components/header/HeaderService.tsx +12 -7
  81. package/src/components/header/types.ts +6 -0
  82. package/src/components/index.ts +10 -0
  83. package/src/components/inputwrapper/InputWrapper.tsx +28 -6
  84. package/src/components/link/Link.tsx +2 -6
  85. package/src/components/radio/RadioButton.tsx +18 -2
  86. package/src/components/searchinput/SearchInput.tsx +13 -3
  87. package/src/components/select/Select.tsx +5 -0
  88. package/src/components/strings/PktStringsProvider.tsx +38 -0
  89. package/src/components/strings/strings-override.test.tsx +130 -0
  90. package/src/components/tag/Tag.tsx +13 -2
  91. package/src/components/textarea/Textarea.tsx +5 -0
  92. package/src/components/textinput/Textinput.tsx +5 -0
  93. package/src/components/timepicker/Timepicker.tsx +1 -0
  94. package/src/components/timepicker/types.ts +3 -2
  95. package/src/components/timepicker/useTimepickerState.ts +20 -22
@@ -1,17 +1,26 @@
1
1
  export type { Booleanish } from './booleanish';
2
2
  export { booleanishConverter, nullableBooleanishConverter } from './booleanish';
3
- export type { TAriaBoolean, TAriaBooleanMixed, TAriaCurrent, TAriaHasPopup, TAriaInvalid, TAriaLive, TAriaRelevant, TAriaSort, TAriaAutoComplete, TAriaOrientation, IAriaAttributes, TAlertRole, } from './aria';
3
+ export { ARIA_LIVE_VALUES, type TAriaBoolean, type TAriaBooleanMixed, type TAriaCurrent, type TAriaHasPopup, type TAriaInvalid, type TAriaLive, type TAriaRelevant, type TAriaSort, type TAriaAutoComplete, type TAriaOrientation, type IAriaAttributes, type TAlertRole, } from './aria';
4
+ export { ALERT_SKINS, ALERT_SIZES, type TAlertSkin, type TAlertSize, type IAlertSpecProps, } from './alert';
4
5
  export type { THeadingLevel, THeadingSize, THeadingWeight } from './heading';
5
6
  export type { User, Representing, UserMenuItem, InternalMenuItemBase, InternalMenuLink, InternalMenuButton, TInternalMenuItem, TSlotMenuVariant, THeaderMenu, TLogOutButtonPlacement, THeaderPosition, THeaderScrollBehavior, } from './header';
6
7
  export { convertUserMenuItem } from './header';
8
+ export { HEADER_MENU_LOCALES, DEFAULT_HEADER_FOOTER_URL, DEFAULT_HEADER_LOGO_LINK, } from './header-footer';
7
9
  export type { THeaderMenuLocale, IHeaderMenuLink, IHeaderMenuIconLink, IHeaderMenuButton, IHeaderMenuServices, IHeaderMenuSection, IMegaMenu, IHeaderFooter, IHeaderFooterLocaleData, THeaderFooterApi, IPktHeaderMenu, IPktHeaderGlobal, } from './header-footer';
8
- export { DEFAULT_HEADER_FOOTER_URL, DEFAULT_HEADER_LOGO_LINK } from './header-footer';
9
- export type { IPktFooterLink, IPktFooterColumn, IPktFooterSocialLink, IPktFooterBase, IPktFooterProps, IPktFooterSimpleProps, } from './footer';
10
+ export type { IPktFooterLink, IPktFooterColumn, IPktFooterSocialLink, IPktFooterBase, IPktFooterProps, IPktFooterSimpleProps, IFooterSimpleSpecProps, IFooterSpecProps, } from './footer';
10
11
  export { DEFAULT_FOOTER_PERSONVERN_URL, DEFAULT_FOOTER_TILGJENGELIGHET_URL } from './footer';
11
12
  export type { TLayout, TVerticalPosition, THorizontalPosition, TPosition, TPositionWithCenter, } from './layout';
12
13
  export type { THTMLButtonType, TSelectionMode } from './form';
13
14
  export type { TPktInputSize, TSize3, TSize5, TSize7 } from './size';
14
- export type { TAlertSkin, TMessageboxSkin, TAccordionSkin, TCardSkin, TTagSkin } from './skin';
15
+ export { ACCORDION_SKINS } from './accordion';
16
+ export type { TAccordionSkin, IPktAccordionBase, IPktAccordionItemBase, IAccordionSpecProps, IAccordionItemSpecProps, IAccordionVisualProps, } from './accordion';
17
+ export type { IPktBackLinkBase, IBacklinkSpecProps } from './backlink';
18
+ export { BREADCRUMBS_NAVIGATION_TYPES } from './breadcrumbs';
19
+ export type { TBreadcrumbsNavigationType, IBreadcrumb, IBreadcrumbItem, IBreadcrumbs, IPktBreadcrumbsBase, IBreadcrumbsSpecProps, } from './breadcrumbs';
20
+ export type { IRenderLink, TRenderLink } from './render-link';
21
+ export type { IPktStringsProps } from '../shared-strings';
22
+ export type { IHelptextProps, ITagOptionalRequiredProps, IFormFieldLabelProps, IFormFieldErrorProps, IFormFieldCommonProps, TFormFieldCommonSpecKeys, } from './form-field';
23
+ export type { TMessageboxSkin, TCardSkin, TTagSkin } from './skin';
15
24
  export type { IDateConstraints, ICalendarGridDimensions, IGridCellPosition, TDateRangeMap, ISelectionState, ISelectionOptions, } from './calendar';
16
25
  export type { IDatepickerStrings } from './datepicker';
17
26
  export { defaultSingleStrings, defaultRangeStrings } from './datepicker';
@@ -21,5 +30,5 @@ export type { TProgressbarRole, TProgressbarSkin, TProgressbarStatusPlacement, T
21
30
  export type { IPktComboboxOption, TPktComboboxTagSkin, TPktComboboxDisplayValue, TPktComboboxTagPlacement, } from './combobox';
22
31
  export type { IPktSelectOption, TSelectOption } from './select';
23
32
  export type { TPktSearchInputAppearance, TPktSearchInputMethod, IPktSearchInputSuggestion, IPktSearchInput, } from './searchinput';
24
- export type { TUploadStrategy as TFileUploadStrategy, TFileUploadItemRenderer, TFilesChangedReason, TFileValidator, TTransferProgress, IFileItem, IFileTransfer, IFileValidateDetail, ITransferCancelledDetail, IFilesChangedDetail, IFileUploadStrings, } from './fileupload';
25
- export { defaultFileUploadStrings } from './fileupload';
33
+ export { UPLOAD_STRATEGIES, FILE_UPLOAD_ITEM_RENDERERS, PREVIEW_UPLOAD_STATUS, } from './fileupload';
34
+ export type { TUploadStrategy as TFileUploadStrategy, TFileUploadItemRenderer, TPreviewUploadStatus, TFilesChangedReason, TFileValidator, TTransferProgress, IFileItem, IFileTransfer, IFileValidateDetail, ITransferCancelledDetail, IFilesChangedDetail, IFileUploadSpecProps, IPktFileUploadBase, } from './fileupload';
@@ -0,0 +1,22 @@
1
+ /**
2
+ * render-link.ts
3
+ *
4
+ * Shared React `renderLink` callback shape used by Link, BackLink, and Breadcrumbs.
5
+ * Elements components do not expose this prop.
6
+ */
7
+ /** Object passed to a custom link renderer (`renderLink` prop). */
8
+ export type IRenderLink<TChildren = unknown, TAnchorProps = Record<string, unknown>> = {
9
+ href: string;
10
+ className: string;
11
+ /** Link label/content — typically `ReactNode` in `@oslokommune/punkt-react`. */
12
+ children: TChildren;
13
+ /** Remaining anchor attributes spread onto the rendered link. */
14
+ props: TAnchorProps;
15
+ };
16
+ /**
17
+ * React-only callback to render a custom link component (Next.js Link, React Router, etc.).
18
+ *
19
+ * @typeParam TReturn — Return value / children type (typically `ReactNode`).
20
+ * @typeParam TAnchorProps — Anchor attribute bag passed in `args.props`.
21
+ */
22
+ export type TRenderLink<TReturn = unknown, TAnchorProps = Record<string, unknown>> = (args: IRenderLink<TReturn, TAnchorProps>) => TReturn;
@@ -5,19 +5,12 @@
5
5
  * Only includes skin types that are identical across packages.
6
6
  * Component-specific skin types should remain in their respective component files.
7
7
  */
8
- /**
9
- * Alert component skin variants
10
- * Used for semantic color coding of alerts (error, success, warning, info)
11
- */
12
- export type TAlertSkin = 'error' | 'success' | 'warning' | 'info';
8
+ export { ALERT_SKINS, type TAlertSkin } from './alert';
9
+ export { ACCORDION_SKINS, type TAccordionSkin } from './accordion';
13
10
  /**
14
11
  * Messagebox component skin variants
15
12
  */
16
13
  export type TMessageboxSkin = 'beige' | 'blue' | 'red' | 'green';
17
- /**
18
- * Accordion component skin variants
19
- */
20
- export type TAccordionSkin = 'borderless' | 'outlined' | 'beige' | 'blue' | 'plus-minus';
21
14
  /**
22
15
  * Card component skin variants
23
16
  */
@@ -8,3 +8,4 @@ export { isLetterOrSpace, createTypeaheadHandler, findTypeaheadOptionMatch } fro
8
8
  export { getInputKeyAction, isArrowToggleKey, getInputValueAction, checkForMatches, getSingleValueForInput, } from './input-utils';
9
9
  export type { TInputKeyAction, TFocusOutAction, IFocusOutResult, ICheckForMatchesResult, } from './input-utils';
10
10
  export { focusAndScrollIntoView, getOptionElements, focusNextOption, focusPreviousOption, focusFirstOption, focusLastOption, focusFirstOrSelectedOption, } from './keyboard-navigation';
11
+ export type { TComboboxMessageKey } from './messages';
@@ -1,3 +1,4 @@
1
+ import type { TComboboxMessageKey } from './messages';
1
2
  /**
2
3
  * Shared combobox input utility functions used by both Elements and React packages.
3
4
  * Covers keyboard actions, focus-out validation, match checking, and input display.
@@ -23,7 +24,7 @@ export interface IFocusOutResult {
23
24
  export declare const getInputValueAction: (inputValue: string, currentValues: string[], options: IPktComboboxOption[], allowUserInput: boolean, multiple: boolean) => IFocusOutResult;
24
25
  export interface ICheckForMatchesResult {
25
26
  addValueText: string | null;
26
- userInfoMessage: string;
27
+ userInfoMessage: TComboboxMessageKey;
27
28
  shouldRemoveValue: boolean;
28
29
  shouldResetInput: boolean;
29
30
  }
@@ -0,0 +1,7 @@
1
+ import type { TPktStrings } from '../../shared-strings';
2
+ /**
3
+ * Nøkkelen til en statusmelding i comboboxen, eller tom streng når det ikke er
4
+ * noe å melde. Utvalgslogikken her returnerer nøkler i stedet for ferdig tekst,
5
+ * slik at Elements og React kan slå opp teksten i sin egen tekstkatalog.
6
+ */
7
+ export type TComboboxMessageKey = keyof TPktStrings['combobox'] | '';
@@ -1,3 +1,4 @@
1
+ import type { TComboboxMessageKey } from './messages';
1
2
  /**
2
3
  * Shared combobox option utility functions used by both Elements and React packages.
3
4
  * Only framework-agnostic functions belong here.
@@ -46,5 +47,5 @@ export declare const findTypeaheadMatches: (options: IPktComboboxOption[], searc
46
47
  */
47
48
  export declare const getSearchInfoMessage: (searchValue: string, selectedValues: string[], options: IPktComboboxOption[], allowUserInput: boolean) => {
48
49
  addValueText: string | null;
49
- userInfoMessage: string;
50
+ userInfoMessage: TComboboxMessageKey;
50
51
  };
@@ -1,3 +1,4 @@
1
+ import type { TComboboxMessageKey } from './messages';
1
2
  /**
2
3
  * Shared combobox selection management functions used by both Elements and React packages.
3
4
  * Only framework-agnostic functions belong here.
@@ -20,7 +21,7 @@ export interface IToggleSelectionResult {
20
21
  options: IPktComboboxOption[];
21
22
  shouldOptionsBeOpen: boolean;
22
23
  shouldResetInput: boolean;
23
- userInfoMessage: string;
24
+ userInfoMessage: TComboboxMessageKey;
24
25
  searchValue: string;
25
26
  newUserOption: IPktComboboxOption | null;
26
27
  }
@@ -47,7 +48,7 @@ export declare const deselectOption: (value: string | null, currentValues: strin
47
48
  export declare const selectAllOptions: (options: IPktComboboxOption[], maxlength: number | null) => {
48
49
  values: string[];
49
50
  options: IPktComboboxOption[];
50
- userInfoMessage: string;
51
+ userInfoMessage: TComboboxMessageKey;
51
52
  };
52
53
  /**
53
54
  * Clears all selections.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-react",
3
- "version": "17.1.3",
3
+ "version": "18.1.0",
4
4
  "description": "React komponentbibliotek til Punkt, et designsystem laget av Oslo Origo",
5
5
  "homepage": "https://punkt.oslo.kommune.no",
6
6
  "author": "Team Designsystem, Oslo Origo",
@@ -44,8 +44,8 @@
44
44
  "@eslint/compat": "^2.1.0",
45
45
  "@eslint/eslintrc": "^3.3.5",
46
46
  "@eslint/js": "^10.0.1",
47
- "@oslokommune/punkt-assets": "^17.1.2",
48
- "@oslokommune/punkt-css": "^17.1.3",
47
+ "@oslokommune/punkt-assets": "^18.0.0",
48
+ "@oslokommune/punkt-css": "^18.0.0",
49
49
  "@testing-library/jest-dom": "^6.9.1",
50
50
  "@testing-library/react": "^16.3.2",
51
51
  "@testing-library/user-event": "^14.6.1",
@@ -102,5 +102,5 @@
102
102
  "url": "https://github.com/oslokommune/punkt/issues"
103
103
  },
104
104
  "license": "MIT",
105
- "gitHead": "f378c0e71d923d9526bc9eea3a399e0d9e00b206"
105
+ "gitHead": "087459aca13f8ff3a34afc42f8e1cd73900d4896"
106
106
  }
@@ -1,22 +1,12 @@
1
1
  'use client'
2
2
 
3
3
  import { createContext, forwardRef, ReactNode, useContext } from 'react'
4
- import type { TAccordionSkin } from 'shared-types'
4
+ import type { IPktAccordionBase, TAccordionSkin } from 'shared-types'
5
5
 
6
6
  export type TPktAccordionSkin = TAccordionSkin
7
7
 
8
- export interface IPktAccordion {
9
- compact?: boolean
10
- /**
11
- * @default skin: "borderless"
12
- */
13
- skin?: TPktAccordionSkin
14
- /**
15
- * @description A unique identifier to connect the accordion with a heading
16
- */
17
- ariaLabelledBy?: string
8
+ export interface IPktAccordion extends IPktAccordionBase {
18
9
  children?: ReactNode
19
- name?: string
20
10
  className?: string
21
11
  }
22
12
 
@@ -1,22 +1,16 @@
1
1
  'use client'
2
2
 
3
3
  import { forwardRef, type MouseEvent, type ReactNode, type SyntheticEvent, useState } from 'react'
4
- import type { TAccordionSkin } from 'shared-types'
4
+ import type { IPktAccordionItemBase, TAccordionSkin } from 'shared-types'
5
5
 
6
6
  import { PktIcon } from '../icon/Icon'
7
7
  import { useAccordionContext } from './Accordion'
8
8
 
9
9
  export type TPktAccordionSkin = TAccordionSkin
10
10
 
11
- export interface IPktAccordionItem {
12
- defaultOpen?: boolean
13
- id: string
11
+ export interface IPktAccordionItem extends Omit<IPktAccordionItemBase, 'title'> {
14
12
  title: string | ReactNode
15
- skin?: TPktAccordionSkin
16
- compact?: boolean
17
- isOpen?: boolean
18
13
  children?: ReactNode
19
- name?: string
20
14
  className?: string
21
15
  onClick?: (e: MouseEvent<HTMLDetailsElement>) => void
22
16
  onToggle?: (e: SyntheticEvent<HTMLDetailsElement>) => void
@@ -2,12 +2,15 @@
2
2
 
3
3
  import classNames from 'classnames'
4
4
  import { FC, HTMLAttributes, ReactElement, ReactNode, useCallback, useState } from 'react'
5
- import type { TAlertRole, TAlertSkin, TAriaLive } from 'shared-types'
5
+ import type { TAlertRole, TAlertSkin, TAlertSize, TAriaLive } from 'shared-types'
6
+ import { formatString, type IPktStringsProps } from 'shared-strings'
6
7
 
7
8
  import { PktButton } from '../button/Button'
8
9
  import { PktIcon } from '../icon/Icon'
10
+ import { usePktStrings } from '../../hooks/usePktStrings'
9
11
 
10
- export interface IPktAlert extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
12
+ export interface IPktAlert
13
+ extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>, IPktStringsProps {
11
14
  skin?: TAlertSkin
12
15
  closeAlert?: boolean
13
16
  title?: ReactNode
@@ -23,7 +26,7 @@ export interface IPktAlert extends Omit<HTMLAttributes<HTMLDivElement>, 'title'>
23
26
  *
24
27
  * @default medium
25
28
  */
26
- size?: 'medium' | 'small' | 'xsmall'
29
+ size?: TAlertSize
27
30
  onClose?: (e: CustomEvent) => void
28
31
  }
29
32
 
@@ -41,9 +44,11 @@ export const PktAlert: FC<IPktAlert> = ({
41
44
  role = 'status',
42
45
  skin = 'info' as TAlertSkin,
43
46
  className,
47
+ strings,
44
48
  ...props
45
49
  }: IPktAlert): ReactElement => {
46
50
  const [isClosed, setIsClosed] = useState(false)
51
+ const s = usePktStrings(['generic', 'alert'], strings)
47
52
 
48
53
  const classes = {
49
54
  'pkt-alert': true,
@@ -87,7 +92,7 @@ export const PktAlert: FC<IPktAlert> = ({
87
92
  variant="icon-only"
88
93
  onClick={onClose}
89
94
  >
90
- <span className="sr-only">Lukk</span>
95
+ <span className="sr-only">{s.generic.close}</span>
91
96
  </PktButton>
92
97
  </div>
93
98
  )}
@@ -95,7 +100,9 @@ export const PktAlert: FC<IPktAlert> = ({
95
100
 
96
101
  <div className="pkt-alert__text">{children}</div>
97
102
 
98
- {date && <div className="pkt-alert__date">Sist oppdatert: {date}</div>}
103
+ {date && (
104
+ <div className="pkt-alert__date">{formatString(s.alert.lastUpdated, { date })}</div>
105
+ )}
99
106
  </div>
100
107
  </div>
101
108
  )
@@ -1,24 +1,23 @@
1
1
  'use client'
2
2
 
3
3
  import { AnchorHTMLAttributes, forwardRef, ReactNode, Ref } from 'react'
4
+ import type { IPktBackLinkBase, TRenderLink } from 'shared-types'
4
5
 
5
6
  import { PktIcon } from '../icon/Icon'
7
+ import { usePktStrings } from '../../hooks/usePktStrings'
6
8
 
7
- export interface IPktBackLink extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
8
- href?: string
9
- text?: string
10
- ariaLabel?: string
11
- renderLink?: (args: {
12
- href: string
13
- className: string
14
- children: ReactNode
15
- props: AnchorHTMLAttributes<HTMLAnchorElement>
16
- }) => ReactNode
9
+ export interface IPktBackLink
10
+ extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'>,
11
+ IPktBackLinkBase {
12
+ renderLink?: TRenderLink<ReactNode, AnchorHTMLAttributes<HTMLAnchorElement>>
17
13
  ref?: Ref<HTMLElement>
18
14
  }
19
15
 
20
16
  export const PktBackLink = forwardRef<HTMLElement, IPktBackLink>(
21
- ({ href = '/', text = 'Forsiden', ariaLabel, renderLink, className, ...props }, ref) => {
17
+ (
18
+ { href = '/', text = 'Forsiden', ariaLabel, renderLink, className, strings, ...props },
19
+ ref,
20
+ ) => {
22
21
  const linkChildren = (
23
22
  <>
24
23
  <PktIcon className="pkt-back-link__icon pkt-icon pkt-link__icon" name="chevron-thin-left" aria-hidden="true" />
@@ -34,10 +33,11 @@ export const PktBackLink = forwardRef<HTMLElement, IPktBackLink>(
34
33
  </a>
35
34
  ))
36
35
 
36
+ const s = usePktStrings(['backlink'], strings)
37
37
  const classes = ['pkt-back-link', className].filter(Boolean).join(' ')
38
38
 
39
39
  return (
40
- <nav className={classes} aria-label={ariaLabel || 'Gå tilbake til forrige side'} ref={ref}>
40
+ <nav className={classes} aria-label={ariaLabel ?? s.backlink.label} ref={ref}>
41
41
  {linkRenderer({
42
42
  href,
43
43
  className: 'pkt-link pkt-link--icon-left',
@@ -2,28 +2,31 @@
2
2
 
3
3
  import { ForwardedRef, forwardRef, type AnchorHTMLAttributes, type ReactNode } from 'react'
4
4
  import { Link } from 'react-router-dom'
5
+ import type {
6
+ IBreadcrumb,
7
+ IPktBreadcrumbsBase,
8
+ TBreadcrumbsNavigationType,
9
+ TRenderLink,
10
+ } from 'shared-types'
5
11
 
6
12
  import { PktIcon } from '../icon/Icon'
13
+ import { usePktStrings } from '../../hooks/usePktStrings'
7
14
 
8
- export interface IBreadcrumbs {
9
- text: string
10
- href: string
11
- }
15
+ export type { IBreadcrumb }
16
+
17
+ /** @deprecated Use `IBreadcrumb`. */
18
+ export type IBreadcrumbs = IBreadcrumb
12
19
 
13
- export interface IPktBreadcrumbs extends AnchorHTMLAttributes<HTMLAnchorElement> {
14
- breadcrumbs: IBreadcrumbs[]
15
- navigationType?: 'router' | 'anchor'
16
- renderLink?: (args: {
17
- href: string
18
- className: string
19
- children: ReactNode
20
- props: AnchorHTMLAttributes<HTMLAnchorElement>
21
- }) => ReactNode
20
+ export interface IPktBreadcrumbs
21
+ extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'children'>,
22
+ IPktBreadcrumbsBase {
23
+ navigationType?: TBreadcrumbsNavigationType
24
+ renderLink?: TRenderLink<ReactNode, AnchorHTMLAttributes<HTMLAnchorElement>>
22
25
  }
23
26
 
24
27
  export const PktBreadcrumbs = forwardRef(
25
28
  (
26
- { breadcrumbs, navigationType, renderLink, className, ...props }: IPktBreadcrumbs,
29
+ { breadcrumbs, navigationType, renderLink, className, strings, ...props }: IPktBreadcrumbs,
27
30
  ref: ForwardedRef<HTMLAnchorElement>,
28
31
  ) => {
29
32
  const slicedBreadcrumbs = breadcrumbs
@@ -31,6 +34,7 @@ export const PktBreadcrumbs = forwardRef(
31
34
  const backLink = slicedBreadcrumbs[slicedBreadcrumbs.length - 2]
32
35
 
33
36
  // Define the classes
37
+ const s = usePktStrings(['breadcrumbs'], strings)
34
38
  const classes = [className, 'pkt-breadcrumbs'].filter(Boolean).join(' ')
35
39
 
36
40
  // Create a default renderLink implementation based on navigationType
@@ -50,7 +54,7 @@ export const PktBreadcrumbs = forwardRef(
50
54
 
51
55
  // Mobile and desktop have different markup
52
56
  return (
53
- <nav ref={ref} aria-label="brødsmulemeny" className={classes}>
57
+ <nav ref={ref} aria-label={s.breadcrumbs.ariaLabel} className={classes}>
54
58
  <ol className="pkt-breadcrumbs__list pkt-breadcrumbs--desktop">
55
59
  {slicedBreadcrumbs.map((item, index) => (
56
60
  <li key={`breadcrumb-${index}`} className="pkt-breadcrumbs__item">
@@ -223,7 +223,7 @@ describe('PktCalendar', () => {
223
223
 
224
224
  test('handles custom day strings', () => {
225
225
  const { container } = createCalendar({
226
- strings: { daysShort: ['S', 'M', 'T', 'W', 'T', 'F', 'S'] },
226
+ strings: { dates: { daysShort: ['S', 'M', 'T', 'W', 'T', 'F', 'S'] } },
227
227
  })
228
228
 
229
229
  const dayHeaders = container.querySelectorAll('.pkt-calendar__day-name')
@@ -235,10 +235,12 @@ describe('PktCalendar', () => {
235
235
  const { container } = createCalendar({
236
236
  withcontrols: true,
237
237
  strings: {
238
- months: [
239
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
240
- 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
241
- ],
238
+ dates: {
239
+ months: [
240
+ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
241
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
242
+ ],
243
+ },
242
244
  },
243
245
  })
244
246
 
@@ -1,38 +1,17 @@
1
1
  import type { IDateConstraints, TDateRangeMap } from 'shared-types/calendar'
2
2
 
3
- export const DEFAULT_STRINGS: Required<IPktCalendarStrings> = {
4
- month: 'Måned',
5
- year: 'År',
6
- days: ['Mandag', 'Tirsdag', 'Onsdag', 'Torsdag', 'Fredag', 'Lørdag', 'Søndag'],
7
- daysShort: ['Man', 'Tir', 'Ons', 'Tor', 'Fre', 'Lør', 'Søn'],
8
- months: [
9
- 'Januar',
10
- 'Februar',
11
- 'Mars',
12
- 'April',
13
- 'Mai',
14
- 'Juni',
15
- 'Juli',
16
- 'August',
17
- 'September',
18
- 'Oktober',
19
- 'November',
20
- 'Desember',
21
- ],
22
- week: 'Uke',
23
- prevMonth: 'Forrige måned',
24
- nextMonth: 'Neste måned',
25
- }
3
+ import type { TPktStringsOverride } from 'shared-strings'
26
4
 
5
+ /** Datonavn og navigasjonstekster slik kalender-underkomponentene forventer dem. */
27
6
  export interface IPktCalendarStrings {
28
- month?: string
29
- year?: string
30
- days?: string[]
31
- daysShort?: string[]
32
- months?: string[]
33
- week?: string
34
- prevMonth?: string
35
- nextMonth?: string
7
+ month: string
8
+ year: string
9
+ days: string[]
10
+ daysShort: string[]
11
+ months: string[]
12
+ week: string
13
+ prevMonth: string
14
+ nextMonth: string
36
15
  }
37
16
 
38
17
  export interface PktCalendarHandle {
@@ -64,7 +43,8 @@ export interface IPktCalendar {
64
43
  today?: string
65
44
 
66
45
  // Localization
67
- strings?: IPktCalendarStrings
46
+ /** Overstyrer tekster for denne komponenten. */
47
+ strings?: TPktStringsOverride
68
48
 
69
49
  // Callbacks
70
50
  onDateSelected?: (selected: string[]) => void
@@ -87,7 +67,7 @@ export type TDayViewData = {
87
67
 
88
68
  export interface ICalendarState {
89
69
  componentId: string
90
- strings: Required<IPktCalendarStrings>
70
+ strings: IPktCalendarStrings
91
71
 
92
72
  // Navigation
93
73
  year: number
@@ -148,7 +128,7 @@ export interface ICalendarState {
148
128
 
149
129
  export interface ICalendarNavProps {
150
130
  componentId: string
151
- strings: Required<IPktCalendarStrings>
131
+ strings: IPktCalendarStrings
152
132
  year: number
153
133
  month: number
154
134
  earliest: string | null
@@ -17,7 +17,8 @@ import {
17
17
  getKeyDirection,
18
18
  } from 'shared-utils/calendar/keyboard-navigation'
19
19
 
20
- import { DEFAULT_STRINGS, type IPktCalendar, type ICalendarState } from './types'
20
+ import { type IPktCalendar, type ICalendarState } from './types'
21
+ import { usePktStrings } from '../../hooks/usePktStrings'
21
22
 
22
23
  export function useCalendarState(props: IPktCalendar): ICalendarState {
23
24
  const {
@@ -43,7 +44,20 @@ export function useCalendarState(props: IPktCalendar): ICalendarState {
43
44
  const generatedId = useId()
44
45
  const componentId = idProp ?? generatedId
45
46
 
46
- const strings = useMemo(() => ({ ...DEFAULT_STRINGS, ...stringsProp }), [stringsProp])
47
+ const catalog = usePktStrings(['dates', 'calendar'], stringsProp)
48
+ const strings = useMemo(
49
+ () => ({
50
+ month: catalog.dates.month,
51
+ year: catalog.dates.year,
52
+ week: catalog.dates.week,
53
+ days: catalog.dates.days,
54
+ daysShort: catalog.dates.daysShort,
55
+ months: catalog.dates.months,
56
+ prevMonth: catalog.calendar.prevMonth,
57
+ nextMonth: catalog.calendar.nextMonth,
58
+ }),
59
+ [catalog],
60
+ )
47
61
 
48
62
  const todayDate = useMemo(
49
63
  () => (todayProp ? parseISODateString(todayProp) : todayInTz()),
@@ -8,6 +8,8 @@ import {
8
8
  useEffect,
9
9
  useRef,
10
10
  } from 'react'
11
+ import { usePktStrings } from '../../hooks/usePktStrings'
12
+ import type { TPktStringsOverride } from 'shared-strings'
11
13
 
12
14
  export interface IPktCheckbox extends InputHTMLAttributes<HTMLInputElement> {
13
15
  id: string
@@ -24,9 +26,13 @@ export interface IPktCheckbox extends InputHTMLAttributes<HTMLInputElement> {
24
26
  hideLabel?: boolean
25
27
  labelPosition?: 'right' | 'left'
26
28
  optionalTag?: boolean
29
+ /** @deprecated Bruk `strings={{ forms: { optional: '…' } }}` eller `setPktStrings()`. */
27
30
  optionalText?: string
28
31
  requiredTag?: boolean
32
+ /** @deprecated Bruk `strings={{ forms: { required: '…' } }}` eller `setPktStrings()`. */
29
33
  requiredText?: string
34
+ /** Overstyrer tekster for denne komponenten. */
35
+ strings?: TPktStringsOverride
30
36
  tagText?: string | null
31
37
  onChange?: ChangeEventHandler<HTMLInputElement>
32
38
  }
@@ -48,14 +54,16 @@ export const PktCheckbox = forwardRef(
48
54
  checked,
49
55
  indeterminate,
50
56
  optionalTag,
51
- optionalText = 'Valgfritt',
57
+ optionalText,
52
58
  requiredTag,
53
- requiredText = 'Må fylles ut',
59
+ requiredText,
60
+ strings,
54
61
  tagText,
55
62
  ...props
56
63
  }: IPktCheckbox,
57
64
  ref: ForwardedRef<HTMLInputElement>,
58
65
  ): ReactElement => {
66
+ const s = usePktStrings(['forms'], strings)
59
67
  const internalRef = useRef<HTMLInputElement>(null)
60
68
 
61
69
  useEffect(() => {
@@ -97,8 +105,16 @@ export const PktCheckbox = forwardRef(
97
105
  return (
98
106
  <>
99
107
  {tagText && <span className={tagClasses + ` pkt-tag--gray`}>{tagText}</span>}
100
- {optionalTag && <span className={tagClasses + ` pkt-tag--blue-light`}>{optionalText}</span>}
101
- {requiredTag && <span className={tagClasses + ` pkt-tag--beige`}>{requiredText}</span>}
108
+ {optionalTag && (
109
+ <span className={tagClasses + ` pkt-tag--blue-light`}>
110
+ {optionalText ?? s.forms.optional}
111
+ </span>
112
+ )}
113
+ {requiredTag && (
114
+ <span className={tagClasses + ` pkt-tag--beige`}>
115
+ {requiredText ?? s.forms.required}
116
+ </span>
117
+ )}
102
118
  </>
103
119
  )
104
120
  }
@@ -27,6 +27,7 @@ export const PktCombobox = forwardRef<HTMLDivElement, IPktCombobox>((props, ref)
27
27
  return (
28
28
  <div className={outerClasses} ref={state.wrapperRef} id={state.id || undefined}>
29
29
  <PktInputWrapper
30
+ strings={state.strings}
30
31
  forId={wrapperForId}
31
32
  hasFieldset={props.hasFieldset ?? !hasTextInput}
32
33
  label={state.label || ''}
@@ -2,12 +2,14 @@
2
2
 
3
3
  import type { IComboboxState } from './types'
4
4
  import { PktIcon } from '../icon/Icon'
5
+ import { usePktStrings } from '../../hooks/usePktStrings'
5
6
 
6
7
  interface IListboxProps {
7
8
  state: IComboboxState
8
9
  }
9
10
 
10
11
  export const Listbox = ({ state }: IListboxProps) => {
12
+ const s = usePktStrings(['listbox'], state.strings)
11
13
  const {
12
14
  listboxId,
13
15
  label,
@@ -59,9 +61,9 @@ export const Listbox = ({ state }: IListboxProps) => {
59
61
  <input
60
62
  className="pkt-txt-16-light"
61
63
  type="text"
62
- aria-label="Søk i listen"
64
+ aria-label={s.listbox.searchLabel}
63
65
  form=""
64
- placeholder={searchPlaceholder || 'Søk...'}
66
+ placeholder={searchPlaceholder ?? s.listbox.searchPlaceholder}
65
67
  onChange={handleSearchInput}
66
68
  onKeyDown={handleSearchKeydown}
67
69
  value={searchValue}