diff --git a/MIGRATION.md b/MIGRATION.md index 0805c3682b8b..2b6ba2137b9c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -627,6 +627,16 @@ The PopoverProvider component acts as a counterpoint to WithTooltip. When you wa PopoverProvider is based on react-aria. It must have a single child that acts as a trigger. This child must have a pressable role (can be clicked or pressed) and must be able to receive React refs. Wrap your trigger component in `forwardRef` if you notice placement issues for your popover. +##### Added: ariaLabel + +The `ariaLabel` prop was added in Storybook 10.3 to provide an accessible label for the popover dialog. This label is announced by screen readers when the popover opens. `ariaLabel` will become mandatory in Storybook 11. + +```tsx +}> + + +``` + #### WithTooltip Component API Changes The WithTooltip component has been reimplemented from the ground up, under the new name `TooltipProvider`. The new implementation will replace `WithTooltip` entirely in Storybook 11. Below is a summary of the changes between both APIs, which will take full effect in Storybook 11. diff --git a/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx b/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx index 1be3d2075744..66a723c592ad 100644 --- a/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx +++ b/code/addons/docs/src/blocks/components/ArgsTable/ArgValue.tsx @@ -203,6 +203,7 @@ const ArgSummary: FC = ({ value, initialExpandedArgs }) => { return ( { diff --git a/code/addons/docs/src/blocks/controls/Color.tsx b/code/addons/docs/src/blocks/controls/Color.tsx index 3c3610c21621..0758f7b8611c 100644 --- a/code/addons/docs/src/blocks/controls/Color.tsx +++ b/code/addons/docs/src/blocks/controls/Color.tsx @@ -393,6 +393,7 @@ export const ColorControl: FC = ({ placeholder="Choose color..." /> color && addPreset(color)} diff --git a/code/core/src/components/components/Popover/PopoverProvider.stories.tsx b/code/core/src/components/components/Popover/PopoverProvider.stories.tsx index c6a4f4f68cb5..0db176a23c44 100644 --- a/code/core/src/components/components/Popover/PopoverProvider.stories.tsx +++ b/code/core/src/components/components/Popover/PopoverProvider.stories.tsx @@ -28,6 +28,7 @@ const meta = preview.meta({ title: 'Overlay/PopoverProvider', component: PopoverProvider, args: { + ariaLabel: 'Sample popover', hasChrome: true, offset: 8, placement: 'top', diff --git a/code/core/src/components/components/Popover/PopoverProvider.tsx b/code/core/src/components/components/Popover/PopoverProvider.tsx index 7b953613c32a..d597c8d0f494 100644 --- a/code/core/src/components/components/Popover/PopoverProvider.tsx +++ b/code/core/src/components/components/Popover/PopoverProvider.tsx @@ -1,5 +1,7 @@ import type { DOMAttributes, ReactElement, ReactNode } from 'react'; -import React, { useCallback, useState } from 'react'; +import React, { cloneElement, useCallback, useState } from 'react'; + +import { deprecate } from 'storybook/internal/client-logger'; import { Pressable } from '@react-aria/interactions'; import { DialogTrigger } from 'react-aria-components/patched-dist/Dialog'; @@ -9,6 +11,12 @@ import { type PopperPlacement, convertToReactAriaPlacement } from '../shared/ove import { Popover } from './Popover'; export interface PopoverProviderProps { + /** + * An accessible label for the popover dialog, announced by screen readers. This prop will become + * mandatory in Storybook 11. Provide a concise description of the popover's purpose. + */ + ariaLabel?: string; + /** Whether to display the Popover in a prestyled container. True by default. */ hasChrome?: boolean; @@ -53,6 +61,7 @@ export interface PopoverProviderProps { } export const PopoverProvider = ({ + ariaLabel, placement: placementProp = 'bottom-start', hasChrome = true, hasCloseButton = false, @@ -66,6 +75,12 @@ export const PopoverProvider = ({ onVisibleChange, ...props }: PopoverProviderProps) => { + if (!ariaLabel) { + deprecate( + "The 'ariaLabel' prop on 'PopoverProvider' will become mandatory in Storybook 11. Provide a concise, accessible label describing the popover's purpose." + ); + } + // Map Popper.js placement to react-aria placement best we can. const placement = convertToReactAriaPlacement(placementProp); @@ -86,8 +101,22 @@ export const PopoverProvider = ({ onOpenChange={onOpenChange} {...props} > - {children} - + + { + /* React-aria does not inject aria-haspopup='dialog' to support legacy screen readers, so we do it ourselves. */ + cloneElement( + children, + // @ts-expect-error aria-haspopup is a valid ARIA attribute but cloneElement types are too strict + { 'aria-haspopup': 'dialog' } + ) + } + + ( height: '300px', }} > - + diff --git a/code/core/src/manager/components/preview/tools/share.tsx b/code/core/src/manager/components/preview/tools/share.tsx index caa46f7c78d4..63c688b0d4af 100644 --- a/code/core/src/manager/components/preview/tools/share.tsx +++ b/code/core/src/manager/components/preview/tools/share.tsx @@ -146,6 +146,7 @@ export const shareTool: Addon_BaseType = { {({ api, storyId, refId }) => storyId ? ( { { {loaded && ( ( diff --git a/code/core/src/manager/components/sidebar/ContextMenu.tsx b/code/core/src/manager/components/sidebar/ContextMenu.tsx index 55bd6b9f34da..93894cec3dd9 100644 --- a/code/core/src/manager/components/sidebar/ContextMenu.tsx +++ b/code/core/src/manager/components/sidebar/ContextMenu.tsx @@ -196,6 +196,7 @@ export const useContextMenu = (context: API_HashEntry, links: Link[], api: API) onMouseEnter: handlers.onMouseEnter, node: shouldRender ? ( = ({ menu, isHighlighted, onClick return ( } diff --git a/code/core/src/manager/components/sidebar/RefBlocks.tsx b/code/core/src/manager/components/sidebar/RefBlocks.tsx index e908249eb068..9ef2564ab2e4 100644 --- a/code/core/src/manager/components/sidebar/RefBlocks.tsx +++ b/code/core/src/manager/components/sidebar/RefBlocks.tsx @@ -127,6 +127,7 @@ export const ErrorBlock: FC<{ error: Error }> = ({ error }) => { Oh no! Something went wrong loading this Storybook.
( diff --git a/code/core/src/manager/components/sidebar/TagsFilter.tsx b/code/core/src/manager/components/sidebar/TagsFilter.tsx index 31d49b8c0e8f..6c8dd4667ba6 100644 --- a/code/core/src/manager/components/sidebar/TagsFilter.tsx +++ b/code/core/src/manager/components/sidebar/TagsFilter.tsx @@ -212,6 +212,7 @@ export const TagsFilter = ({ api, indexJson, tagPresets }: TagsFilterProps) => { return ( { key="tags" ariaLabel="Tag filters" ariaDescription="Filter the items shown in a sidebar based on the tags applied to them." - aria-haspopup="dialog" variant="ghost" padding="small" isHighlighted={tagsActive} diff --git a/code/core/src/manager/container/Menu.stories.tsx b/code/core/src/manager/container/Menu.stories.tsx index 5c81f339a7c9..0368fbf4d89a 100644 --- a/code/core/src/manager/container/Menu.stories.tsx +++ b/code/core/src/manager/container/Menu.stories.tsx @@ -21,7 +21,13 @@ export default { height: '300px', }} > - +