fix(BOOK-468): Added inert attribute to SidePeekSEO element to ignore tab navigation and screen readers
Approved-by: Linus Flood
This commit is contained in:
@@ -2,6 +2,8 @@ import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||
import SidePeek from "@scandic-hotels/design-system/SidePeek"
|
||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||
|
||||
import { env } from "@/env/server"
|
||||
|
||||
import { getIntl } from "@/i18n"
|
||||
import { appendSlugToPathname } from "@/utils/appendSlugToPathname"
|
||||
|
||||
@@ -21,6 +23,7 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
heading,
|
||||
}: MeetingsAndConferencesSidePeekProps) {
|
||||
const intl = await getIntl()
|
||||
const shouldInert = env.SEO_INERT
|
||||
const { seatingText, roomText } = await getConferenceRoomTexts(meetingRooms)
|
||||
const visibleImages = meetingFacilities?.heroImages.slice(0, 2)
|
||||
const meetingPageHref = await appendSlugToPathname(meetingPageUrl)
|
||||
@@ -33,6 +36,7 @@ export default async function MeetingsAndConferencesSidePeek({
|
||||
id: "common.close",
|
||||
defaultMessage: "Close",
|
||||
})}
|
||||
shouldInert={shouldInert}
|
||||
>
|
||||
<div className={styles.wrapper}>
|
||||
<Typography variant="Title/Subtitle/lg">
|
||||
|
||||
6
apps/scandic-web/env/server.ts
vendored
6
apps/scandic-web/env/server.ts
vendored
@@ -107,6 +107,11 @@ export const env = createEnv({
|
||||
// transform to boolean
|
||||
.transform((s) => s === "true")
|
||||
.default("false"),
|
||||
SEO_INERT: z
|
||||
.string()
|
||||
.refine((s) => s === "1" || s === "0")
|
||||
.transform((s) => s === "1")
|
||||
.default("0"),
|
||||
},
|
||||
emptyStringAsUndefined: true,
|
||||
runtimeEnv: {
|
||||
@@ -162,5 +167,6 @@ export const env = createEnv({
|
||||
HOTEL_BRANDING: process.env.HOTEL_BRANDING,
|
||||
CHATBOT_LIVE_LANGS: process.env.CHATBOT_LIVE_LANGS,
|
||||
NEW_STAYS_ON_MY_PAGES: process.env.NEW_STAYS_ON_MY_PAGES,
|
||||
SEO_INERT: process.env.SEO_INERT,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import type { SidePeekSelfControlledProps } from './sidePeek'
|
||||
|
||||
// Sidepeeks generally have important content that should be indexed by search engines.
|
||||
// The content is hidden behind a modal, but it is still important for SEO.
|
||||
// This component is used to provide SEO information for the sidepeek content.
|
||||
export default function SidePeekSEO({
|
||||
title,
|
||||
children,
|
||||
}: React.PropsWithChildren<Pick<SidePeekSelfControlledProps, 'title'>>) {
|
||||
return (
|
||||
<div className="sr-only">
|
||||
<h2>{title}</h2>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import { IconButton } from '../../IconButton'
|
||||
import { MaterialIcon } from '../../Icons/MaterialIcon'
|
||||
import { Typography } from '../../Typography'
|
||||
|
||||
import SidePeekSEO from './SidePeekSEO'
|
||||
import SidePeekSEO from '../SidePeekSEO'
|
||||
|
||||
import styles from './sidePeekSelfControlled.module.css'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
interface SidePeekSEOProps {
|
||||
title: string
|
||||
shouldInert?: boolean
|
||||
}
|
||||
|
||||
// Sidepeeks generally have important content that should be indexed by search engines.
|
||||
@@ -7,10 +8,15 @@ interface SidePeekSEOProps {
|
||||
// This component is used to provide SEO information for the sidepeek content.
|
||||
export default function SidePeekSEO({
|
||||
title,
|
||||
shouldInert = false,
|
||||
children,
|
||||
}: React.PropsWithChildren<SidePeekSEOProps>) {
|
||||
return (
|
||||
<div className="sr-only">
|
||||
// Both inert and sr-only to ensure that the content is not focusable
|
||||
// or visible to screen readers but still available for SEO.
|
||||
// The other possible options, such as aria-hidden and the hidden attribute,
|
||||
// are less suitable for SEO purposes.
|
||||
<div className="sr-only" inert={shouldInert}>
|
||||
<h2>{title}</h2>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ interface SidePeekProps {
|
||||
openInRoot?: boolean
|
||||
handleClose?: (isOpen: boolean) => void
|
||||
closeLabel: string
|
||||
shouldInert?: boolean
|
||||
}
|
||||
|
||||
export default function SidePeek({
|
||||
@@ -30,6 +31,7 @@ export default function SidePeek({
|
||||
isOpen,
|
||||
openInRoot = false,
|
||||
closeLabel,
|
||||
shouldInert,
|
||||
}: React.PropsWithChildren<SidePeekProps>) {
|
||||
const rootDiv = useRef<HTMLDivElement>(null)
|
||||
const headerRef = useRef<HTMLElement>(null)
|
||||
@@ -116,7 +118,9 @@ export default function SidePeek({
|
||||
</ModalOverlay>
|
||||
</div>
|
||||
|
||||
<SidePeekSEO title={title}>{children}</SidePeekSEO>
|
||||
<SidePeekSEO title={title} shouldInert={shouldInert}>
|
||||
{children}
|
||||
</SidePeekSEO>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user