@oslokommune/punkt-react 18.2.0 → 18.3.1

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.
@@ -62,7 +62,7 @@ var M = /* @__PURE__ */ O((/* @__PURE__ */ E(((e, t) => {
62
62
  return n;
63
63
  }) : window.classNames = n;
64
64
  })();
65
- })))(), 1), N = "https://punkt-cdn.oslo.kommune.no/18.2/icons/";
65
+ })))(), 1), N = "https://punkt-cdn.oslo.kommune.no/18.3/icons/";
66
66
  typeof window < "u" && (window.pktFetch = window.pktFetch === void 0 ? fetch : window.pktFetch, window.pktIconPath = window.pktIconPath || N);
67
67
  var P = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 32 32\"></svg>", ee = {}, te = 2, ne = 1500, F = typeof Storage < "u" && typeof sessionStorage < "u", re = typeof window < "u" ? d : c, ie = (e, t) => !e || !F ? "" : sessionStorage.getItem(t + e + ".svg") ?? "", ae = (e) => window.pktFetch(e).then((t) => {
68
68
  if (!t.ok) throw Error("Missing icon: " + e);
@@ -278,6 +278,7 @@ var ce = {
278
278
  },
279
279
  backlink: { label: "Gå tilbake til forrige side" },
280
280
  breadcrumbs: { ariaLabel: "brødsmulemeny" },
281
+ linkcard: { openInNewTab: "Åpnes i ny fane" },
281
282
  alert: { lastUpdated: "Sist oppdatert: {date}" },
282
283
  tag: { remove: "Klikk for å fjerne {label}" },
283
284
  header: {
@@ -526,7 +527,7 @@ var ge = {
526
527
  };
527
528
  //#endregion
528
529
  //#region src/components/button/Button.tsx
529
- typeof window < "u" && (window.pktAnimationPath = window.pktAnimationPath || "https://punkt-cdn.oslo.kommune.no/18.2/animations/");
530
+ typeof window < "u" && (window.pktAnimationPath = window.pktAnimationPath || "https://punkt-cdn.oslo.kommune.no/18.3/animations/");
530
531
  var Y = i(({ children: e, className: t, iconName: n = "user", secondIconName: r = "user", iconPath: i, secondIconPath: a, size: o = "medium", fullWidth: s = !1, fullWidthOnMobile: c = !1, skin: l = "primary", type: u = "button", variant: d = "label-only", state: f, color: p, isLoading: m = void 0, disabled: h = void 0, loadingAnimationPath: g = window.pktAnimationPath, ...y }, b) => {
531
532
  let x = [
532
533
  t,
@@ -7074,7 +7075,7 @@ var co = i(({ appearance: e = "local", inputSize: t = "medium", disabled: n = !1
7074
7075
  co.displayName = "PktSearchInput";
7075
7076
  //#endregion
7076
7077
  //#region src/components/header/HeaderGlobal.tsx
7077
- var lo = "https://punkt-cdn.oslo.kommune.no/18.2/logos/";
7078
+ var lo = "https://punkt-cdn.oslo.kommune.no/18.3/logos/";
7078
7079
  typeof window < "u" && (window.pktLogoPath = window.pktLogoPath || lo);
7079
7080
  var uo = "pkt-header-global-menu", fo = "pkt-header-global-menu-toggle", po = "pkt-header-global-search-panel", mo = "pkt-header-global-search-toggle", ho = i(({ className: e, "data-mode": t, dataUrl: n, data: r, locale: i = "nb-NO", logoLink: a = ga, logoClick: s, logoPath: l = typeof window < "u" ? window.pktLogoPath : lo, showSearch: u = !0, searchPlaceholder: h, onSearch: y, showContact: b = !0, user: x, userMenu: S, representing: C, canChangeRepresentation: w = !1, changeRepresentation: T, logOutButtonPlacement: E = "none", logOut: D, position: O = "fixed", scrollBehavior: k = "hide", mobileBreakpoint: A = 768, tabletBreakpoint: j = 1024, onDataLoaded: N, onDataError: P, strings: ee }, te) => {
7080
7081
  let ne = k === "hide", [F, re] = m("none"), [ie, ae] = m(void 0), [oe, se] = m(!1), I = p(N), R = p(P);
@@ -7431,7 +7432,7 @@ var go = i(({ id: e, ariaDescribedby: t, ariaLabelledby: n, autocomplete: r = "o
7431
7432
  go.displayName = "PktTextinput";
7432
7433
  //#endregion
7433
7434
  //#region src/components/header/HeaderService.tsx
7434
- var _o = "https://punkt-cdn.oslo.kommune.no/18.2/logos/";
7435
+ var _o = "https://punkt-cdn.oslo.kommune.no/18.3/logos/";
7435
7436
  typeof window < "u" && (window.pktLogoPath = window.pktLogoPath || _o);
7436
7437
  var vo = i(({ mobileBreakpoint: e = 768, tabletBreakpoint: t = 1280, children: n, className: r, compact: i = !1, "data-mode": a, logOutButtonPlacement: s = "none", logOut: l, openedMenu: u, showSearch: d = !1, onSearch: h, onSearchChange: y, searchValue: b, searchPlaceholder: x, representing: S, serviceLink: C, serviceClick: w, serviceName: T, user: E, userMenu: D, userMenuFooter: O, userOptions: k, canChangeRepresentation: A = !1, changeRepresentation: j, hideLogo: N = !1, logoLink: P = ga, logoClick: ee, logoPath: te = typeof window < "u" ? window.pktLogoPath : _o, position: ne = "fixed", scrollBehavior: F = "hide", slotMenuVariant: re = "icon-only", slotMenuText: ie = "Meny", strings: ae }, oe) => {
7437
7438
  let se = F === "hide";
@@ -7752,12 +7753,12 @@ var Co = i(({ label: e, id: t, children: n, ...r }, i) => /* @__PURE__ */ v("div
7752
7753
  Co.displayName = "PktInput";
7753
7754
  //#endregion
7754
7755
  //#region src/components/linkcard/LinkCard.tsx
7755
- var wo = ({ children: e, skin: t, title: n, href: r, iconName: i, openInNewTab: a, external: o, className: s, ...c }) => {
7756
- let l = ["pkt-linkcard", t && `pkt-linkcard--${t}`].filter(Boolean).join(" "), u = ["pkt-linkcard__title", o && "pkt-link pkt-link--external"].filter(Boolean).join(" ");
7756
+ var wo = ({ children: e, skin: t, title: n, href: r, iconName: i, openInNewTab: a, external: o, className: s, strings: c, ...l }) => {
7757
+ let u = _e(["linkcard"], c), d = ["pkt-linkcard", t && `pkt-linkcard--${t}`].filter(Boolean).join(" "), f = ["pkt-linkcard__title", o && "pkt-link pkt-link--external"].filter(Boolean).join(" ");
7757
7758
  return /* @__PURE__ */ v("a", {
7758
- ...c,
7759
+ ...l,
7759
7760
  href: r,
7760
- className: (0, M.default)(l, s),
7761
+ className: (0, M.default)(d, s),
7761
7762
  target: a ? "_blank" : "_self",
7762
7763
  rel: a ? "noopener noreferrer" : void 0,
7763
7764
  children: [
@@ -7766,9 +7767,13 @@ var wo = ({ children: e, skin: t, title: n, href: r, iconName: i, openInNewTab:
7766
7767
  name: i
7767
7768
  }),
7768
7769
  n && /* @__PURE__ */ _("div", {
7769
- className: (0, M.default)(u),
7770
+ className: (0, M.default)(f),
7770
7771
  children: n
7771
7772
  }),
7773
+ a && u.linkcard.openInNewTab ? /* @__PURE__ */ _("span", {
7774
+ className: "pkt-sr-only",
7775
+ children: u.linkcard.openInNewTab
7776
+ }) : null,
7772
7777
  /* @__PURE__ */ _("div", {
7773
7778
  className: "pkt-linkcard__text",
7774
7779
  children: e
@@ -7778,7 +7783,7 @@ var wo = ({ children: e, skin: t, title: n, href: r, iconName: i, openInNewTab:
7778
7783
  };
7779
7784
  //#endregion
7780
7785
  //#region src/components/loader/Loader.tsx
7781
- wo.displayName = "PktLinkCard", typeof window < "u" && (window.pktAnimationPath = window.pktAnimationPath || "https://punkt-cdn.oslo.kommune.no/18.2/animations/");
7786
+ wo.displayName = "PktLinkCard", typeof window < "u" && (window.pktAnimationPath = window.pktAnimationPath || "https://punkt-cdn.oslo.kommune.no/18.3/animations/");
7782
7787
  var To = (e) => {
7783
7788
  switch (e) {
7784
7789
  case "blue": return "spinner-blue";
@@ -7972,7 +7977,7 @@ var Ao = /* @__PURE__ */ _("svg", {
7972
7977
  cx: "12",
7973
7978
  cy: "12",
7974
7979
  r: "7",
7975
- style: { fill: "var(--pkt-color-grays-grey-200, #CCC)" }
7980
+ style: { fill: "var(--pkt-color-brand-neutrals-300, #CCC)" }
7976
7981
  })
7977
7982
  }), jo = /* @__PURE__ */ v("svg", {
7978
7983
  xmlns: "http://www.w3.org/2000/svg",
@@ -66,6 +66,9 @@ export declare const nb: {
66
66
  breadcrumbs: {
67
67
  ariaLabel: string;
68
68
  };
69
+ linkcard: {
70
+ openInNewTab: string;
71
+ };
69
72
  alert: {
70
73
  lastUpdated: string;
71
74
  };
@@ -15,6 +15,8 @@ export type { TPktInputSize, TSize3, TSize5, TSize7 } from './size';
15
15
  export { ACCORDION_SKINS } from './accordion';
16
16
  export type { TAccordionSkin, IPktAccordionBase, IPktAccordionItemBase, IAccordionSpecProps, IAccordionItemSpecProps, IAccordionVisualProps, } from './accordion';
17
17
  export type { IPktBackLinkBase, IBacklinkSpecProps } from './backlink';
18
+ export { LINKCARD_SKINS } from './linkcard';
19
+ export type { TLinkCardSkin, IPktLinkCardBase, ILinkcardSpecProps } from './linkcard';
18
20
  export { BREADCRUMBS_NAVIGATION_TYPES } from './breadcrumbs';
19
21
  export type { TBreadcrumbsNavigationType, IBreadcrumb, IBreadcrumbItem, IBreadcrumbs, IPktBreadcrumbsBase, IBreadcrumbsSpecProps, } from './breadcrumbs';
20
22
  export type { IRenderLink, TRenderLink } from './render-link';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * linkcard.ts
3
+ *
4
+ * Type definitions for pkt-linkcard.
5
+ * Used by React, Elements, and typed component specs.
6
+ */
7
+ import type { IPktStringsProps } from '../shared-strings';
8
+ /** LinkCard skin variants — shared runtime source for types, specs, and Preview. */
9
+ export declare const LINKCARD_SKINS: readonly ['normal', 'no-padding', 'blue', 'beige', 'green', 'gray', 'beige-outline', 'gray-outline'];
10
+ export type TLinkCardSkin = (typeof LINKCARD_SKINS)[number];
11
+ /** Shared LinkCard props for React, Elements, and typed specs. */
12
+ export type IPktLinkCardBase = IPktStringsProps<'linkcard'> & {
13
+ title?: string;
14
+ href?: string;
15
+ skin?: TLinkCardSkin;
16
+ iconName?: string;
17
+ openInNewTab?: boolean;
18
+ external?: boolean;
19
+ };
20
+ /**
21
+ * Documented LinkCard props (subset of `IPktLinkCardBase`).
22
+ * Used for typed component specs in documentation.
23
+ */
24
+ export type ILinkcardSpecProps = Pick<IPktLinkCardBase, 'title' | 'href' | 'skin' | 'iconName' | 'openInNewTab' | 'external'>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-react",
3
- "version": "18.2.0",
3
+ "version": "18.3.1",
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",
@@ -45,7 +45,7 @@
45
45
  "@eslint/eslintrc": "^3.3.5",
46
46
  "@eslint/js": "^10.0.1",
47
47
  "@oslokommune/punkt-assets": "^18.0.0",
48
- "@oslokommune/punkt-css": "^18.0.0",
48
+ "@oslokommune/punkt-css": "^18.3.1",
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": "3e850f524f546977081c875a222256fb2e63eb5a"
105
+ "gitHead": "076395b7934546d637a3136ac35eef4583d8f8a7"
106
106
  }
@@ -3,6 +3,7 @@ import '@testing-library/jest-dom'
3
3
 
4
4
  import { render } from '@testing-library/react'
5
5
  import { axe, toHaveNoViolations } from 'jest-axe'
6
+ import { nb } from 'shared-strings'
6
7
 
7
8
  import { PktLinkCard } from './LinkCard'
8
9
 
@@ -103,6 +104,48 @@ describe('PktLinkCard', () => {
103
104
  })
104
105
  })
105
106
 
107
+ describe('openInNewTab strings', () => {
108
+ it('does not announce a new-tab string when openInNewTab is unset', () => {
109
+ const { container, queryByText } = render(<PktLinkCard title="Normal">Content</PktLinkCard>)
110
+ expect(container.querySelector('.pkt-sr-only')).not.toBeInTheDocument()
111
+ expect(queryByText(nb.linkcard.openInNewTab)).not.toBeInTheDocument()
112
+ })
113
+
114
+ it('uses the catalogue default when strings is omitted', () => {
115
+ const { getByText } = render(
116
+ <PktLinkCard title="Normal" openInNewTab>
117
+ Content
118
+ </PktLinkCard>,
119
+ )
120
+ const srOnly = getByText(nb.linkcard.openInNewTab)
121
+ expect(srOnly).toHaveClass('pkt-sr-only')
122
+ })
123
+
124
+ it('uses a custom override from the strings prop', () => {
125
+ const { getByText, queryByText } = render(
126
+ <PktLinkCard
127
+ title="Normal"
128
+ openInNewTab
129
+ strings={{ linkcard: { openInNewTab: 'Opens in a new tab' } }}
130
+ >
131
+ Content
132
+ </PktLinkCard>,
133
+ )
134
+ expect(getByText('Opens in a new tab')).toHaveClass('pkt-sr-only')
135
+ expect(queryByText(nb.linkcard.openInNewTab)).not.toBeInTheDocument()
136
+ })
137
+
138
+ it('hides the announcement when the override is an empty string', () => {
139
+ const { container, queryByText } = render(
140
+ <PktLinkCard title="Normal" openInNewTab strings={{ linkcard: { openInNewTab: '' } }}>
141
+ Content
142
+ </PktLinkCard>,
143
+ )
144
+ expect(container.querySelector('.pkt-sr-only')).not.toBeInTheDocument()
145
+ expect(queryByText(nb.linkcard.openInNewTab)).not.toBeInTheDocument()
146
+ })
147
+ })
148
+
106
149
  // ACCESSIBILITY TESTS
107
150
 
108
151
  describe('accessibility', () => {
@@ -2,16 +2,16 @@
2
2
 
3
3
  import classNames from 'classnames'
4
4
  import { HTMLAttributes, ReactNode } from 'react'
5
+ import type { IPktLinkCardBase, TLinkCardSkin } from 'shared-types'
5
6
 
6
7
  import { PktIcon } from '../icon/Icon'
8
+ import { usePktStrings } from '../../hooks/usePktStrings'
7
9
 
8
- export interface IPktLinkCard extends Omit<HTMLAttributes<HTMLAnchorElement>, 'title'> {
9
- skin?: 'normal' | 'no-padding' | 'blue' | 'beige' | 'green' | 'gray' | 'beige-outline' | 'gray-outline'
10
- title?: string
11
- href?: string
12
- iconName?: string
13
- openInNewTab?: boolean
14
- external?: boolean
10
+ export type { TLinkCardSkin }
11
+
12
+ export interface IPktLinkCard
13
+ extends IPktLinkCardBase,
14
+ Omit<HTMLAttributes<HTMLAnchorElement>, 'title'> {
15
15
  children?: ReactNode
16
16
  }
17
17
 
@@ -24,8 +24,10 @@ export const PktLinkCard = ({
24
24
  openInNewTab,
25
25
  external,
26
26
  className,
27
+ strings,
27
28
  ...props
28
29
  }: IPktLinkCard) => {
30
+ const s = usePktStrings(['linkcard'], strings)
29
31
  const classes = ['pkt-linkcard', skin && `pkt-linkcard--${skin}`].filter(Boolean).join(' ')
30
32
 
31
33
  const titleClasses = ['pkt-linkcard__title', external && 'pkt-link pkt-link--external'].filter(Boolean).join(' ')
@@ -40,6 +42,9 @@ export const PktLinkCard = ({
40
42
  >
41
43
  {iconName && <PktIcon className="pkt-link__icon" name={iconName} />}
42
44
  {title && <div className={classNames(titleClasses)}>{title}</div>}
45
+ {openInNewTab && s.linkcard.openInNewTab ? (
46
+ <span className="pkt-sr-only">{s.linkcard.openInNewTab}</span>
47
+ ) : null}
43
48
  <div className="pkt-linkcard__text">{children}</div>
44
49
  </a>
45
50
  )
@@ -26,7 +26,7 @@ export interface IPktStep {
26
26
 
27
27
  const incompleteStepSVG = (
28
28
  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none">
29
- <circle cx="12" cy="12" r="7" style={{ fill: 'var(--pkt-color-grays-grey-200, #CCC)' }} />
29
+ <circle cx="12" cy="12" r="7" style={{ fill: 'var(--pkt-color-brand-neutrals-300, #CCC)' }} />
30
30
  </svg>
31
31
  )
32
32
 
@@ -5,6 +5,7 @@ import { resetPktStrings, setPktStrings } from 'shared-strings'
5
5
  import { PktBackLink } from '../backlink/BackLink'
6
6
  import { PktBreadcrumbs } from '../breadcrumbs/Breadcrumbs'
7
7
  import { PktFileUpload } from '../fileupload/FileUpload'
8
+ import { PktLinkCard } from '../linkcard/LinkCard'
8
9
  import { PktSearchInput } from '../searchinput/SearchInput'
9
10
  import { PktTextinput } from '../textinput/Textinput'
10
11
  import { PktStringsProvider } from './PktStringsProvider'
@@ -25,6 +26,18 @@ describe('strings-propen per komponent', () => {
25
26
  expect(screen.getByLabelText('Tilbake til lista')).toBeInTheDocument()
26
27
  })
27
28
 
29
+ test('linkcard bruker teksten fra propen', () => {
30
+ render(
31
+ <PktLinkCard
32
+ title="Dokument"
33
+ href="/"
34
+ openInNewTab
35
+ strings={{ linkcard: { openInNewTab: 'Opens in a new tab' } }}
36
+ />,
37
+ )
38
+ expect(screen.getByText('Opens in a new tab')).toBeInTheDocument()
39
+ })
40
+
28
41
  test('breadcrumbs bruker teksten fra propen', () => {
29
42
  render(
30
43
  <PktBreadcrumbs