diff --git a/packages/lab/src/__tests__/__e2e__/skip-link/SkipLink.cy.tsx b/packages/lab/src/__tests__/__e2e__/skip-link/SkipLink.cy.tsx index 2f9bf628278..c8e61c39e31 100644 --- a/packages/lab/src/__tests__/__e2e__/skip-link/SkipLink.cy.tsx +++ b/packages/lab/src/__tests__/__e2e__/skip-link/SkipLink.cy.tsx @@ -1,4 +1,3 @@ -import { SkipLink, SkipLinks } from "@salt-ds/lab"; import * as skipLinkStories from "@stories/skip-link/skip-link.stories"; import { composeStories } from "@storybook/react"; import { checkAccessibility } from "../../../../../../cypress/tests/checkAccessibility"; @@ -6,61 +5,6 @@ import { checkAccessibility } from "../../../../../../cypress/tests/checkAccessi const composedStories = composeStories(skipLinkStories); const { Default, MultipleLinks } = composedStories; -const NoTargetRef = () => { - return ( - <> -
- - Click here and press the Tab key to see the Skip Link - -
- - - Skip to main content - - -
- What we do -
- -
-
-

Salt

-

- Salt provides you with a suite of UI components and a flexible - theming system. With no customisation, the default theme offers - an attractive and modern look-and-feel, with both light and dark - variants and support for a range of UI densities. We have - included a theming system which allows you to easily create - theme variations, or in fact substitute alternate themes. -

-
-
-

Goals

-

Salt has been developed with the following design goals:

-
    -
  • - Providing a comprehensive set of commonly-used UI controls -
  • -
  • Complying with WCAG 2.1 accessibility guidelines
  • -
  • To be lightweight and performant
  • -
  • Offering flexible styling and theming support
  • -
  • Minimizing dependencies on third-party libraries
  • -
