diff --git a/docs/docs/a11y.md b/docs/docs/a11y.md index 8129c4c7bd73..7cc09b9b0df3 100644 --- a/docs/docs/a11y.md +++ b/docs/docs/a11y.md @@ -105,7 +105,7 @@ yarn rw g layout main --skipLink ```jsx title="web/src/layouts/MainLayout/MainLayout.js" import { SkipNavLink, SkipNavContent } from '@redwoodjs/router' -import '@reach/skip-nav/styles.css' +import '@redwoodjs/router/skip-nav.css' const MainLayout = ({ children }) => { return ( @@ -122,7 +122,7 @@ export default MainLayout ``` `SkipNavLink` renders a link that remains hidden till focused and `SkipNavContent` renders a div as the target for the link. -For more on these components, see [Reach UI's docs](https://reach.tech/skip-nav/#reach-skip-nav). +The code for these components comes from Reach UI. For more details, see [Reach UI's docs](https://reach.tech/skip-nav/#reach-skip-nav). One thing you'll probably want to do is change the URL the skip link sends the user to when activated. You can do that by changing the `contentId` and `id` props of `SkipNavLink` and `SkipNavContent` respectively: diff --git a/packages/cli/src/commands/generate/layout/__tests__/__snapshots__/layout.test.ts.snap b/packages/cli/src/commands/generate/layout/__tests__/__snapshots__/layout.test.ts.snap index 2fe095bbdd3d..12cfbf0e20e0 100644 --- a/packages/cli/src/commands/generate/layout/__tests__/__snapshots__/layout.test.ts.snap +++ b/packages/cli/src/commands/generate/layout/__tests__/__snapshots__/layout.test.ts.snap @@ -2,7 +2,7 @@ exports[`JavaScript: includes skip link when --skipLink is set to true 1`] = ` "import { SkipNavLink, SkipNavContent } from '@redwoodjs/router' -import '@reach/skip-nav/styles.css' +import '@redwoodjs/router/skip-nav.css' /** * since the main content isn't usually the first thing in the document, @@ -109,7 +109,7 @@ describe('AppLayout', () => { exports[`TypeScript: includes skip link when --skipLink is set to true 1`] = ` "import { SkipNavLink, SkipNavContent } from '@redwoodjs/router' -import '@reach/skip-nav/styles.css' +import '@redwoodjs/router/skip-nav.css' /** * since the main content isn't usually the first thing in the document, diff --git a/packages/cli/src/commands/generate/layout/templates/layout.tsx.a11yTemplate b/packages/cli/src/commands/generate/layout/templates/layout.tsx.a11yTemplate index 6d52879f8ec6..3294ae6e678d 100644 --- a/packages/cli/src/commands/generate/layout/templates/layout.tsx.a11yTemplate +++ b/packages/cli/src/commands/generate/layout/templates/layout.tsx.a11yTemplate @@ -1,5 +1,5 @@ import { SkipNavLink, SkipNavContent } from '@redwoodjs/router' -import '@reach/skip-nav/styles.css' +import '@redwoodjs/router/skip-nav.css' /** * since the main content isn't usually the first thing in the document, diff --git a/packages/forms/package.json b/packages/forms/package.json index 14ca529d12b2..495016c02c62 100644 --- a/packages/forms/package.json +++ b/packages/forms/package.json @@ -25,6 +25,7 @@ "dependencies": { "@babel/runtime-corejs3": "7.23.9", "core-js": "3.35.1", + "graphql": "16.8.1", "pascalcase": "1.0.0", "react-hook-form": "7.49.3" }, @@ -38,7 +39,6 @@ "@types/pascalcase": "1.0.3", "@types/react": "18.2.37", "@types/react-dom": "18.2.15", - "graphql": "16.8.1", "nodemon": "3.0.2", "react": "0.0.0-experimental-e5205658f-20230913", "react-dom": "0.0.0-experimental-e5205658f-20230913", @@ -46,7 +46,6 @@ "vitest": "1.2.2" }, "peerDependencies": { - "graphql": "16.8.1", "react": "0.0.0-experimental-e5205658f-20230913" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" diff --git a/packages/router/package.json b/packages/router/package.json index a188f880776a..4de556f08c3d 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -10,7 +10,8 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "files": [ - "dist" + "dist", + "skip-nav.css" ], "scripts": { "build": "yarn build:js && yarn build:types", @@ -25,7 +26,6 @@ }, "dependencies": { "@babel/runtime-corejs3": "7.23.9", - "@reach/skip-nav": "0.18.0", "@redwoodjs/auth": "6.0.7", "core-js": "3.35.1" }, diff --git a/packages/router/skip-nav.css b/packages/router/skip-nav.css new file mode 100644 index 000000000000..def5fcc4f07d --- /dev/null +++ b/packages/router/skip-nav.css @@ -0,0 +1,33 @@ +/** + * Original code source https://github.com/reach/reach-ui + * https://github.com/reach/reach-ui/blob/dev/packages/skip-nav/styles.css + */ + +:root { + --reach-skip-nav: 1; +} + +[data-reach-skip-nav-link] { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + width: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + position: absolute; +} + +[data-reach-skip-nav-link]:focus { + padding: 1rem; + position: fixed; + top: 10px; + left: 10px; + background: white; + z-index: 1; + width: auto; + height: auto; + clip: auto; +} + + diff --git a/packages/router/src/index.ts b/packages/router/src/index.ts index 56c715a8bbec..3840c793a140 100644 --- a/packages/router/src/index.ts +++ b/packages/router/src/index.ts @@ -24,6 +24,8 @@ export * from './useMatch' export { parseSearch, getRouteRegexAndParams, matchPath } from './util' +export { SkipNavLink, SkipNavContent } from './skipNav' + /** * A more specific interface is created in `.redwood/types/includes/web-routerRoutes` * when the site is built, which will describe all known routes. @@ -37,7 +39,5 @@ export { parseSearch, getRouteRegexAndParams, matchPath } from './util' // Keep this in index.ts so it can be extended with declaration merging export interface AvailableRoutes {} -export { SkipNavLink, SkipNavContent } from '@reach/skip-nav' - // Used by packages/internal/src/generate/templates/web-routerRoutes.d.ts.template export * from './routeParamsTypes' diff --git a/packages/router/src/skipNav.tsx b/packages/router/src/skipNav.tsx new file mode 100644 index 000000000000..61f8f89d83ef --- /dev/null +++ b/packages/router/src/skipNav.tsx @@ -0,0 +1,166 @@ +// Original Code Source https://github.com/reach/reach-ui +// Moving here to resolve unmet peer dependency issues related to React 18 +// If resolved, should consider reverting to @reach/skip-nav +// See: https://github.com/reach/reach-ui/issues/916 + +import * as React from 'react' + +//////////////////////////////////////////////////////////////////////////////// + +// Original Code Source @reach/polymorphic +// https://github.com/reach/reach-ui/blob/dev/packages/polymorphic/src/reach-polymorphic.ts + +type Merge = Omit & P2 + +type ForwardRefExoticComponent = React.ForwardRefExoticComponent< + Merge< + E extends React.ElementType ? React.ComponentPropsWithRef : never, + OwnProps & { as?: E } + > +> + +interface ForwardRefComponent< + IntrinsicElementString, + OwnProps = {} + /* + * Extends original type to ensure built in React types play nice with + * polymorphic components still e.g. `React.ElementRef` etc. + */ +> extends ForwardRefExoticComponent { + /* + * When `as` prop is passed, use this overload. Merges original own props + * (without DOM props) and the inferred props from `as` element with the own + * props taking precendence. + * + * We explicitly avoid `React.ElementType` and manually narrow the prop types + * so that events are typed when using JSX.IntrinsicElements. + */ + ( + props: As extends '' + ? { as: keyof JSX.IntrinsicElements } + : As extends React.ComponentType + ? Merge + : As extends keyof JSX.IntrinsicElements + ? Merge + : never + ): React.ReactElement | null +} + +//////////////////////////////////////////////////////////////////////////////// + +// Original Code Source @reach/skip-nav +// https://github.com/reach/reach-ui/blob/dev/packages/skip-nav/src/reach-skip-nav.tsx + +// The user may want to provide their own ID (maybe there are multiple nav +// menus on a page a use might want to skip at various points in tabbing?). +const defaultId = 'reach-skip-nav' + +/** + * SkipNavLink + * + * Renders a link that remains hidden until focused to skip to the main content. + * + * @see Docs https://reach.tech/skip-nav#skipnavlink + */ +const SkipNavLink = React.forwardRef(function SkipNavLink( + { as: Comp = 'a', children = 'Skip to content', contentId, ...props }, + forwardedRef +) { + const id = contentId || defaultId + return ( + + {children} + + ) +}) as ForwardRefComponent<'a', SkipNavLinkProps> + +/** + * @see Docs https://reach.tech/skip-nav#skipnavlink-props + */ +interface SkipNavLinkProps { + /** + * Allows you to change the text for your preferred phrase or localization. + * + * @see Docs https://reach.tech/skip-nav#skipnavlink-children + */ + children?: React.ReactNode + /** + * An alternative ID for `SkipNavContent`. If used, the same value must be + * provided to the `id` prop in `SkipNavContent`. + * + * @see Docs https://reach.tech/skip-nav#skipnavlink-contentid + */ + contentId?: string +} + +SkipNavLink.displayName = 'SkipNavLink' + +//////////////////////////////////////////////////////////////////////////////// + +/** + * SkipNavContent + * + * Renders a div as the target for the link. + * + * @see Docs https://reach.tech/skip-nav#skipnavcontent + */ +const SkipNavContent = React.forwardRef(function SkipNavContent( + { as: Comp = 'div', id: idProp, ...props }, + forwardedRef +) { + const id = idProp || defaultId + return ( + + ) +}) as ForwardRefComponent<'div', SkipNavContentProps> + +/** + * @see Docs https://reach.tech/skip-nav#skipnavcontent-props + */ +interface SkipNavContentProps { + /** + * You can place the `SkipNavContent` element as a sibling to your main + * content or as a wrapper. + * + * Keep in mind it renders a `div`, so it may mess with your CSS depending on + * where it’s placed. + * + * @example + * + * + * // vs. + * + * + * + * + * @see Docs https://reach.tech/skip-nav#skipnavcontent-children + */ + children?: React.ReactNode + /** + * An alternative ID. If used, the same value must be provided to the + * `contentId` prop in `SkipNavLink`. + * + * @see Docs https://reach.tech/skip-nav#skipnavcontent-id + */ + id?: string +} + +SkipNavContent.displayName = 'SkipNavContent' + +//////////////////////////////////////////////////////////////////////////////// +// Exports + +export type { SkipNavContentProps, SkipNavLinkProps } +export { SkipNavLink, SkipNavContent } diff --git a/yarn.lock b/yarn.lock index 4e99e2396617..d5940098b61e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7329,27 +7329,6 @@ __metadata: languageName: node linkType: hard -"@reach/polymorphic@npm:0.18.0": - version: 0.18.0 - resolution: "@reach/polymorphic@npm:0.18.0" - peerDependencies: - react: ^16.8.0 || 17.x - checksum: dfde6dc901005f92e16f0e3601f0c659b70ee14d91e612cd68c9a918744fd94de30e8065d73663b72964225d3476f377c650daf2ac1e256de61df9ee386aabdc - languageName: node - linkType: hard - -"@reach/skip-nav@npm:0.18.0": - version: 0.18.0 - resolution: "@reach/skip-nav@npm:0.18.0" - dependencies: - "@reach/polymorphic": "npm:0.18.0" - peerDependencies: - react: ^16.8.0 || 17.x - react-dom: ^16.8.0 || 17.x - checksum: 30e3d4c568093e170b9e80ee42764a080c0a15ab3f9c7edf78dcc6ebd92f156799dba0f544757b84401806765f60fe527c869ef86d5323c0a7a3bdccf1a68f85 - languageName: node - linkType: hard - "@react-email/render@npm:0.0.10": version: 0.0.10 resolution: "@react-email/render@npm:0.0.10" @@ -8344,7 +8323,6 @@ __metadata: typescript: "npm:5.3.3" vitest: "npm:1.2.2" peerDependencies: - graphql: 16.8.1 react: 0.0.0-experimental-e5205658f-20230913 languageName: unknown linkType: soft @@ -8644,7 +8622,6 @@ __metadata: "@babel/cli": "npm:7.23.9" "@babel/core": "npm:^7.22.20" "@babel/runtime-corejs3": "npm:7.23.9" - "@reach/skip-nav": "npm:0.18.0" "@redwoodjs/auth": "npm:6.0.7" "@types/react": "npm:18.2.37" "@types/react-dom": "npm:18.2.15"