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',
}}
>
-
+