-
-
-
-
- - ); -}; - describe("GIVEN a SkipLink", () => { checkAccessibility(composedStories); describe("WHEN there is a single SkipLink", () => { @@ -70,22 +14,22 @@ describe("GIVEN a SkipLink", () => { "Click here and press the Tab key to see the Skip Link", ).click(); cy.realPress("Tab"); - cy.findByTestId("skipLink").should("be.visible"); - cy.findByTestId("skipLink").click(); - + cy.findByRole("link", { name: "Skip to main content" }).should( + "be.visible", + ); + cy.findByRole("link", { name: "Skip to main content" }).click(); cy.get("#main").should("be.focused"); }); - it("THEN it should not move focus if no target ref is given", () => { - cy.mount(); + it("THEN it should hide the skip link if ref is broken", () => { + cy.mount(); cy.findByText( "Click here and press the Tab key to see the Skip Link", ).click(); cy.realPress("Tab"); - cy.findByTestId("skipLink").should("be.visible"); - cy.findByTestId("skipLink").click(); - cy.findByTestId("skipLink").should("not.be.focused"); - cy.get("#main").should("not.be.focused"); + cy.findByRole("link", { name: "Skip to main content" }).should( + "not.exist", + ); }); }); @@ -96,11 +40,14 @@ describe("GIVEN a SkipLink", () => { "Click here and press the Tab key to see the Skip Link", ).click(); cy.realPress("Tab"); + cy.findByRole("link", { name: "Skip to Introduction" }).should( + "be.visible", + ); cy.realPress("Tab"); + cy.findByRole("link", { name: "Skip to Goals" }).should("be.visible"); cy.realPress("Enter"); cy.get("#goals").should("be.focused"); - cy.get("#introduction").should("not.be.focused"); }); }); }); diff --git a/packages/lab/src/skip-link/SkipLink.css b/packages/lab/src/skip-link/SkipLink.css index da55c1c1650..408cddafa40 100644 --- a/packages/lab/src/skip-link/SkipLink.css +++ b/packages/lab/src/skip-link/SkipLink.css @@ -1,52 +1,35 @@ -/* CSS Variables for the Skip Link */ -.saltSkipLink { - --skipLink-padding: var(--saltSkipLink-padding, var(--salt-size-unit)); - --skipLink-margin: var(--saltSkipLink-margin, var(--salt-size-unit)); - --skipLink-background: var(--saltSkipLink-background, var(--salt-actionable-primary-background)); - --skipLink-color: var(--saltSkipLink-color, var(--salt-content-primary-foreground)); -} - -/* Overrides */ -.saltSkipLink { - --saltLink-color-focus: var(--skipLink-color); -} - -.saltSkipLink-target { - --skipLink-target-focus: var(--salt-focused-outline); -} - /*Styles applied when the link is focused to hide the Skip Link when not in focus*/ + .saltSkipLink { top: 0; left: 0; + opacity: 0; width: 1px; height: 1px; - display: block; - opacity: 0; + margin: 0; + padding: 0; overflow: hidden; position: absolute; + + color: var(--salt-content-primary-foreground); + letter-spacing: var(--salt-text-letterSpacing); + text-decoration: var(--salt-navigable-textDecoration); + font-family: var(--salt-text-fontFamily); + white-space: nowrap; + background: var(--saltSkipLink-background, var(--salt-container-primary-background)); } /* Styles applied when the link is focused to display the Skip Link only when in focus*/ .saltSkipLink:focus { opacity: 1; width: auto; - height: auto; - white-space: nowrap; - margin: var(--skipLink-margin); - padding: calc(var(--skipLink-padding) - 1px) var(--skipLink-padding) var(--skipLink-padding); - background: var(--skipLink-background); - color: var(--skipLink-color); - box-shadow: var(--salt-overlayable-shadow-popout); -} - -.saltSkipLink { - font-size: var(--salt-text-fontSize); - font-family: var(--saltSkipLink-fontFamily, var(--salt-text-fontFamily)); - line-height: var(--saltSkipLink-lineHeight, var(--salt-text-lineHeight)); + height: max(var(--salt-size-base), auto); + padding: var(--salt-spacing-100) var(--salt-spacing-300); + outline: var(--salt-focused-outline); + outline-offset: calc(-1 * var(--salt-focused-outlineWidth)); + box-shadow: var(--salt-overlayable-shadow); } -/*Styles applied to the skip link focus target*/ .saltSkipLink-target { - outline: var(--skipLink-target-focus); + outline: var(--salt-focused-outline); } diff --git a/packages/lab/src/skip-link/SkipLink.tsx b/packages/lab/src/skip-link/SkipLink.tsx index 3c8359c2ff5..61608ab620b 100644 --- a/packages/lab/src/skip-link/SkipLink.tsx +++ b/packages/lab/src/skip-link/SkipLink.tsx @@ -1,8 +1,8 @@ -import { Link, type LinkProps, makePrefixer } from "@salt-ds/core"; +import { type LinkProps, makePrefixer } from "@salt-ds/core"; import { useComponentCssInjection } from "@salt-ds/styles"; import { useWindow } from "@salt-ds/window"; import { clsx } from "clsx"; -import { type RefObject, forwardRef } from "react"; +import { forwardRef, useEffect, useRef } from "react"; import { useManageFocusOnTarget } from "./internal/useManageFocusOnTarget"; import skipLinkCss from "./SkipLink.css"; @@ -16,13 +16,13 @@ interface SkipLinkProps extends LinkProps { * Refs are referentially stable so if this changes it won't be picked up * will need to find a better way of passing in the target element to apply the attributes */ - targetRef?: RefObject; + target: string; } const withBaseName = makePrefixer("saltSkipLink"); export const SkipLink = forwardRef( - function SkipLink({ className, targetRef, ...rest }, ref) { + function SkipLink({ className, target, children, ...rest }, ref) { const targetWindow = useWindow(); useComponentCssInjection({ testId: "salt-skip-link", @@ -30,19 +30,31 @@ export const SkipLink = forwardRef( window: targetWindow, }); - const targetClass = clsx(withBaseName("target"), className); + const targetRef = useRef(null); - const eventHandlers = useManageFocusOnTarget({ targetRef, targetClass }); + const targetClass = withBaseName("target"); + useEffect(() => { + targetRef.current = document.getElementById(target); + }, [target]); + + const eventHandlers = useManageFocusOnTarget({ + targetRef, + targetClass, + }); return ( -
  • - -
  • + target="_self" + {...rest} + {...eventHandlers} + > + {children} + + ) ); }, ); diff --git a/packages/lab/src/skip-link/SkipLinks.css b/packages/lab/src/skip-link/SkipLinks.css deleted file mode 100644 index 763cbb7604a..00000000000 --- a/packages/lab/src/skip-link/SkipLinks.css +++ /dev/null @@ -1,7 +0,0 @@ -.saltSkipLinks { - position: relative; - float: left; - list-style: none; - margin: 0; - padding: 0; -} diff --git a/packages/lab/src/skip-link/SkipLinks.tsx b/packages/lab/src/skip-link/SkipLinks.tsx deleted file mode 100644 index 7a0b8e83804..00000000000 --- a/packages/lab/src/skip-link/SkipLinks.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { makePrefixer } from "@salt-ds/core"; -import { clsx } from "clsx"; -import { type HTMLAttributes, forwardRef } from "react"; - -import { useComponentCssInjection } from "@salt-ds/styles"; -import { useWindow } from "@salt-ds/window"; - -import skipLinksCss from "./SkipLinks.css"; - -const withBaseName = makePrefixer("saltSkipLinks"); - -export const SkipLinks = forwardRef< - HTMLUListElement, - HTMLAttributes ->(function SkipLinks(props, ref) { - const { className, children, ...restProps } = props; - const targetWindow = useWindow(); - useComponentCssInjection({ - testId: "salt-skip-links", - css: skipLinksCss, - window: targetWindow, - }); - - return ( -
      - {children} -
    - ); -}); diff --git a/packages/lab/src/skip-link/index.ts b/packages/lab/src/skip-link/index.ts index 1de755de460..cb4f10ea24e 100644 --- a/packages/lab/src/skip-link/index.ts +++ b/packages/lab/src/skip-link/index.ts @@ -1,2 +1 @@ export * from "./SkipLink"; -export * from "./SkipLinks"; diff --git a/packages/lab/stories/skip-link/skip-link.stories.tsx b/packages/lab/stories/skip-link/skip-link.stories.tsx index 1f264821f8d..97aad6c8630 100644 --- a/packages/lab/stories/skip-link/skip-link.stories.tsx +++ b/packages/lab/stories/skip-link/skip-link.stories.tsx @@ -1,42 +1,34 @@ -import { Button } from "@salt-ds/core"; -import { SkipLink, SkipLinks } from "@salt-ds/lab"; +import { + Button, + Divider, + H2, + H3, + SplitLayout, + StackLayout, + Text, +} from "@salt-ds/core"; +import { SkipLink } from "@salt-ds/lab"; import type { Meta, StoryFn } from "@storybook/react"; -import { useRef } from "react"; -import "./skip-link.stories.css"; export default { title: "Lab/Skip Link", component: SkipLink, } as Meta; -export const Default: StoryFn = () => { - const articleRef = useRef(null); - +const DefaultStory: StoryFn = (args) => { return ( - <> - + + Click here and press the Tab key to see the Skip Link - -
    - - - Skip to main content - - - -
    - What we do -
    + +
    + Skip to main content + +

    What we do

    -
    +
    -

    Salt

    +

    Salt

    Salt provides you with a suite of UI components and a flexible theming system. With no customisation, the default theme offers an @@ -47,7 +39,7 @@ export const Default: StoryFn = () => {

    -

    Goals

    +

    Goals

    Salt has been developed with the following design goals:

    • @@ -60,46 +52,31 @@ export const Default: StoryFn = () => {
    -
    - -
    + Next} />
    - + ); }; +export const Default = DefaultStory.bind({}); +Default.args = { + target: "main", +}; export const MultipleLinks: StoryFn = () => { - const sectionRef1 = useRef(null); - const sectionRef2 = useRef(null); - return ( - <> - + + Click here and press the Tab key to see the Skip Link - -
    - - - Skip to Introduction - - - Skip to Goals - - - -
    - What we do -
    + +
    + Skip to Introduction + Skip to Goals + +

    What we do

    -
    -

    Salt

    +
    +

    Salt

    Salt provides you with a suite of UI components and a flexible theming system. With no customisation, the default theme offers an @@ -109,8 +86,8 @@ export const MultipleLinks: StoryFn = () => { variations, or in fact substitute alternate themes.

    -
    -

    Goals

    +
    +

    Goals

    Salt has been developed with the following design goals:

    • @@ -123,10 +100,8 @@ export const MultipleLinks: StoryFn = () => {
    -
    - -
    + Next} />
    - + ); };