diff --git a/.changeset/great-crews-hang.md b/.changeset/great-crews-hang.md new file mode 100644 index 0000000000..dff4186b55 --- /dev/null +++ b/.changeset/great-crews-hang.md @@ -0,0 +1,5 @@ +--- +"@navikt/ds-react": major +--- + +ErrorSummary: Added fallback text for `heading`. diff --git a/.changeset/little-files-cough.md b/.changeset/little-files-cough.md new file mode 100644 index 0000000000..2cf34eaed9 --- /dev/null +++ b/.changeset/little-files-cough.md @@ -0,0 +1,5 @@ +--- +"@navikt/ds-react": major +--- + +Tooltip: :boom: Updated labeling of items inside Tooltip. [See migration guide for how to update](https://aksel.nav.no/grunnleggende/kode/migrering#3b5cf05fd100). diff --git a/.changeset/small-apples-pretend.md b/.changeset/small-apples-pretend.md new file mode 100644 index 0000000000..48c1c32caf --- /dev/null +++ b/.changeset/small-apples-pretend.md @@ -0,0 +1,5 @@ +--- +"@navikt/aksel-icons": major +--- + +Icons: Removed renamed icons. [See migration guide for changes](https://aksel.nav.no/grunnleggende/kode/migrering#194b60833d9e). diff --git a/.changeset/stupid-pianos-end.md b/.changeset/stupid-pianos-end.md new file mode 100644 index 0000000000..e65fff2069 --- /dev/null +++ b/.changeset/stupid-pianos-end.md @@ -0,0 +1,5 @@ +--- +"@navikt/ds-tailwind": major +--- + +Tailwind: Extended 'screens'-config in theme to match Aksel breakpoints. Tailwind and Primitives can now be used side by side with matching dynamic breakpoints. [See migration guide for potential issues when adopting](https://aksel.nav.no/grunnleggende/kode/migrering#3a2340f6f69b). diff --git a/.changeset/thick-roses-sneeze.md b/.changeset/thick-roses-sneeze.md new file mode 100644 index 0000000000..927e57e140 --- /dev/null +++ b/.changeset/thick-roses-sneeze.md @@ -0,0 +1,6 @@ +--- +"@navikt/ds-react": patch +"@navikt/ds-css": patch +--- + +ErrorSummary: Focus heading instead of container for improved experience with screen reader. diff --git a/@navikt/aksel-icons/icons/BeaconSignals.svg b/@navikt/aksel-icons/icons/BeaconSignals.svg deleted file mode 100644 index 1f9ed4e26a..0000000000 --- a/@navikt/aksel-icons/icons/BeaconSignals.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/@navikt/aksel-icons/icons/BeaconSignals.yml b/@navikt/aksel-icons/icons/BeaconSignals.yml deleted file mode 100644 index d1e4f813bc..0000000000 --- a/@navikt/aksel-icons/icons/BeaconSignals.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: BeaconSignals -category: Development -sub_category: Development -keywords: - - location - - tracking - - identification - - signaling - - broadcasting - - detection - - lokalisering - - sporing - - identifikasjon - - signalisering - - kringkasting - - deteksjon -variant: Stroke -updated_at: 04.12.2023 -created_at: 07.03.2023 diff --git a/@navikt/aksel-icons/icons/BeaconSignalsFill.svg b/@navikt/aksel-icons/icons/BeaconSignalsFill.svg deleted file mode 100644 index b91c73a6c0..0000000000 --- a/@navikt/aksel-icons/icons/BeaconSignalsFill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/@navikt/aksel-icons/icons/BeaconSignalsFill.yml b/@navikt/aksel-icons/icons/BeaconSignalsFill.yml deleted file mode 100644 index d5d98d98ef..0000000000 --- a/@navikt/aksel-icons/icons/BeaconSignalsFill.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: BeaconSignalsFill -category: Development -sub_category: Development -keywords: - - location - - tracking - - identification - - signaling - - broadcasting - - detection - - lokalisering - - sporing - - identifikasjon - - signalisering - - kringkasting - - deteksjon -variant: Fill -updated_at: 04.12.2023 -created_at: 07.03.2023 diff --git a/@navikt/aksel-icons/icons/Buldings2.svg b/@navikt/aksel-icons/icons/Buldings2.svg deleted file mode 100644 index f459660dad..0000000000 --- a/@navikt/aksel-icons/icons/Buldings2.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/@navikt/aksel-icons/icons/Buldings2.yml b/@navikt/aksel-icons/icons/Buldings2.yml deleted file mode 100644 index 35971f6fe2..0000000000 --- a/@navikt/aksel-icons/icons/Buldings2.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Buldings2 -category: Workplace -sub_category: Offices -keywords: - - "[ignore-docs]" -variant: Stroke -updated_at: 16.01.2024 -created_at: 07.03.2023 diff --git a/@navikt/aksel-icons/icons/Buldings2Fill.svg b/@navikt/aksel-icons/icons/Buldings2Fill.svg deleted file mode 100644 index a26d8e5a8c..0000000000 --- a/@navikt/aksel-icons/icons/Buldings2Fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/@navikt/aksel-icons/icons/Buldings2Fill.yml b/@navikt/aksel-icons/icons/Buldings2Fill.yml deleted file mode 100644 index d6c266835c..0000000000 --- a/@navikt/aksel-icons/icons/Buldings2Fill.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Buldings2Fill -category: Workplace -sub_category: Offices -keywords: - - "[ignore-docs]" -variant: Fill -updated_at: 16.01.2024 -created_at: 07.03.2023 diff --git a/@navikt/aksel-icons/icons/Buldings3.svg b/@navikt/aksel-icons/icons/Buldings3.svg deleted file mode 100644 index 2672b3cfc7..0000000000 --- a/@navikt/aksel-icons/icons/Buldings3.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/@navikt/aksel-icons/icons/Buldings3.yml b/@navikt/aksel-icons/icons/Buldings3.yml deleted file mode 100644 index d7d9f28244..0000000000 --- a/@navikt/aksel-icons/icons/Buldings3.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Buldings3 -category: Workplace -sub_category: Offices -keywords: - - "[ignore-docs]" -variant: Stroke -updated_at: 16.01.2024 -created_at: 07.03.2023 diff --git a/@navikt/aksel-icons/icons/Buldings3Fill.svg b/@navikt/aksel-icons/icons/Buldings3Fill.svg deleted file mode 100644 index a26d8e5a8c..0000000000 --- a/@navikt/aksel-icons/icons/Buldings3Fill.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/@navikt/aksel-icons/icons/Buldings3Fill.yml b/@navikt/aksel-icons/icons/Buldings3Fill.yml deleted file mode 100644 index 9e3c4ebda8..0000000000 --- a/@navikt/aksel-icons/icons/Buldings3Fill.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Buldings3Fill -category: Workplace -sub_category: Offices -keywords: - - "[ignore-docs]" -variant: Fill -updated_at: 16.01.2024 -created_at: 07.03.2023 diff --git a/@navikt/core/css/form/error-summary.css b/@navikt/core/css/form/error-summary.css index d03146f5e3..6c5c054fd7 100644 --- a/@navikt/core/css/form/error-summary.css +++ b/@navikt/core/css/form/error-summary.css @@ -9,6 +9,14 @@ padding: var(--a-spacing-3); } +.navds-error-summary__heading { + scroll-margin-top: var(--a-spacing-6); +} + +.navds-error-summary--small .navds-error-summary__heading { + scroll-margin-top: var(--a-spacing-4); +} + .navds-error-summary__heading:focus { outline: none; } diff --git a/@navikt/core/react/src/form/error-summary/ErrorSummary.tsx b/@navikt/core/react/src/form/error-summary/ErrorSummary.tsx index e3fb07a795..8155205472 100644 --- a/@navikt/core/react/src/form/error-summary/ErrorSummary.tsx +++ b/@navikt/core/react/src/form/error-summary/ErrorSummary.tsx @@ -1,10 +1,12 @@ import cl from "clsx"; -import React, { HTMLAttributes, forwardRef, isValidElement } from "react"; +import React, { HTMLAttributes, forwardRef, useRef } from "react"; import { BodyShort, Heading } from "../../typography"; -import { useId } from "../../util/hooks"; -import ErrorSummaryItem, { ErrorSummaryItemType } from "./ErrorSummaryItem"; +import { composeEventHandlers } from "../../util/composeEventHandlers"; +import { useId, useMergeRefs } from "../../util/hooks"; +import ErrorSummaryItem from "./ErrorSummaryItem"; -export interface ErrorSummaryProps extends HTMLAttributes { +export interface ErrorSummaryProps + extends Omit, "tabIndex"> { /** * Collection of `ErrorSummary.Item`. */ @@ -16,6 +18,7 @@ export interface ErrorSummaryProps extends HTMLAttributes { size?: "medium" | "small"; /** * Heading above links. + * @default "Du må rette disse feilene før du kan fortsette:" */ heading?: React.ReactNode; /** @@ -41,7 +44,7 @@ interface ErrorSummaryComponent * * ``` */ - Item: ErrorSummaryItemType; + Item: typeof ErrorSummaryItem; } /** @@ -69,16 +72,21 @@ export const ErrorSummary = forwardRef( className, size = "medium", headingTag = "h2", - heading, + heading = "Du må rette disse feilene før du kan fortsette:", ...rest }, ref, ) => { const headingId = useId(); + const sectionRef = useRef(null); + const headingRef = useRef(null); + + const mergedRef = useMergeRefs(ref, sectionRef); + return (
( aria-live="polite" aria-relevant="all" aria-labelledby={headingId} + onFocus={composeEventHandlers(rest.onFocus, (event) => { + if (event.target === sectionRef.current) { + headingRef?.current?.focus(); + } + })} > {heading} - {React.Children.map(children, (child) => { - if (!isValidElement(child)) { - return null; - } - return
  • {child}
  • ; - })} + {children}
    ); diff --git a/@navikt/core/react/src/form/error-summary/ErrorSummaryItem.tsx b/@navikt/core/react/src/form/error-summary/ErrorSummaryItem.tsx index 107efa31cf..3d23da3ee3 100644 --- a/@navikt/core/react/src/form/error-summary/ErrorSummaryItem.tsx +++ b/@navikt/core/react/src/form/error-summary/ErrorSummaryItem.tsx @@ -14,7 +14,7 @@ export interface ErrorSummaryItemProps href?: string; } -export type ErrorSummaryItemType = OverridableComponent< +type ErrorSummaryItemType = OverridableComponent< ErrorSummaryItemProps, HTMLAnchorElement >; @@ -22,13 +22,15 @@ export type ErrorSummaryItemType = OverridableComponent< export const ErrorSummaryItem: ErrorSummaryItemType = forwardRef( ({ children, as: Component = "a", className, ...rest }, ref) => { return ( - - {children} - +
  • + + {children} + +
  • ); }, ); diff --git a/@navikt/core/react/src/form/error-summary/error-summary.stories.tsx b/@navikt/core/react/src/form/error-summary/error-summary.stories.tsx index ea244f48b7..5253597d23 100644 --- a/@navikt/core/react/src/form/error-summary/error-summary.stories.tsx +++ b/@navikt/core/react/src/form/error-summary/error-summary.stories.tsx @@ -1,24 +1,12 @@ import { Meta, StoryObj } from "@storybook/react"; -import React from "react"; +import { expect, userEvent, within } from "@storybook/test"; +import React, { useRef } from "react"; import { VStack } from "../../layout/stack"; import { ErrorSummary } from "./ErrorSummary"; export default { - title: "ds-react/Errorsummary", + title: "ds-react/ErrorSummary", component: ErrorSummary, - argTypes: { - headingTag: { - control: { - type: "text", - }, - }, - size: { - control: { - type: "radio", - }, - options: ["medium", "small"], - }, - }, parameters: { chromatic: { disable: true }, }, @@ -27,18 +15,32 @@ export default { type Story = StoryObj; export const Default: Story = { - render: (props) => ( - + render: ({ headingTag, ...rest }) => ( + Checkbox må fylles ut Tekstfeltet må ha en godkjent e-mail ), + argTypes: { + heading: { + control: { + type: "text", + }, + }, + headingTag: { + control: { + type: "text", + }, + }, + size: { + control: { + type: "radio", + }, + options: ["medium", "small"], + }, + }, }; export const Small: Story = { @@ -52,6 +54,59 @@ export const Small: Story = { ), }; +export const A11yDemo: Story = { + name: "A11y Demo", + render: () => { + const ref = useRef(null); + return ( +
    + + + Checkbox må fylles ut + + Tekstfeltet må ha en godkjent e-mail + + +
    + ); + }, +}; + +export const FocusDemo: Story = { + render: () => { + const ref = useRef(null); + return ( +
    + + + Checkbox må fylles ut + + Tekstfeltet må ha en godkjent e-mail + + +
    + ); + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + + const button = canvas.getByText("Focus summary"); + const heading = canvas.getByText("Feiloppsummering tittel"); + + await step("click button", async () => { + await userEvent.click(button); + }); + + expect(heading).toHaveFocus(); + }, +}; + export const Chromatic: Story = { render: () => ( diff --git a/@navikt/core/react/src/tooltip/Tooltip.tsx b/@navikt/core/react/src/tooltip/Tooltip.tsx index cc7c6aa384..27c2643142 100644 --- a/@navikt/core/react/src/tooltip/Tooltip.tsx +++ b/@navikt/core/react/src/tooltip/Tooltip.tsx @@ -12,9 +12,10 @@ import { useInteractions, } from "@floating-ui/react"; import cl from "clsx"; -import React, { HTMLAttributes, cloneElement, forwardRef, useRef } from "react"; +import React, { HTMLAttributes, forwardRef, useRef } from "react"; import { useModalContext } from "../modal/Modal.context"; import { Portal } from "../portal"; +import { Slot } from "../slot/Slot"; import { Detail } from "../typography"; import { useId } from "../util/hooks"; import { useControllableState } from "../util/hooks/useControllableState"; @@ -80,6 +81,12 @@ export interface TooltipProps extends HTMLAttributes { * List of Keyboard-keys for shortcuts. */ keys?: string[]; + /** + * When false, Tooltip labels the element, and child-elements content will be ignored by screen-readers. + * When true, content is added as additional information to the child element. + * @default false + */ + describesChild?: boolean; } /** @@ -89,9 +96,9 @@ export interface TooltipProps extends HTMLAttributes { * @see 🏷️ {@link TooltipProps} * * @example - * ```jsx + * ```jsx Tooltip as only form of labeling * - * - - -); - export const Default = (props) => { return ( { arrow={props?.arrow} delay={props?.delay} offset={props?.offset} + describesChild={props?.describesChild} > - + ); }; @@ -65,6 +50,7 @@ Default.args = { keys: false, arrow: true, delay: 150, + describesChild: true, }; export const Placement = () => { @@ -90,10 +76,10 @@ export const Keys = () => { return ( -
    Element
    +
    ); }; diff --git a/@navikt/core/tailwind/src/index.ts b/@navikt/core/tailwind/src/index.ts index f7a8332b26..156ec40144 100644 --- a/@navikt/core/tailwind/src/index.ts +++ b/@navikt/core/tailwind/src/index.ts @@ -18,7 +18,7 @@ const tokens = Object.entries(TokensBuild).reduce((old, [key, val]) => { const config = { theme: { colors: getColors(tokens), - screen: getBreakpoints(tokens), + screens: getBreakpoints(tokens), extend: { spacing: Reducer(tokens, ["spacing"]), zIndex: Reducer(tokens, ["z-index"]), diff --git a/aksel.nav.no/website/components/layout/templates/WithSidebar.tsx b/aksel.nav.no/website/components/layout/templates/WithSidebar.tsx index 5d24b1b5f6..085532819f 100644 --- a/aksel.nav.no/website/components/layout/templates/WithSidebar.tsx +++ b/aksel.nav.no/website/components/layout/templates/WithSidebar.tsx @@ -109,7 +109,7 @@ export const WithSidebar = ({ )} {variant === "landingPage" && ( -
    +
    )} diff --git a/aksel.nav.no/website/pages/eksempler/errorsummary/demo.tsx b/aksel.nav.no/website/pages/eksempler/errorsummary/demo.tsx index 646edfae2b..0626a598e8 100644 --- a/aksel.nav.no/website/pages/eksempler/errorsummary/demo.tsx +++ b/aksel.nav.no/website/pages/eksempler/errorsummary/demo.tsx @@ -3,7 +3,7 @@ import { withDsExample } from "@/web/examples/withDsExample"; const Example = () => { return ( - + Felt må fylles ut med alder diff --git a/aksel.nav.no/website/pages/eksempler/errorsummary/setting-focus.tsx b/aksel.nav.no/website/pages/eksempler/errorsummary/setting-focus.tsx index d14c010370..a9da97612c 100644 --- a/aksel.nav.no/website/pages/eksempler/errorsummary/setting-focus.tsx +++ b/aksel.nav.no/website/pages/eksempler/errorsummary/setting-focus.tsx @@ -13,10 +13,7 @@ const Example = () => { return ( {hasError && ( - + Felt må fylles ut med alder @@ -40,5 +37,5 @@ export const Demo = { export const args = { index: 3, - desc: "Sett fokus på ErrorSummary ved submit. Hvis du gjør en ny sidelasting kan du også sette en ID på ErrorSummary og referere til den i URLens hash.", + desc: "Sett fokus på ErrorSummary ved submit. Hvis du gjør en ny sidelasting kan du også sette en `id` og referere til den i URLens hash.", }; diff --git a/aksel.nav.no/website/pages/eksempler/errorsummary/small.tsx b/aksel.nav.no/website/pages/eksempler/errorsummary/small.tsx index c256d7c992..a95209c9b0 100644 --- a/aksel.nav.no/website/pages/eksempler/errorsummary/small.tsx +++ b/aksel.nav.no/website/pages/eksempler/errorsummary/small.tsx @@ -3,10 +3,7 @@ import { withDsExample } from "@/web/examples/withDsExample"; const Example = () => { return ( - + Felt må fylles ut med alder diff --git a/aksel.nav.no/website/pages/eksempler/togglegroup/with-tooltip.tsx b/aksel.nav.no/website/pages/eksempler/togglegroup/with-tooltip.tsx index 43c40113f9..d5431e2b37 100644 --- a/aksel.nav.no/website/pages/eksempler/togglegroup/with-tooltip.tsx +++ b/aksel.nav.no/website/pages/eksempler/togglegroup/with-tooltip.tsx @@ -12,20 +12,17 @@ const Example = () => { } + icon={} /> } + icon={} /> - } - /> + } /> ); diff --git a/aksel.nav.no/website/pages/eksempler/tooltip/demo.tsx b/aksel.nav.no/website/pages/eksempler/tooltip/demo.tsx index 893a8816d8..409eba9377 100644 --- a/aksel.nav.no/website/pages/eksempler/tooltip/demo.tsx +++ b/aksel.nav.no/website/pages/eksempler/tooltip/demo.tsx @@ -5,7 +5,7 @@ import { withDsExample } from "@/web/examples/withDsExample"; const Example = () => { return ( - + + + ); +}; + +// EXAMPLES DO NOT INCLUDE CONTENT BELOW THIS LINE +export default withDsExample(Example); + +/* Storybook story */ +export const Demo = { + render: Example, +}; + +export const args = { + index: 4, + desc: "Hvis tooltip ikke er eneste form for tekstlig beskrivelse, kan du sette 'describesChild' til 'true'. Tooltip content blir da satt som 'title' (lukket) og 'aria-describedby' (åpen).", +}; diff --git a/aksel.nav.no/website/pages/eksempler/tooltip/no-arrow.tsx b/aksel.nav.no/website/pages/eksempler/tooltip/no-arrow.tsx index b1a10e391f..0f9e1a619a 100644 --- a/aksel.nav.no/website/pages/eksempler/tooltip/no-arrow.tsx +++ b/aksel.nav.no/website/pages/eksempler/tooltip/no-arrow.tsx @@ -5,7 +5,7 @@ import { withDsExample } from "@/web/examples/withDsExample"; const Example = () => { return ( -
    ); diff --git a/aksel.nav.no/website/pages/eksempler/tooltip/with-shortcuts.tsx b/aksel.nav.no/website/pages/eksempler/tooltip/with-shortcuts.tsx index 7fc1c2fb65..15923bfb0a 100644 --- a/aksel.nav.no/website/pages/eksempler/tooltip/with-shortcuts.tsx +++ b/aksel.nav.no/website/pages/eksempler/tooltip/with-shortcuts.tsx @@ -5,7 +5,7 @@ import { withDsExample } from "@/web/examples/withDsExample"; const Example = () => { return ( -