diff --git a/packages/@react-aria/gridlist/src/useGridListItem.ts b/packages/@react-aria/gridlist/src/useGridListItem.ts index 2ecfc4f8125..f4c32cdfa31 100644 --- a/packages/@react-aria/gridlist/src/useGridListItem.ts +++ b/packages/@react-aria/gridlist/src/useGridListItem.ts @@ -283,10 +283,10 @@ export function useGridListItem(props: AriaGridListItemOptions, state: ListSt onKeyDown, onFocus, // 'aria-label': [(node.textValue || undefined), rowAnnouncement].filter(Boolean).join(', '), - 'aria-label': node.textValue || undefined, + 'aria-label': node['aria-label'] || node.textValue || undefined, 'aria-selected': state.selectionManager.canSelectItem(node.key) ? state.selectionManager.isSelected(node.key) : undefined, 'aria-disabled': state.selectionManager.isDisabled(node.key) || undefined, - 'aria-labelledby': descriptionId && node.textValue ? `${getRowId(state, node.key)} ${descriptionId}` : undefined, + 'aria-labelledby': descriptionId && (node['aria-label'] || node.textValue) ? `${getRowId(state, node.key)} ${descriptionId}` : undefined, id: getRowId(state, node.key) }); diff --git a/packages/@react-spectrum/s2/chromatic/Breadcrumbs.stories.tsx b/packages/@react-spectrum/s2/chromatic/Breadcrumbs.stories.tsx index 76996eea018..7fa983a51b1 100644 --- a/packages/@react-spectrum/s2/chromatic/Breadcrumbs.stories.tsx +++ b/packages/@react-spectrum/s2/chromatic/Breadcrumbs.stories.tsx @@ -12,7 +12,7 @@ import {Breadcrumb, Breadcrumbs} from '../src'; import {generatePowerset} from '@react-spectrum/story-utils'; -import {Many} from '../stories/Breadcrumbs.stories'; +import {type IMany, Many} from '../stories/Breadcrumbs.stories'; import type {Meta, StoryObj} from '@storybook/react'; import {ReactNode} from 'react'; import {shortName} from './utils'; @@ -27,7 +27,7 @@ const meta: Meta = { export default meta; -export const Dynamic: StoryObj = { +export const Dynamic: StoryObj = { ...Many, parameters: { // TODO: move these options back to meta above once we get strings for ar-AE. This is just to prevent the RTL story's config from actually applying diff --git a/packages/@react-spectrum/s2/package.json b/packages/@react-spectrum/s2/package.json index cb644b964bc..4d996a0c34c 100644 --- a/packages/@react-spectrum/s2/package.json +++ b/packages/@react-spectrum/s2/package.json @@ -164,6 +164,7 @@ "@react-stately/utils": "^3.10.8", "@react-types/dialog": "^3.5.22", "@react-types/grid": "^3.3.6", + "@react-types/overlays": "^3.9.2", "@react-types/provider": "^3.8.13", "@react-types/shared": "^3.32.1", "@react-types/table": "^3.13.4", diff --git a/packages/@react-spectrum/s2/src/ActionBar.tsx b/packages/@react-spectrum/s2/src/ActionBar.tsx index e664e66f636..993d3632e90 100644 --- a/packages/@react-spectrum/s2/src/ActionBar.tsx +++ b/packages/@react-spectrum/s2/src/ActionBar.tsx @@ -15,11 +15,12 @@ import {announce} from '@react-aria/live-announcer'; import {CloseButton} from './CloseButton'; import {ContextValue, SlotProps} from 'react-aria-components'; import {createContext, ForwardedRef, forwardRef, ReactElement, ReactNode, RefObject, useCallback, useEffect, useMemo, useRef, useState} from 'react'; -import {DOMRef, DOMRefValue, Key} from '@react-types/shared'; +import {DOMProps, DOMRef, DOMRefValue, Key} from '@react-types/shared'; import {FocusScope, useKeyboard} from 'react-aria'; // @ts-ignore import intlMessages from '../intl/*.json'; import {lightDark, style} from '../style' with {type: 'macro'}; +import {StyleProps} from './style-utils' with { type: 'macro' }; import {useControlledState} from '@react-stately/utils'; import {useDOMRef} from '@react-spectrum/utils'; import {useEnterAnimation, useExitAnimation, useObjectRef, useResizeObserver} from '@react-aria/utils'; @@ -74,7 +75,7 @@ const actionBarStyles = style({ } }); -export interface ActionBarProps extends SlotProps { +export interface ActionBarProps extends SlotProps, StyleProps, DOMProps { /** A list of ActionButtons to display. */ children: ReactNode, /** Whether the ActionBar should be displayed with a emphasized style. */ @@ -106,7 +107,8 @@ export const ActionBar = forwardRef(function ActionBar(props: ActionBarProps, re }); const ActionBarInner = forwardRef(function ActionBarInner(props: ActionBarProps & {isExiting: boolean}, ref: ForwardedRef) { - let {isEmphasized, selectedItemCount = 0, children, onClearSelection, isExiting} = props; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + let {isEmphasized, selectedItemCount = 0, children, onClearSelection, isExiting, slot, ...otherProps} = props; let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/s2'); // Store the last count greater than zero so that we can retain it while rendering the fade-out animation. @@ -158,6 +160,7 @@ const ActionBarInner = forwardRef(function ActionBarInner(props: ActionBarProps
diff --git a/packages/@react-spectrum/s2/src/ActionMenu.tsx b/packages/@react-spectrum/s2/src/ActionMenu.tsx index ed891a42d70..c533d2c1702 100644 --- a/packages/@react-spectrum/s2/src/ActionMenu.tsx +++ b/packages/@react-spectrum/s2/src/ActionMenu.tsx @@ -29,6 +29,11 @@ export interface ActionMenuProps extends Pick, 'children' | 'items' | 'disabledKeys' | 'onAction'>, Pick, StyleProps, DOMProps, AriaLabelingProps { + /** + * The size of the Menu. + * + * @default 'M' + */ menuSize?: 'S' | 'M' | 'L' | 'XL' } diff --git a/packages/@react-spectrum/s2/src/AlertDialog.tsx b/packages/@react-spectrum/s2/src/AlertDialog.tsx index dfa156cf042..7395b3169be 100644 --- a/packages/@react-spectrum/s2/src/AlertDialog.tsx +++ b/packages/@react-spectrum/s2/src/AlertDialog.tsx @@ -29,7 +29,10 @@ import {UnsafeStyles} from './style-utils' with {type: 'macro'}; import {useLocalizedStringFormatter} from '@react-aria/i18n'; export interface AlertDialogProps extends DOMProps, UnsafeStyles { - /** The [visual style](https://spectrum.adobe.com/page/alert-dialog/#Options) of the AlertDialog. */ + /** + * The [visual style](https://spectrum.adobe.com/page/alert-dialog/#Options) of the AlertDialog. + * @default 'confirmation' + */ variant?: 'confirmation' | 'information' | 'destructive' | 'error' | 'warning', /** The title of the AlertDialog. */ title: string, diff --git a/packages/@react-spectrum/s2/src/Breadcrumbs.tsx b/packages/@react-spectrum/s2/src/Breadcrumbs.tsx index a8369b9e757..689055cfae9 100644 --- a/packages/@react-spectrum/s2/src/Breadcrumbs.tsx +++ b/packages/@react-spectrum/s2/src/Breadcrumbs.tsx @@ -62,9 +62,9 @@ interface BreadcrumbsStyleProps { // TODO: showRoot?: boolean, } -export interface BreadcrumbsProps extends Omit, 'children' | 'items' | 'style' | 'className' | keyof GlobalDOMAttributes>, BreadcrumbsStyleProps, StyleProps { +export interface BreadcrumbsProps extends Omit, 'children' | 'style' | 'className' | keyof GlobalDOMAttributes>, BreadcrumbsStyleProps, StyleProps { /** The children of the Breadcrumbs. */ - children: ReactNode + children: ReactNode | ((item: T) => ReactNode) } export const BreadcrumbsContext = createContext>, DOMRefValue>>(null); diff --git a/packages/@react-spectrum/s2/src/ContextualHelp.tsx b/packages/@react-spectrum/s2/src/ContextualHelp.tsx index 0590b534438..7cd5a31f5e0 100644 --- a/packages/@react-spectrum/s2/src/ContextualHelp.tsx +++ b/packages/@react-spectrum/s2/src/ContextualHelp.tsx @@ -11,6 +11,7 @@ import InfoIcon from '../s2wf-icons/S2_Icon_InfoCircle_20_N.svg'; // @ts-ignore import intlMessages from '../intl/*.json'; import {mergeStyles} from '../style/runtime'; +import {Placement} from '@react-types/overlays'; import {Popover, PopoverDialogProps} from './Popover'; import {space, style} from '../style' with {type: 'macro'}; import {StyleProps} from './style-utils' with { type: 'macro' }; @@ -29,6 +30,11 @@ export interface ContextualHelpProps extends Pick, Pick, ContextualHelpStyleProps, StyleProps, DOMProps, AriaLabelingProps { + /** + * The placement of the popover with respect to the action button. + * @default 'bottom start' + */ + placement?: Placement, /** Contents of the Contextual Help popover. */ children: ReactNode, /** @@ -58,11 +64,10 @@ export const ContextualHelp = forwardRef(function ContextualHelp(props: Contextu let { children, defaultOpen, - // containerPadding = 24, // See popover() above. Issue noted in Popover.tsx. + containerPadding = 8, size = 'XS', crossOffset, isOpen, - offset = 8, onOpenChange, placement = 'bottom start', shouldFlip, @@ -101,8 +106,8 @@ export const ContextualHelp = forwardRef(function ContextualHelp(props: Contextu padding="none" placement={placement} shouldFlip={shouldFlip} - // not working => containerPadding={containerPadding} - offset={offset} + containerPadding={containerPadding} + offset={8} crossOffset={crossOffset} hideArrow>
extends Omit, 'children' | 'className' | 'style' | keyof GlobalDOMAttributes>, Pick, 'createCalendar' | 'pageBehavior' | 'firstDayOfWeek' | 'isDateUnavailable'>, + Pick, StyleProps, SpectrumLabelableProps, HelpTextProps { @@ -203,7 +205,7 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function - + @@ -238,9 +240,10 @@ export const DatePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(function ); }); -export function CalendarPopover(props: PropsWithChildren): ReactElement { +export function CalendarPopover(props: Omit & {children: ReactNode}): ReactElement { return (
extends Omit, 'children' | 'className' | 'style' | keyof GlobalDOMAttributes>, Pick, 'createCalendar' | 'pageBehavior' | 'firstDayOfWeek' | 'isDateUnavailable'>, + Pick, StyleProps, SpectrumLabelableProps, HelpTextProps { @@ -143,7 +145,7 @@ export const DateRangePicker = /*#__PURE__*/ (forwardRef as forwardRefType)(func
- + diff --git a/packages/@react-spectrum/s2/src/Picker.tsx b/packages/@react-spectrum/s2/src/Picker.tsx index 6140ae33e4b..71df9588f8f 100644 --- a/packages/@react-spectrum/s2/src/Picker.tsx +++ b/packages/@react-spectrum/s2/src/Picker.tsx @@ -675,7 +675,7 @@ function DefaultProvider({context, value, children}: {context: React.Context{children}; } -export interface PickerSectionProps extends Omit, keyof GlobalDOMAttributes> {} +export interface PickerSectionProps extends Omit, 'style' | 'className' | keyof GlobalDOMAttributes>, StyleProps {} export function PickerSection(props: PickerSectionProps): ReactNode { let {size} = useContext(InternalPickerContext); return ( diff --git a/packages/@react-spectrum/s2/src/Provider.tsx b/packages/@react-spectrum/s2/src/Provider.tsx index b1c34d198c4..bfbae3cc936 100644 --- a/packages/@react-spectrum/s2/src/Provider.tsx +++ b/packages/@react-spectrum/s2/src/Provider.tsx @@ -13,6 +13,8 @@ import type {ColorScheme, Router} from '@react-types/provider'; import {colorScheme, UnsafeStyles} from './style-utils' with {type: 'macro'}; import {createContext, JSX, ReactNode, useContext} from 'react'; +import {DOMProps} from '@react-types/shared'; +import {filterDOMProps} from '@react-aria/utils'; import {Fonts} from './Fonts'; import {generateDefaultColorSchemeStyles} from './page.macro' with {type: 'macro'}; import {I18nProvider, RouterProvider, useLocale} from 'react-aria-components'; @@ -20,7 +22,7 @@ import {mergeStyles} from '../style/runtime'; import {style} from '../style' with {type: 'macro'}; import {StyleString} from '../style/types'; -export interface ProviderProps extends UnsafeStyles { +export interface ProviderProps extends UnsafeStyles, DOMProps { /** The content of the Provider. */ children: ReactNode, /** @@ -113,6 +115,7 @@ function ProviderInner(props: ProviderProps) { let {locale, direction} = useLocale(); return ( ({ */ export const StatusLight = /*#__PURE__*/ forwardRef(function StatusLight(props: StatusLightProps, ref: DOMRef) { [props, ref] = useSpectrumContextProps(props, ref, StatusLightContext); - let {children, size = 'M', variant, role, UNSAFE_className = '', UNSAFE_style, styles} = props; + let {children, size = 'M', variant = 'neutral', role, UNSAFE_className = '', UNSAFE_style, styles} = props; let domRef = useDOMRef(ref); let isSkeleton = useIsSkeleton(); diff --git a/packages/@react-spectrum/s2/src/TableView.tsx b/packages/@react-spectrum/s2/src/TableView.tsx index cf47ed4d6cf..05dd53c7ee4 100644 --- a/packages/@react-spectrum/s2/src/TableView.tsx +++ b/packages/@react-spectrum/s2/src/TableView.tsx @@ -58,7 +58,7 @@ import Chevron from '../ui-icons/Chevron'; import Close from '../s2wf-icons/S2_Icon_Close_20_N.svg'; import {ColumnSize} from '@react-types/table'; import {CustomDialog, DialogContainer} from '..'; -import {DOMRef, DOMRefValue, forwardRefType, GlobalDOMAttributes, LoadingState, Node} from '@react-types/shared'; +import {DOMProps, DOMRef, DOMRefValue, forwardRefType, GlobalDOMAttributes, LoadingState, Node} from '@react-types/shared'; import {getActiveElement, getOwnerDocument, useLayoutEffect, useObjectRef} from '@react-aria/utils'; import {GridNode} from '@react-types/grid'; import {IconContext} from './Icon'; @@ -120,7 +120,7 @@ interface S2TableProps { } // TODO: Note that loadMore and loadingState are now on the Table instead of on the TableBody -export interface TableViewProps extends Omit, UnsafeStyles, S2TableProps { +export interface TableViewProps extends Omit, DOMProps, UnsafeStyles, S2TableProps { /** Spectrum-defined styles, returned by the `style()` macro. */ styles?: StylesPropWithHeight } diff --git a/packages/@react-spectrum/s2/src/TagGroup.tsx b/packages/@react-spectrum/s2/src/TagGroup.tsx index cc87e336ce2..7b039822c43 100644 --- a/packages/@react-spectrum/s2/src/TagGroup.tsx +++ b/packages/@react-spectrum/s2/src/TagGroup.tsx @@ -35,7 +35,7 @@ import {ClearButton} from './ClearButton'; import {Collection, CollectionBuilder} from '@react-aria/collections'; import {control, field, getAllowedOverrides, StyleProps} from './style-utils' with {type: 'macro'}; import {createContext, forwardRef, ReactNode, useContext, useEffect, useMemo, useRef, useState} from 'react'; -import {DOMRef, DOMRefValue, GlobalDOMAttributes, HelpTextProps, Node, SpectrumLabelableProps} from '@react-types/shared'; +import {DOMRef, DOMRefValue, GlobalDOMAttributes, HelpTextProps, LabelableProps, Node, SpectrumLabelableProps} from '@react-types/shared'; import {FieldLabel, helpTextStyles} from './Field'; import {flushSync} from 'react-dom'; import {FormContext, useFormProps} from './Form'; @@ -52,7 +52,7 @@ import {useLocalizedStringFormatter} from '@react-aria/i18n'; import {useSpectrumContextProps} from './useSpectrumContextProps'; // Get types from RSP and extend those? -export interface TagProps extends Omit { +export interface TagProps extends Omit, LabelableProps { /** The children of the tag. */ children: ReactNode } diff --git a/packages/@react-spectrum/s2/src/ToggleButtonGroup.tsx b/packages/@react-spectrum/s2/src/ToggleButtonGroup.tsx index 79a972a40da..022f0bc0119 100644 --- a/packages/@react-spectrum/s2/src/ToggleButtonGroup.tsx +++ b/packages/@react-spectrum/s2/src/ToggleButtonGroup.tsx @@ -13,10 +13,10 @@ import {ActionButtonGroupProps, actionGroupStyle} from './ActionButtonGroup'; import {ContextValue, ToggleButtonGroup as RACToggleButtonGroup, ToggleButtonGroupProps as RACToggleButtonGroupProps} from 'react-aria-components'; import {createContext, ForwardedRef, forwardRef} from 'react'; -import {GlobalDOMAttributes} from '@react-types/shared'; +import {DOMProps, GlobalDOMAttributes} from '@react-types/shared'; import {useSpectrumContextProps} from './useSpectrumContextProps'; -export interface ToggleButtonGroupProps extends ActionButtonGroupProps, Omit { +export interface ToggleButtonGroupProps extends ActionButtonGroupProps, Omit, DOMProps { /** Whether the button should be displayed with an [emphasized style](https://spectrum.adobe.com/page/action-button/#Emphasis). */ isEmphasized?: boolean } diff --git a/packages/@react-spectrum/s2/src/Tooltip.tsx b/packages/@react-spectrum/s2/src/Tooltip.tsx index abeaa5c0352..ea3ea7f3b62 100644 --- a/packages/@react-spectrum/s2/src/Tooltip.tsx +++ b/packages/@react-spectrum/s2/src/Tooltip.tsx @@ -23,11 +23,11 @@ import {centerPadding, colorScheme, UnsafeStyles} from './style-utils' with {typ import {ColorScheme} from '@react-types/provider'; import {ColorSchemeContext} from './Provider'; import {createContext, forwardRef, MutableRefObject, ReactNode, useCallback, useContext, useState} from 'react'; -import {DOMRef, GlobalDOMAttributes} from '@react-types/shared'; +import {DOMProps, DOMRef, GlobalDOMAttributes} from '@react-types/shared'; import {style} from '../style' with {type: 'macro'}; import {useDOMRef} from '@react-spectrum/utils'; -export interface TooltipTriggerProps extends Omit, Pick { +export interface TooltipTriggerProps extends Omit, Pick { /** The content of the tooltip. */ children: ReactNode, /** @@ -38,7 +38,7 @@ export interface TooltipTriggerProps extends Omit, UnsafeStyles { +export interface TooltipProps extends Omit, DOMProps, UnsafeStyles { /** The content of the tooltip. */ children: ReactNode } @@ -139,7 +139,6 @@ export const Tooltip = forwardRef(function Tooltip(props: TooltipProps, ref: DOM let { containerPadding, crossOffset, - offset, placement = 'top', shouldFlip } = useContext(InternalTooltipTriggerContext); @@ -166,7 +165,7 @@ export const Tooltip = forwardRef(function Tooltip(props: TooltipProps, ref: DOM arrowBoundaryOffset={borderRadius} containerPadding={containerPadding} crossOffset={crossOffset} - offset={offset} + offset={4} placement={placement} shouldFlip={shouldFlip} ref={tooltipRef} @@ -195,7 +194,6 @@ export function TooltipTrigger(props: TooltipTriggerProps): ReactNode { let { containerPadding, crossOffset, - offset, placement, shouldFlip, ...triggerProps @@ -207,7 +205,6 @@ export function TooltipTrigger(props: TooltipTriggerProps): ReactNode { value={{ containerPadding: containerPadding, crossOffset: crossOffset, - offset: offset, placement: placement, shouldFlip: shouldFlip }}> diff --git a/packages/@react-spectrum/s2/stories/Breadcrumbs.stories.tsx b/packages/@react-spectrum/s2/stories/Breadcrumbs.stories.tsx index ab5110309d2..0a6a6affc8f 100644 --- a/packages/@react-spectrum/s2/stories/Breadcrumbs.stories.tsx +++ b/packages/@react-spectrum/s2/stories/Breadcrumbs.stories.tsx @@ -11,7 +11,7 @@ */ import {action} from '@storybook/addon-actions'; -import {Breadcrumb, Breadcrumbs} from '../src'; +import {Breadcrumb, Breadcrumbs, BreadcrumbsProps} from '../src'; import type {Meta, StoryObj} from '@storybook/react'; const meta: Meta = { @@ -57,24 +57,31 @@ export const Example: Story = { ) }; -let items = [ +interface Item { + id: string, + name: string +} +let items: Item[] = [ {id: 'home', name: 'Home'}, {id: 'react-aria', name: 'React Aria'}, {id: 'breadcrumbs', name: 'Breadcrumbs'} ]; -export const WithActions: Story = { - render: (args: any) => ( - - {item => ( - - {item.name} - - )} - - ) + +const BreadcrumbsExampleDynamic = (args: BreadcrumbsProps) => ( + + {item => ( + + {item.name} + + )} + +); + +export const WithActions: StoryObj = { + render: BreadcrumbsExampleDynamic }; -let manyItems = [ +let manyItems: Item[] = [ {id: 'Folder 1', name: 'The quick brown fox jumps over'}, {id: 'Folder 2', name: 'My Documents'}, {id: 'Folder 3', name: 'Kangaroos jump high'}, @@ -83,9 +90,9 @@ let manyItems = [ {id: 'Folder 6', name: 'Wattle trees'}, {id: 'Folder 7', name: 'April 7'} ]; - -export const Many: Story = { - render: (args: any) => ( +export type IMany = typeof BreadcrumbsExampleDynamic; +export const Many: StoryObj = { + render: (args: BreadcrumbsProps) => (
{item => ( @@ -98,7 +105,10 @@ export const Many: Story = { ) }; -let manyItemsWithLinks = [ +interface ItemWithLink extends Item { + href: string +} +let manyItemsWithLinks: ItemWithLink[] = [ {id: 'Folder 1', name: 'The quick brown fox jumps over', href: '/folder1'}, {id: 'Folder 2', name: 'My Documents', href: '/folder2'}, {id: 'Folder 3', name: 'Kangaroos jump high', href: '/folder3'}, @@ -108,16 +118,16 @@ let manyItemsWithLinks = [ {id: 'Folder 7', name: 'April 7', href: '/folder7'} ]; -export const ManyWithLinks: Story = { - render: (args: any) => ( -
- - {item => ( - - {item.name} - - )} - -
- ) +const BreadcrumbsExampleDynamicWithLinks = (args: BreadcrumbsProps) => ( + + {item => ( + + {item.name} + + )} + +); + +export const ManyWithLinks: StoryObj = { + render: BreadcrumbsExampleDynamicWithLinks }; diff --git a/packages/@react-spectrum/s2/test/TagGroup.test.tsx b/packages/@react-spectrum/s2/test/TagGroup.test.tsx index 55381107a14..9deff9e855d 100644 --- a/packages/@react-spectrum/s2/test/TagGroup.test.tsx +++ b/packages/@react-spectrum/s2/test/TagGroup.test.tsx @@ -15,6 +15,18 @@ import React from 'react'; import {Tag, TagGroup} from '../src'; import userEvent from '@testing-library/user-event'; + +let TestTagGroup = ({tagGroupProps, itemProps}) => ( + + Cat + Dog + Kangaroo + +); + +let renderTagGroup = (tagGroupProps = {}, itemProps = {}) => render(); + + describe('TagGroup', () => { let user; beforeAll(() => { @@ -52,4 +64,12 @@ describe('TagGroup', () => { expect(onRemove).toHaveBeenCalledTimes(1); expect(onRemove).toHaveBeenCalledWith(new Set(['chocolate'])); }); + + it('should aria label on tags', () => { + let {getAllByRole} = renderTagGroup({label: 'TagGroup label'}, {'aria-label': 'Test'}); + + for (let row of getAllByRole('row')) { + expect(row).toHaveAttribute('aria-label', 'Test'); + } + }); }); diff --git a/packages/@react-spectrum/tree/test/TreeView.test.tsx b/packages/@react-spectrum/tree/test/TreeView.test.tsx index e7658edd446..f87c26f13a3 100644 --- a/packages/@react-spectrum/tree/test/TreeView.test.tsx +++ b/packages/@react-spectrum/tree/test/TreeView.test.tsx @@ -518,7 +518,7 @@ describe('Tree', () => { let rows = getAllByRole('row'); expect(rows).toHaveLength(1); - expect(rows[0]).toHaveAttribute('aria-label', 'Test'); + expect(rows[0]).toHaveAttribute('aria-label', 'test row'); }); describe('general interactions', () => { diff --git a/packages/@react-types/combobox/src/index.d.ts b/packages/@react-types/combobox/src/index.d.ts index 5b96371b82e..3558c274dc8 100644 --- a/packages/@react-types/combobox/src/index.d.ts +++ b/packages/@react-types/combobox/src/index.d.ts @@ -84,7 +84,7 @@ export interface SpectrumComboBoxProps extends SpectrumTextInputBase, Omit, SpectrumFieldValidation, InputDOMProps, StyleProps, SpectrumLabelableProps { /** Whether the numberfield should be displayed with a quiet style. */ isQuiet?: boolean, - /** Whether to hide the increment and decrement buttons. */ + /** + * Whether to hide the increment and decrement buttons. + * @default false + */ hideStepper?: boolean } diff --git a/packages/@react-types/tabs/src/index.d.ts b/packages/@react-types/tabs/src/index.d.ts index a24d94a8338..9a20acc7ef9 100644 --- a/packages/@react-types/tabs/src/index.d.ts +++ b/packages/@react-types/tabs/src/index.d.ts @@ -60,7 +60,7 @@ export interface AriaTabPanelProps extends Omit, AriaLabelingPro id?: Key } -export interface SpectrumTabsProps extends AriaTabListBase, Omit, DOMProps, StyleProps { +export interface SpectrumTabsProps extends AriaTabListBase, Omit, DOMProps, StyleProps { /** The children of the `` element. Should include `` and `` elements. */ children: ReactNode, /** The item objects for each tab, for dynamic collections. */ diff --git a/packages/dev/s2-docs/pages/s2/ToggleButtonGroup.mdx b/packages/dev/s2-docs/pages/s2/ToggleButtonGroup.mdx index dd910602b6e..e29832d395e 100644 --- a/packages/dev/s2-docs/pages/s2/ToggleButtonGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/ToggleButtonGroup.mdx @@ -11,7 +11,7 @@ export const description = 'Allows a user to toggle multiple options, with singl {docs.exports.ToggleButtonGroup.description} -```tsx render docs={docs.exports.ToggleButtonGroup} links={docs.links} props={['selectionMode', 'orientation', 'size', 'density', 'staticColor', 'isQuiet', 'isJustified', 'isDisabled']} type="s2" +```tsx render docs={docs.exports.ToggleButtonGroup} links={docs.links} props={['selectionMode', 'orientation', 'size', 'density', 'staticColor', 'isQuiet', 'isJustified', 'isDisabled', 'isEmphasized']} type="s2" "use client"; import {ToggleButtonGroup, ToggleButton, Text} from '@react-spectrum/s2'; import TextBold from '@react-spectrum/s2/icons/TextBold'; diff --git a/packages/dev/s2-docs/pages/s2/Tooltip.mdx b/packages/dev/s2-docs/pages/s2/Tooltip.mdx index ec1955f281b..26e3f7d83f3 100644 --- a/packages/dev/s2-docs/pages/s2/Tooltip.mdx +++ b/packages/dev/s2-docs/pages/s2/Tooltip.mdx @@ -11,7 +11,7 @@ export const description = 'Displays a description of an element on hover or foc {docs.exports.Tooltip.description} -```tsx render docs={docs.exports.TooltipTrigger} links={docs.links} props={['placement', 'offset', 'crossOffset', 'shouldFlip']} type="s2" +```tsx render docs={docs.exports.TooltipTrigger} links={docs.links} props={['placement', 'crossOffset', 'shouldFlip']} type="s2" "use client"; import {Tooltip, TooltipTrigger, ActionButton} from '@react-spectrum/s2'; import Edit from '@react-spectrum/s2/icons/Edit'; diff --git a/packages/react-aria-components/src/Breadcrumbs.tsx b/packages/react-aria-components/src/Breadcrumbs.tsx index 21a0929ace6..9149c9c1ab7 100644 --- a/packages/react-aria-components/src/Breadcrumbs.tsx +++ b/packages/react-aria-components/src/Breadcrumbs.tsx @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ import {AriaBreadcrumbsProps, useBreadcrumbs} from 'react-aria'; +import {AriaLabelingProps, forwardRefType, GlobalDOMAttributes, Key} from '@react-types/shared'; import { ClassNameOrFunction, ContextValue, @@ -23,12 +24,11 @@ import { import {Collection, CollectionBuilder, CollectionNode, createLeafComponent} from '@react-aria/collections'; import {CollectionProps, CollectionRendererContext} from './Collection'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {forwardRefType, GlobalDOMAttributes, Key} from '@react-types/shared'; import {LinkContext} from './Link'; import {Node} from 'react-stately'; import React, {createContext, ForwardedRef, forwardRef, useContext} from 'react'; -export interface BreadcrumbsProps extends Omit, 'disabledKeys'>, AriaBreadcrumbsProps, StyleProps, SlotProps, GlobalDOMAttributes { +export interface BreadcrumbsProps extends Omit, 'disabledKeys'>, AriaBreadcrumbsProps, StyleProps, SlotProps, AriaLabelingProps, GlobalDOMAttributes { /** * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. * @default 'react-aria-Breadcrumbs' @@ -49,7 +49,7 @@ export const Breadcrumbs = /*#__PURE__*/ (forwardRef as forwardRefType)(function [props, ref] = useContextProps(props, ref, BreadcrumbsContext); let {CollectionRoot} = useContext(CollectionRendererContext); let {navProps} = useBreadcrumbs(props); - let DOMProps = filterDOMProps(props, {global: true}); + let DOMProps = filterDOMProps(props, {global: true, labelable: true}); return ( }> @@ -82,7 +82,7 @@ export interface BreadcrumbRenderProps { isDisabled: boolean } -export interface BreadcrumbProps extends RenderProps, GlobalDOMAttributes { +export interface BreadcrumbProps extends RenderProps, AriaLabelingProps, GlobalDOMAttributes { /** * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. * @default 'react-aria-Breadcrumb' @@ -116,7 +116,7 @@ export const Breadcrumb = /*#__PURE__*/ createLeafComponent(BreadcrumbNode, func defaultClassName: 'react-aria-Breadcrumb' }); - let DOMProps = filterDOMProps(props as any, {global: true}); + let DOMProps = filterDOMProps(props as any, {global: true, labelable: true}); delete DOMProps.id; return ( diff --git a/packages/react-aria-components/src/Disclosure.tsx b/packages/react-aria-components/src/Disclosure.tsx index c4bb05d10aa..6bfebd7db32 100644 --- a/packages/react-aria-components/src/Disclosure.tsx +++ b/packages/react-aria-components/src/Disclosure.tsx @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import {AriaDisclosureProps, useDisclosure, useFocusRing} from 'react-aria'; +import {AriaDisclosureProps, LabelAriaProps, useDisclosure, useFocusRing} from 'react-aria'; import {ButtonContext} from './Button'; import { ClassNameOrFunction, @@ -206,7 +206,7 @@ export interface DisclosurePanelRenderProps { isFocusVisibleWithin: boolean } -export interface DisclosurePanelProps extends RenderProps, DOMProps, GlobalDOMAttributes { +export interface DisclosurePanelProps extends RenderProps, DOMProps, LabelAriaProps, GlobalDOMAttributes { /** * The CSS [className](https://developer.mozilla.org/en-US/docs/Web/API/Element/className) for the element. A function may be provided to compute the class based on component state. * @default 'react-aria-DisclosurePanel' @@ -240,7 +240,7 @@ export const DisclosurePanel = /*#__PURE__*/ (forwardRef as forwardRefType)(func isFocusVisibleWithin } }); - let DOMProps = filterDOMProps(props, {global: true}); + let DOMProps = filterDOMProps(props, {global: true, labelable: true}); return (
{ }); it('should support DOM props', () => { - let {getByRole, getAllByRole} = renderBreadcrumbs({'data-foo': 'bar'}, {'data-bar': 'foo'}); + let {getByRole, getAllByRole} = renderBreadcrumbs({'data-foo': 'bar', 'aria-label': 'test group'}, {'data-bar': 'foo', 'aria-label': 'test item'}); let breadcrumbs = getByRole('list'); expect(breadcrumbs).toHaveAttribute('data-foo', 'bar'); + expect(breadcrumbs).toHaveAttribute('aria-label', 'test group'); for (let item of getAllByRole('listitem')) { expect(item).toHaveAttribute('data-bar', 'foo'); + expect(item).toHaveAttribute('aria-label', 'test item'); } }); diff --git a/packages/react-aria-components/test/Disclosure.test.js b/packages/react-aria-components/test/Disclosure.test.js index 1ebfb13e91e..00411840f50 100644 --- a/packages/react-aria-components/test/Disclosure.test.js +++ b/packages/react-aria-components/test/Disclosure.test.js @@ -77,6 +77,23 @@ describe('Disclosure', () => { expect(disclosure).toHaveAttribute('data-foo', 'bar'); }); + it('should support aria label', () => { + const {container, getByRole} = render( + + + + + +

Content

+
+
+ ); + const heading = getByRole('heading'); + expect(heading).toHaveAttribute('aria-label', 'Test disclosure heading'); + const panel = container.querySelector('.react-aria-DisclosurePanel'); + expect(panel).toHaveAttribute('aria-label', 'Test disclosure panel'); + }); + it('should support disabled state', () => { const {getByTestId, getByRole, queryByText} = render( diff --git a/packages/react-aria-components/test/TagGroup.test.js b/packages/react-aria-components/test/TagGroup.test.js index 2dad220810a..adc4f07135b 100644 --- a/packages/react-aria-components/test/TagGroup.test.js +++ b/packages/react-aria-components/test/TagGroup.test.js @@ -527,9 +527,9 @@ describe('TagGroup', () => { let {getAllByRole} = renderTagGroup({selectionMode: 'single', onSelectionChange}); let items = getAllByRole('row'); - await user.pointer({target: items[0], keys: '[MouseLeft>]'}); + await user.pointer({target: items[0], keys: '[MouseLeft>]'}); expect(onSelectionChange).toBeCalledTimes(1); - + await user.pointer({target: items[0], keys: '[/MouseLeft]'}); expect(onSelectionChange).toBeCalledTimes(1); }); @@ -539,9 +539,9 @@ describe('TagGroup', () => { let {getAllByRole} = renderTagGroup({selectionMode: 'single', onSelectionChange, shouldSelectOnPressUp: false}); let items = getAllByRole('row'); - await user.pointer({target: items[0], keys: '[MouseLeft>]'}); + await user.pointer({target: items[0], keys: '[MouseLeft>]'}); expect(onSelectionChange).toBeCalledTimes(1); - + await user.pointer({target: items[0], keys: '[/MouseLeft]'}); expect(onSelectionChange).toBeCalledTimes(1); }); @@ -551,9 +551,9 @@ describe('TagGroup', () => { let {getAllByRole} = renderTagGroup({selectionMode: 'single', onSelectionChange, shouldSelectOnPressUp: true}); let items = getAllByRole('row'); - await user.pointer({target: items[0], keys: '[MouseLeft>]'}); + await user.pointer({target: items[0], keys: '[MouseLeft>]'}); expect(onSelectionChange).toBeCalledTimes(0); - + await user.pointer({target: items[0], keys: '[/MouseLeft]'}); expect(onSelectionChange).toBeCalledTimes(1); }); @@ -572,7 +572,7 @@ describe('TagGroup', () => { let {getByRole} = renderTagGroup({selectionMode: 'multiple'}, {}, {onPressStart, onPressEnd, onPress, onClick}); let tester = testUtilUser.createTester('GridList', {root: getByRole('grid')}); await tester.triggerRowAction({row: 1, interactionType}); - + expect(onPressStart).toHaveBeenCalledTimes(1); expect(onPressEnd).toHaveBeenCalledTimes(1); expect(onPress).toHaveBeenCalledTimes(1); diff --git a/starters/tailwind/src/SearchField.tsx b/starters/tailwind/src/SearchField.tsx index 12e1d095eca..9c155fc889a 100644 --- a/starters/tailwind/src/SearchField.tsx +++ b/starters/tailwind/src/SearchField.tsx @@ -14,7 +14,7 @@ export interface SearchFieldProps extends AriaSearchFieldProps { label?: string; description?: string; errorMessage?: string | ((validation: ValidationResult) => string); - placeholder?: string + placeholder?: string; } export function SearchField( diff --git a/yarn.lock b/yarn.lock index 46ceb7b0a44..9ec7b6d341c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7568,6 +7568,7 @@ __metadata: "@react-stately/utils": "npm:^3.10.8" "@react-types/dialog": "npm:^3.5.22" "@react-types/grid": "npm:^3.3.6" + "@react-types/overlays": "npm:^3.9.2" "@react-types/provider": "npm:^3.8.13" "@react-types/shared": "npm:^3.32.1" "@react-types/table": "npm:^3.13.4"