diff --git a/packages/@react-aria/autocomplete/src/useAutocomplete.ts b/packages/@react-aria/autocomplete/src/useAutocomplete.ts index 3abaafd6413..291cd9065bb 100644 --- a/packages/@react-aria/autocomplete/src/useAutocomplete.ts +++ b/packages/@react-aria/autocomplete/src/useAutocomplete.ts @@ -15,7 +15,7 @@ import {AriaTextFieldProps} from '@react-aria/textfield'; import {AutocompleteProps, AutocompleteState} from '@react-stately/autocomplete'; import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getOwnerDocument, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useId, useLabels, useLayoutEffect, useObjectRef} from '@react-aria/utils'; import {dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement, moveVirtualFocus} from '@react-aria/focus'; -import {getInteractionModality} from '@react-aria/interactions'; +import {getInteractionModality, getPointerType} from '@react-aria/interactions'; // @ts-ignore import intlMessages from '../intl/*.json'; import {FocusEvent as ReactFocusEvent, KeyboardEvent as ReactKeyboardEvent, useCallback, useEffect, useMemo, useRef, useState} from 'react'; @@ -92,7 +92,6 @@ export function useAutocomplete(props: AriaAutocompleteOptions, state: Aut let timeout = useRef | undefined>(undefined); let delayNextActiveDescendant = useRef(false); let queuedActiveDescendant = useRef(null); - let lastPointerType = useRef(null); // For mobile screen readers, we don't want virtual focus, instead opting to disable FocusScope's restoreFocus and manually // moving focus back to the subtriggers @@ -106,23 +105,10 @@ export function useAutocomplete(props: AriaAutocompleteOptions, state: Aut return () => clearTimeout(timeout.current); }, []); - useEffect(() => { - let handlePointerDown = (e: PointerEvent) => { - lastPointerType.current = e.pointerType; - }; - - if (typeof PointerEvent !== 'undefined') { - document.addEventListener('pointerdown', handlePointerDown, true); - return () => { - document.removeEventListener('pointerdown', handlePointerDown, true); - }; - } - }, []); - let updateActiveDescendantEvent = useEffectEvent((e: Event) => { // Ensure input is focused if the user clicks on the collection directly. // don't trigger on touch so that mobile keyboard doesnt appear when tapping on options - if (!e.isTrusted && shouldUseVirtualFocus && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) !== inputRef.current && lastPointerType.current !== 'touch') { + if (!e.isTrusted && shouldUseVirtualFocus && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) !== inputRef.current && getPointerType() !== 'touch') { inputRef.current.focus(); } diff --git a/packages/@react-aria/interactions/src/index.ts b/packages/@react-aria/interactions/src/index.ts index 2a7a3891ea4..4e56054350a 100644 --- a/packages/@react-aria/interactions/src/index.ts +++ b/packages/@react-aria/interactions/src/index.ts @@ -17,6 +17,7 @@ export { isFocusVisible, getInteractionModality, setInteractionModality, + getPointerType, addWindowFocusTracking, useInteractionModality, useFocusVisible, diff --git a/packages/@react-aria/interactions/src/useFocusVisible.ts b/packages/@react-aria/interactions/src/useFocusVisible.ts index 45a93b86deb..80ac9c0940c 100644 --- a/packages/@react-aria/interactions/src/useFocusVisible.ts +++ b/packages/@react-aria/interactions/src/useFocusVisible.ts @@ -17,6 +17,7 @@ import {getOwnerDocument, getOwnerWindow, isMac, isVirtualClick, openLink} from '@react-aria/utils'; import {ignoreFocusEvent} from './utils'; +import {PointerType} from '@react-types/shared'; import {useEffect, useState} from 'react'; import {useIsSSR} from '@react-aria/ssr'; @@ -37,6 +38,7 @@ export interface FocusVisibleResult { } let currentModality: null | Modality = null; +let currentPointerType: PointerType = 'keyboard'; let changeHandlers = new Set(); interface GlobalListenerData { focus: () => void @@ -70,12 +72,14 @@ function handleKeyboardEvent(e: KeyboardEvent) { hasEventBeforeFocus = true; if (!(openLink as any).isOpening && isValidKey(e)) { currentModality = 'keyboard'; + currentPointerType = 'keyboard'; triggerChangeHandlers('keyboard', e); } } function handlePointerEvent(e: PointerEvent | MouseEvent) { currentModality = 'pointer'; + currentPointerType = 'pointerType' in e ? e.pointerType as PointerType : 'mouse'; if (e.type === 'mousedown' || e.type === 'pointerdown') { hasEventBeforeFocus = true; triggerChangeHandlers('pointer', e); @@ -86,6 +90,7 @@ function handleClickEvent(e: MouseEvent) { if (!(openLink as any).isOpening && isVirtualClick(e)) { hasEventBeforeFocus = true; currentModality = 'virtual'; + currentPointerType = 'virtual'; } } @@ -101,6 +106,7 @@ function handleFocusEvent(e: FocusEvent) { // This occurs, for example, when navigating a form with the next/previous buttons on iOS. if (!hasEventBeforeFocus && !hasBlurredWindowRecently) { currentModality = 'virtual'; + currentPointerType = 'virtual'; triggerChangeHandlers('virtual', e); } @@ -249,9 +255,15 @@ export function getInteractionModality(): Modality | null { export function setInteractionModality(modality: Modality): void { currentModality = modality; + currentPointerType = modality === 'pointer' ? 'mouse' : modality; triggerChangeHandlers(modality, null); } +/** @private */ +export function getPointerType(): PointerType { + return currentPointerType; +} + /** * Keeps state of the current modality. */ diff --git a/packages/@react-aria/test-utils/src/testSetup.ts b/packages/@react-aria/test-utils/src/testSetup.ts index a2ce54f9408..7a031bec9a9 100644 --- a/packages/@react-aria/test-utils/src/testSetup.ts +++ b/packages/@react-aria/test-utils/src/testSetup.ts @@ -35,35 +35,37 @@ export function installMouseEvent(): void { }); } +export function definePointerEvent(): void { + // @ts-ignore + global.PointerEvent = class FakePointerEvent extends MouseEvent { + _init: {pageX: number, pageY: number, pointerType: string, pointerId: number, width: number, height: number}; + constructor(name, init) { + super(name, init); + this._init = init; + } + get pointerType() { + return this._init.pointerType ?? 'mouse'; + } + get pointerId() { + return this._init.pointerId; + } + get pageX() { + return this._init.pageX; + } + get pageY() { + return this._init.pageY; + } + get width() { + return this._init.width; + } + get height() { + return this._init.height; + } + }; +} + export function installPointerEvent(): void { - beforeAll(() => { - // @ts-ignore - global.PointerEvent = class FakePointerEvent extends MouseEvent { - _init: {pageX: number, pageY: number, pointerType: string, pointerId: number, width: number, height: number}; - constructor(name, init) { - super(name, init); - this._init = init; - } - get pointerType() { - return this._init.pointerType ?? 'mouse'; - } - get pointerId() { - return this._init.pointerId; - } - get pageX() { - return this._init.pageX; - } - get pageY() { - return this._init.pageY; - } - get width() { - return this._init.width; - } - get height() { - return this._init.height; - } - }; - }); + beforeAll(definePointerEvent); afterAll(() => { // @ts-ignore delete global.PointerEvent; diff --git a/packages/@react-aria/utils/src/mergeProps.ts b/packages/@react-aria/utils/src/mergeProps.ts index 47319f2d1e3..c7f442fc48e 100644 --- a/packages/@react-aria/utils/src/mergeProps.ts +++ b/packages/@react-aria/utils/src/mergeProps.ts @@ -28,8 +28,7 @@ type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( /** * Merges multiple props objects together. Event handlers are chained, - * classNames are combined, and ids are deduplicated - different ids - * will trigger a side-effect and re-render components hooked up with `useId`. + * classNames are combined, and ids are deduplicated. * For all other props, the last prop object overrides all previous ones. * @param args - Multiple sets of props to merge together. */ diff --git a/packages/@react-spectrum/s2/src/Tabs.tsx b/packages/@react-spectrum/s2/src/Tabs.tsx index 5f5217cc4d5..b69c13b05f8 100644 --- a/packages/@react-spectrum/s2/src/Tabs.tsx +++ b/packages/@react-spectrum/s2/src/Tabs.tsx @@ -476,7 +476,7 @@ export function TabPanel(props: TabPanelProps): ReactNode | null { function CollapsedTabPanel(props: TabPanelProps) { // eslint-disable-next-line @typescript-eslint/no-unused-vars - let {UNSAFE_style, UNSAFE_className = '', id, ...otherProps} = props; + let {UNSAFE_style, UNSAFE_className = '', id, shouldForceMount, ...otherProps} = props; let {menuId, valueId} = useContext(CollapseContext); let ref = useRef(null); let tabIndex = useHasTabbableChild(ref) ? undefined : 0; diff --git a/packages/@react-spectrum/s2/src/index.ts b/packages/@react-spectrum/s2/src/index.ts index 9543dc1fc2b..2d495809dd2 100644 --- a/packages/@react-spectrum/s2/src/index.ts +++ b/packages/@react-spectrum/s2/src/index.ts @@ -91,10 +91,8 @@ export {TreeView, TreeViewItem, TreeViewItemContent, TreeViewLoadMoreItem} from export {pressScale} from './pressScale'; -export {Autocomplete} from 'react-aria-components'; -export {Collection} from 'react-aria-components'; -export {FileTrigger} from 'react-aria-components'; -export {parseColor} from 'react-aria-components'; +export {Autocomplete, Collection, FileTrigger, parseColor, useLocale} from 'react-aria-components'; +export {useListData, useTreeData, useAsyncList} from 'react-stately'; export type {AccordionProps, AccordionItemProps, AccordionItemHeaderProps, AccordionItemTitleProps, AccordionItemPanelProps} from './Accordion'; export type {ActionBarProps} from './ActionBar'; @@ -169,3 +167,4 @@ export type {ToggleButtonGroupProps} from './ToggleButtonGroup'; export type {TooltipProps} from './Tooltip'; export type {TreeViewProps, TreeViewItemProps, TreeViewItemContentProps, TreeViewLoadMoreItemProps} from './TreeView'; export type {AutocompleteProps, FileTriggerProps, TooltipTriggerComponentProps as TooltipTriggerProps, SortDescriptor, Color, Key, Selection} from 'react-aria-components'; +export type {ListData, TreeData, AsyncListData} from 'react-stately'; diff --git a/packages/dev/s2-docs/pages/react-aria/Autocomplete.mdx b/packages/dev/s2-docs/pages/react-aria/Autocomplete.mdx index 8d7710cedcb..c3532067e88 100644 --- a/packages/dev/s2-docs/pages/react-aria/Autocomplete.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Autocomplete.mdx @@ -122,7 +122,6 @@ Autocomplete filters a collection component using a [TextField](TextField) or [S import {Autocomplete, useFilter} from 'react-aria-components'; import {MenuTrigger, Menu, MenuItem} from 'vanilla-starter/Menu'; import {Button} from 'vanilla-starter/Button'; -import {Popover} from 'vanilla-starter/Popover'; import {SearchField} from 'vanilla-starter/SearchField'; function Example(props) { @@ -131,7 +130,7 @@ function Example(props) { return ( - +
{/*- begin highlight -*/} Science - +
); } @@ -1059,10 +1058,9 @@ When the `filter` prop is not set, the items are controlled. This example uses a ```tsx render "use client"; -import {Autocomplete} from 'react-aria-components'; +import {Autocomplete, useAsyncList} from 'react-aria-components'; import {SearchField} from 'vanilla-starter/SearchField'; import {ListBox, ListBoxItem} from 'vanilla-starter/ListBox'; -import {useAsyncList} from 'react-stately'; function AsyncLoadingExample() { let list = useAsyncList<{name: string}>({ diff --git a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx index ca5f3d68530..d1dc519332d 100644 --- a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx @@ -20,8 +20,8 @@ export const description = 'Displays a hierarchy of links to the current page or import {Breadcrumbs, Breadcrumb} from 'vanilla-starter/Breadcrumbs'; - Home - React Aria + Adobe + React Aria Breadcrumbs ``` @@ -31,8 +31,8 @@ export const description = 'Displays a hierarchy of links to the current page or import {Breadcrumbs, Breadcrumb} from 'tailwind-starter/Breadcrumbs'; - Home - React Aria + Adobe + React Aria Breadcrumbs ``` diff --git a/packages/dev/s2-docs/pages/react-aria/Button.mdx b/packages/dev/s2-docs/pages/react-aria/Button.mdx index 9e97fbed462..0cd92e40b35 100644 --- a/packages/dev/s2-docs/pages/react-aria/Button.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Button.mdx @@ -10,7 +10,6 @@ import vanillaDocs from 'docs:vanilla-starter/Button'; import tailwindDocs from 'docs:tailwind-starter/Button'; import '../../tailwind/tailwind.css'; import typesDocs from 'docs:@react-types/shared/src/events.d.ts'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['btn']; export const relatedPages = [{'title': 'useButton', 'url': 'https://react-spectrum.adobe.com/react-aria/useButton.html'}]; @@ -50,7 +49,7 @@ export const description = 'Allows a user to perform an action, with mouse, touc Use the `onPress` prop to handle interactions via mouse, keyboard, and touch. This is similar to `onClick`, but normalized for consistency across browsers, devices, and interaction methods. Read our [blog post](blog/building-a-button-part-1) to learn more. -The `onPressStart`, `onPressEnd`, and `onPressChange` events are also emitted as the user interacts with the button. Each of these handlers receives a , which provides information about the target and interaction method. See usePress for more details. +The `onPressStart`, `onPressEnd`, and `onPressChange` events are also emitted as the user interacts with the button. Each of these handlers receives a , which provides information about the target and interaction method. See [usePress](usePress) for more details. ```tsx render "use client"; diff --git a/packages/dev/s2-docs/pages/react-aria/Calendar.mdx b/packages/dev/s2-docs/pages/react-aria/Calendar.mdx index 3090329a09f..c059247e0a2 100644 --- a/packages/dev/s2-docs/pages/react-aria/Calendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Calendar.mdx @@ -151,7 +151,7 @@ Use the `minValue` and `maxValue` props to set the valid date range. The `isDate ```tsx render docs={vanillaDocs.exports.Calendar} links={docs.links} props={['isInvalid', 'errorMessage']} wide "use client"; import {isWeekend, today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; +import {useLocale} from 'react-aria-components'; import {Calendar} from 'vanilla-starter/Calendar'; function Example(props) { diff --git a/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx index a7882d113f8..ccd11dd71e1 100644 --- a/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx @@ -99,7 +99,7 @@ Use the `name` prop to submit the selected date to the server as an [ISO 8601](h ```tsx render "use client"; import {isWeekend, today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; +import {useLocale} from 'react-aria-components'; import {DatePicker} from 'vanilla-starter/DatePicker'; import {Button} from 'vanilla-starter/Button'; import {Form} from 'vanilla-starter/Form';; diff --git a/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx index 7650bdcace5..652cbc92069 100644 --- a/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx @@ -112,7 +112,7 @@ Use the `name` prop to submit the selected date to the server as an [ISO 8601](h ```tsx render docs={vanillaDocs.exports.DateRangePicker} links={docs.links} props={['allowsNonContiguousRanges']} wide "use client"; import {isWeekend, today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; +import {useLocale} from 'react-aria-components'; import {DateRangePicker} from 'vanilla-starter/DateRangePicker'; import {Button} from 'vanilla-starter/Button'; import {Form} from 'vanilla-starter/Form';; diff --git a/packages/dev/s2-docs/pages/react-aria/FileTrigger.mdx b/packages/dev/s2-docs/pages/react-aria/FileTrigger.mdx index f174f18adec..63941e546e9 100644 --- a/packages/dev/s2-docs/pages/react-aria/FileTrigger.mdx +++ b/packages/dev/s2-docs/pages/react-aria/FileTrigger.mdx @@ -4,7 +4,6 @@ export default Layout; import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['upload', 'input']; export const description = 'Allows a user to access the file system with any pressable React Aria or React Spectrum component.'; @@ -44,7 +43,7 @@ function Example(props) { ## Custom trigger -`FileTrigger` works with any pressable React Aria component (e.g. [Button](Button), [Link](Link), etc.). Use the `` component or usePress hook to wrap a custom trigger element such as a third party component or DOM element. +`FileTrigger` works with any pressable React Aria component (e.g. [Button](Button), [Link](Link), etc.). Use the `` component or [usePress](usePress) hook to wrap a custom trigger element such as a third party component or DOM element. ```tsx render "use client" diff --git a/packages/dev/s2-docs/pages/react-aria/FocusRing.mdx b/packages/dev/s2-docs/pages/react-aria/FocusRing.mdx index 8c7ba8568b8..4511135bdab 100644 --- a/packages/dev/s2-docs/pages/react-aria/FocusRing.mdx +++ b/packages/dev/s2-docs/pages/react-aria/FocusRing.mdx @@ -20,20 +20,6 @@ export const description = 'A utility component that applies a visual focus indi {docs.exports.FocusRing.description} -## Introduction - -`FocusRing` is a utility component that can be used to apply a CSS class when an element has keyboard focus. -This helps keyboard users determine which element on a page or in an application has keyboard focus as they -navigate around. Focus rings are only visible when interacting with a keyboard so as not to distract mouse -and touch screen users. When we are unable to detect if the user is using a mouse or touch screen, such as -switching in from a different tab, we show the focus ring. - -If CSS classes are not being used for styling, see [useFocusRing](useFocusRing) for a hooks version. - -## Example - -This example shows how to use `` to apply a CSS class when keyboard focus is on a button. - ```tsx render files={["packages/dev/s2-docs/pages/react-aria/FocusRingExample.css"]} 'use client'; import {FocusRing} from '@react-aria/focus'; @@ -44,8 +30,18 @@ import './FocusRingExample.css'; ``` +## Features + +`FocusRing` is a utility component that can be used to apply a CSS class when an element has keyboard focus. +This helps keyboard users determine which element on a page or in an application has keyboard focus as they +navigate around. Focus rings are only visible when interacting with a keyboard so as not to distract mouse +and touch screen users. When we are unable to detect if the user is using a mouse or touch screen, such as +switching in from a different tab, we show the focus ring. + +If CSS classes are not being used for styling, see [useFocusRing](useFocusRing) for a hooks version. + ## API ### FocusRing - \ No newline at end of file + diff --git a/packages/dev/s2-docs/pages/react-aria/FocusRingExample.css b/packages/dev/s2-docs/pages/react-aria/FocusRingExample.css index 3d335cd4e5b..180854288e3 100644 --- a/packages/dev/s2-docs/pages/react-aria/FocusRingExample.css +++ b/packages/dev/s2-docs/pages/react-aria/FocusRingExample.css @@ -5,7 +5,8 @@ border: none; color: white; font-size: 14px; - padding: 4px 8px; + padding: 8px 12px; + border-radius: 8px; } .button.focus-ring { diff --git a/packages/dev/s2-docs/pages/react-aria/GridList.mdx b/packages/dev/s2-docs/pages/react-aria/GridList.mdx index 72df160cfaa..474df90dad3 100644 --- a/packages/dev/s2-docs/pages/react-aria/GridList.mdx +++ b/packages/dev/s2-docs/pages/react-aria/GridList.mdx @@ -611,9 +611,8 @@ GridList supports drag and drop interactions when the `dragAndDropHooks` prop is ```tsx render "use client"; -import {useListData} from 'react-stately'; import {GridList, GridListItem} from 'vanilla-starter/GridList'; -import {useDragAndDrop, Text} from 'react-aria-components'; +import {useDragAndDrop, Text, useListData} from 'react-aria-components'; ///- begin collapse -/// let images = [ diff --git a/packages/dev/s2-docs/pages/react-aria/I18nProvider.mdx b/packages/dev/s2-docs/pages/react-aria/I18nProvider.mdx index cb20e88fd14..87fee826e67 100644 --- a/packages/dev/s2-docs/pages/react-aria/I18nProvider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/I18nProvider.mdx @@ -18,14 +18,7 @@ export const description = 'Override the default browser locale with an applicat # I18nProvider -## Introduction - -`I18nProvider` allows you to override the default locale as determined by the browser/system setting -with a locale defined by your application (e.g. application setting). This should be done by wrapping -your entire application in the provider, which will be cause all child elements to receive the new locale -information via [useLocale](useLocale). - -## Example +{docs.exports.I18nProvider.description} ```tsx import {I18nProvider} from '@react-aria/i18n'; @@ -35,6 +28,13 @@ import {I18nProvider} from '@react-aria/i18n'; ``` +## Introduction + +`I18nProvider` allows you to override the default locale as determined by the browser/system setting +with a locale defined by your application (e.g. application setting). This should be done by wrapping +your entire application in the provider, which will be cause all child elements to receive the new locale +information via [useLocale](useLocale). + ## API ### I18nProvider diff --git a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx index f974458eb4c..6005798697a 100644 --- a/packages/dev/s2-docs/pages/react-aria/ListBox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ListBox.mdx @@ -152,8 +152,7 @@ Use [renderEmptyState](#empty-state) to display a spinner during initial load. T "use client"; import {ListBox, ListBoxItem, ListBoxLoadMoreItem} from 'vanilla-starter/ListBox'; import {ProgressCircle} from 'vanilla-starter/ProgressCircle'; -import {Collection} from 'react-aria-components'; -import {useAsyncList} from 'react-stately'; +import {Collection, useAsyncList} from 'react-aria-components'; interface Character { name: string @@ -340,8 +339,7 @@ ListBox supports drag and drop interactions when the `dragAndDropHooks` prop is ```tsx render "use client"; -import {useListData} from 'react-stately'; -import {ListBox, ListBoxItem, useDragAndDrop} from 'react-aria-components'; +import {ListBox, ListBoxItem, useDragAndDrop, useListData} from 'react-aria-components'; function Example() { let list = useListData({ diff --git a/packages/dev/s2-docs/pages/react-aria/Menu.mdx b/packages/dev/s2-docs/pages/react-aria/Menu.mdx index 1c511bfa4b0..4d38e4322f4 100644 --- a/packages/dev/s2-docs/pages/react-aria/Menu.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Menu.mdx @@ -322,7 +322,7 @@ function Example() { return ( -
+
{/*- begin highlight -*/} diff --git a/packages/dev/s2-docs/pages/react-aria/PortalProvider.mdx b/packages/dev/s2-docs/pages/react-aria/PortalProvider.mdx index 27f110823e2..47789e9f038 100644 --- a/packages/dev/s2-docs/pages/react-aria/PortalProvider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/PortalProvider.mdx @@ -11,6 +11,7 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-aria/overlays'; import {FunctionAPI} from '../../src/FunctionAPI'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const section = 'Utilities'; export const description = 'Allows specifying the container element where overlays like modals and popovers are rendered.'; @@ -26,12 +27,13 @@ Modals, Popovers, Toasts, and Tooltips will portal their overlay element to. Thi your app is already portalling other elements to a location other than the `document.body` and thus requires your React Aria components to send their overlays to the same container. -Please note that `UNSAFE_PortalProvider` is considered `UNSAFE` because it is an escape hatch, and there are -many places that an application could portal to. Not all of them will work, either with styling, accessibility, + + Safety + `UNSAFE_PortalProvider` is considered `UNSAFE`. Some portal locations may not work with styling, accessibility, or for a variety of other reasons. Typically, it is best to portal to the root of the entire application, e.g. the `body` element, -outside of any possible overflow or stacking contexts. We envision `UNSAFE_PortalProvider` being used to group all of the portalled -elements into a single container at the root of the app or to control the order of children of the `body` element, but you may have use cases -that need to do otherwise. +outside any possible overflow or stacking contexts. `UNSAFE_PortalProvider` can be used to group all of the portalled +elements into a single container at the root of the app or to control the order of children of the `body` element. + ## Example diff --git a/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx index f688eb9f3b8..d3ff5d317ea 100644 --- a/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx @@ -161,7 +161,7 @@ Use the `minValue` and `maxValue` props to set the valid date range. The `isDate ```tsx render docs={vanillaDocs.exports.RangeCalendar} links={docs.links} props={['allowsNonContiguousRanges']} wide "use client"; import {today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; +import {useLocale} from 'react-aria-components'; import {RangeCalendar} from 'vanilla-starter/RangeCalendar'; import {useState} from 'react'; diff --git a/packages/dev/s2-docs/pages/react-aria/SSRProvider.mdx b/packages/dev/s2-docs/pages/react-aria/SSRProvider.mdx index eda0e248e0b..eb27a697f98 100644 --- a/packages/dev/s2-docs/pages/react-aria/SSRProvider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/SSRProvider.mdx @@ -10,23 +10,19 @@ governing permissions and limitations under the License. */} import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-aria/ssr'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const section = 'Utilities'; export const description = 'Ensure consistent auto-generated IDs between server and client in React 16 and 17.'; # SSRProvider -## Introduction - -If you're using React 16 or 17, `SSRProvider` should be used as a wrapper for the entire application during server side rendering. -It works together with the [useId](useId) hook to ensure that auto generated ids are consistent -between the client and server by resetting the id internal counter on each request. -See the [server side rendering](ssr) docs for more information. - -**Note**: if you're using React 18 or newer, `SSRProvider` is not necessary and can be removed from your app. React Aria uses the -[React.useId](https://react.dev/reference/react/useId) hook internally when using React 18, which ensures ids are consistent. +{docs.exports.SSRProvider.description} -## Example + + React 16 or 17 only + If you're using React 18 or newer, `SSRProvider` is not necessary and can be removed from your app. React Aria uses the [React.useId](https://react.dev/reference/react/useId) hook internally when using React 18, which ensures ids are consistent. + ```tsx import {SSRProvider} from '@react-aria/ssr'; @@ -36,6 +32,12 @@ import {SSRProvider} from '@react-aria/ssr'; ``` +## Introduction + +If you're using React 16 or 17, `SSRProvider` should be used as a wrapper for the entire application during server side rendering. +It works together with the [useId](useId) hook to ensure that auto generated ids are consistent +between the client and server by resetting the id internal counter on each request. + ## API ### SSRProvider diff --git a/packages/dev/s2-docs/pages/react-aria/Select.mdx b/packages/dev/s2-docs/pages/react-aria/Select.mdx index 8724009d5d8..c9273350493 100644 --- a/packages/dev/s2-docs/pages/react-aria/Select.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Select.mdx @@ -279,7 +279,7 @@ function Example(props) { let [animal, setAnimal] = useState("bison"); return ( -
+ <>
Current selection: {JSON.stringify(animal)}
-
+ ); } ``` diff --git a/packages/dev/s2-docs/pages/react-aria/SelectionIndicator.css b/packages/dev/s2-docs/pages/react-aria/SelectionIndicator.css index c5d5a81b9a8..f9aa26e732d 100644 --- a/packages/dev/s2-docs/pages/react-aria/SelectionIndicator.css +++ b/packages/dev/s2-docs/pages/react-aria/SelectionIndicator.css @@ -1,14 +1,14 @@ .animated-ListBoxItem { display: flex; align-items: center; - gap: 8px; - min-height: 28px; - padding: 4px 4px 4px 12px; - border-radius: 6px; + gap: var(--spacing-2); + min-height: var(--spacing-7); + padding: var(--spacing-1) var(--spacing-1) var(--spacing-1) var(--spacing-3); + border-radius: var(--radius); outline: none; cursor: default; color: var(--text-color); - font-size: 14px; + font-size: var(--font-size); position: relative; &[data-focus-visible] { @@ -22,9 +22,9 @@ .react-aria-SelectionIndicator { position: absolute; - left: 4px; + left: var(--spacing-1); width: 3px; - height: 20px; + height: var(--spacing-5); border-radius: 4px; background: var(--highlight-background); transition-property: translate, opacity; diff --git a/packages/dev/s2-docs/pages/react-aria/Table.mdx b/packages/dev/s2-docs/pages/react-aria/Table.mdx index 48de1df4a6b..0de9a1e2402 100644 --- a/packages/dev/s2-docs/pages/react-aria/Table.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Table.mdx @@ -57,8 +57,7 @@ export const description = 'Displays data in rows and columns and enables a user ```tsx render docs={docs.exports.Table} links={docs.links} props={['selectionMode']} initialProps={{'aria-label': 'Files', selectionMode: 'multiple'}} type="tailwind" files={["starters/tailwind/src/Table.tsx"]} "use client"; - import {Table, TableHeader, Column, Row, Cell} from 'tailwind-starter/Table'; - import {TableBody} from 'react-aria-components'; + import {Table, TableHeader, TableBody, Column, Row, Cell} from 'tailwind-starter/Table'; @@ -178,10 +177,9 @@ Use [renderEmptyState](#empty-state) to display a spinner during initial load. T ```tsx render "use client"; +import {Collection, useAsyncList} from 'react-aria-components'; import {Table, TableHeader, Column, Row, TableBody, Cell, TableLoadMoreItem} from 'vanilla-starter/Table'; -import {Collection} from 'react-aria-components'; import {ProgressCircle} from 'vanilla-starter/ProgressCircle'; -import {useAsyncList} from 'react-stately'; interface Character { name: string; @@ -235,7 +233,9 @@ function AsyncSortTable() { ( - +
+ +
)}> {(item) => ( @@ -589,10 +589,8 @@ Table supports drag and drop interactions when the `dragAndDropHooks` prop is pr ```tsx render "use client"; -import {useListData} from 'react-stately'; -import {Table, TableHeader, Column, Row} from 'vanilla-starter/Table'; -import {TableBody, Cell} from 'react-aria-components'; -import {useDragAndDrop} from 'react-aria-components'; +import {Table, TableHeader, TableBody, Column, Row, Cell} from 'vanilla-starter/Table'; +import {useDragAndDrop, useListData} from 'react-aria-components'; function ReorderableTable() { let list = useListData({ diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx index d6990c4bdc0..dab0d80a1ec 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx @@ -121,6 +121,7 @@ export const description = 'Organizes content into multiple sections and allows import {Tabs, TabList, Tab, TabPanels, TabPanel} from 'vanilla-starter/Tabs'; import {Button} from 'vanilla-starter/Button'; import {useState} from 'react'; +import {Plus, Minus} from 'lucide-react'; function Example() { let [tabs, setTabs] = useState([ @@ -158,8 +159,12 @@ function Example() { {item => {item.title}}
- - + +
diff --git a/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx b/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx index ab6990235f4..9930b7533a0 100644 --- a/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx @@ -49,7 +49,7 @@ export const description = 'A focusable list of labels, categories, keywords, fi ```tsx render "use client"; import {TagGroup, Tag} from 'vanilla-starter/TagGroup'; -import {useListData} from 'react-stately'; +import {useListData} from 'react-aria-components'; function Example() { let list = useListData({ diff --git a/packages/dev/s2-docs/pages/react-aria/Tree.mdx b/packages/dev/s2-docs/pages/react-aria/Tree.mdx index 51fcd14b12b..3eb0b8ed533 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tree.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tree.mdx @@ -112,8 +112,7 @@ Use [renderEmptyState](#empty-state) to display a spinner during initial load. T "use client"; import {Tree, TreeItem, TreeLoadMoreItem} from 'vanilla-starter/Tree'; import {ProgressCircle} from 'vanilla-starter/ProgressCircle'; -import {Collection} from 'react-aria-components'; -import {useAsyncList} from 'react-stately'; +import {Collection, useAsyncList} from 'react-aria-components'; interface Character { name: string @@ -289,9 +288,8 @@ Tree supports drag and drop interactions when the `dragAndDropHooks` prop is pro ```tsx render "use client"; -import {useTreeData} from 'react-stately'; import {Tree, TreeItem} from 'vanilla-starter/Tree'; -import {useDragAndDrop, Collection} from 'react-aria-components'; +import {useDragAndDrop, Collection, useTreeData} from 'react-aria-components'; function Example() { ///- begin collapse -/// diff --git a/packages/dev/s2-docs/pages/react-aria/VisuallyHidden.mdx b/packages/dev/s2-docs/pages/react-aria/VisuallyHidden.mdx index 8a01162bfb2..25145ef3955 100644 --- a/packages/dev/s2-docs/pages/react-aria/VisuallyHidden.mdx +++ b/packages/dev/s2-docs/pages/react-aria/VisuallyHidden.mdx @@ -11,6 +11,7 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-aria/visually-hidden'; import {FunctionAPI} from '../../src/FunctionAPI'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const section = 'Utilities'; export const description = 'Hides its children visually but keeps content accessible to screen readers.'; @@ -19,43 +20,30 @@ export const description = 'Hides its children visually but keeps content access {docs.exports.VisuallyHidden.description} -## Gotchas - -VisuallyHidden is positioned absolutely, so it needs a positioned ancestor. Otherwise, it will be positioned relative to the nearest positioned ancestor, which may be the body, causing undesired scroll bars to appear. - -```tsx - -``` - -## Hook - -In order to allow additional rendering flexibility, the `useVisuallyHidden` hook can be -used in custom components instead of the `VisuallyHidden` component. It supports the same -options as the component, and returns props to spread onto the element that should be hidden. - - - -### Example - ```tsx -import {useVisuallyHidden} from '@react-aria/visually-hidden'; - -let {visuallyHiddenProps} = useVisuallyHidden(); +import {VisuallyHidden} from 'react-aria'; -
I am hidden
+I am hidden ``` + + Positioning + VisuallyHidden is positioned absolutely, so it must have a `position: relative` or `position: absolute` ancestor. Otherwise, undesired scrollbars may appear. + + ## API ### VisuallyHidden +### useVisuallyHidden + +For additional rendering flexibility, use the `useVisuallyHidden` hook . It supports the same +options as the component, and returns props to spread onto the element that should be hidden. + + + ## Example See [useRadioGroup](https://react-spectrum.adobe.com/react-aria/useRadioGroup.html#styling) and [useCheckbox](https://react-spectrum.adobe.com/react-aria/useCheckbox.html#styling) diff --git a/packages/dev/s2-docs/pages/react-aria/blog/ColorEditorExample.tsx b/packages/dev/s2-docs/pages/react-aria/blog/ColorEditorExample.tsx index df70de58d99..653d6d654f6 100644 --- a/packages/dev/s2-docs/pages/react-aria/blog/ColorEditorExample.tsx +++ b/packages/dev/s2-docs/pages/react-aria/blog/ColorEditorExample.tsx @@ -30,7 +30,7 @@ function ColorEditor({hideAlphaChannel = false}: ColorEditorProps) { let formatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/color'); return ( -
+
diff --git a/packages/dev/s2-docs/pages/react-aria/blog/DragBetweenListsExample.tsx b/packages/dev/s2-docs/pages/react-aria/blog/DragBetweenListsExample.tsx index 1d5287652f2..cef69a845c7 100644 --- a/packages/dev/s2-docs/pages/react-aria/blog/DragBetweenListsExample.tsx +++ b/packages/dev/s2-docs/pages/react-aria/blog/DragBetweenListsExample.tsx @@ -2,8 +2,7 @@ import {ListBox, ListBoxItem} from 'vanilla-starter/ListBox'; import {Folder, File} from 'lucide-react'; -import {useDragAndDrop, isTextDropItem} from 'react-aria-components'; -import {useListData} from 'react-stately'; +import {useDragAndDrop, isTextDropItem, useListData} from 'react-aria-components'; import React from 'react'; function BidirectionalDnDListBox(props) { diff --git a/packages/dev/s2-docs/pages/react-aria/blog/building-a-button-part-1.mdx b/packages/dev/s2-docs/pages/react-aria/blog/building-a-button-part-1.mdx index acccb5c6552..5724b6e896b 100644 --- a/packages/dev/s2-docs/pages/react-aria/blog/building-a-button-part-1.mdx +++ b/packages/dev/s2-docs/pages/react-aria/blog/building-a-button-part-1.mdx @@ -108,7 +108,7 @@ With the [usePress](../usePress) hook, our buttons handle interactions consisten
``` +## Features + +Landmarks provide a way to designate important subsections of a page. They allow screen reader users to get an overview of the various sections of the page, and jump to a specific section. +By default, browsers do not provide a consistent way to navigate between landmarks using the keyboard. +The `useLandmark` hook enables keyboard navigation between landmarks, and provides a consistent experience across browsers. + +* F6 and Shift+F6 key navigation between landmarks +* Alt+F6 key navigation to the main landmark +* Support for navigating nested landmarks + +## Anatomy + +Landmark elements can be registered with the `useLandmark` hook. The `role` prop is required. + +Pressing F6 will move focus to the next landmark on the page, and pressing Shift+F6 will move focus to the previous landmark. +If an element within a landmark was previously focused before leaving that landmark, focus will return to that element when navigating back to that landmark. +Alt+F6 will always move focus to the main landmark if it has been registered. + +If multiple landmarks are registered with the same role, they should have unique labels, which can be provided by aria-label or aria-labelledby. + +For an example of landmarks in use, see the [useToastRegion](https://react-spectrum.adobe.com/react-aria/useToast.html#anatomy) documentation. + ## API diff --git a/packages/dev/s2-docs/pages/react-aria/useLocale.mdx b/packages/dev/s2-docs/pages/react-aria/useLocale.mdx index 7ed25b06402..4f4c71b6ea1 100644 --- a/packages/dev/s2-docs/pages/react-aria/useLocale.mdx +++ b/packages/dev/s2-docs/pages/react-aria/useLocale.mdx @@ -17,6 +17,7 @@ export const description = 'Provides access to the current locale and layout dir # useLocale +{docs.exports.useLocale.description} ## Introduction @@ -32,7 +33,7 @@ so that the browser knows which language and direction the user interface should ## Example ```tsx -import {useLocale} from '@react-aria/i18n'; +import {useLocale} from 'react-aria'; function YourApp() { let {locale, direction} = useLocale(); diff --git a/packages/dev/s2-docs/pages/react-aria/useLongPress.mdx b/packages/dev/s2-docs/pages/react-aria/useLongPress.mdx index 9e6c89275ef..15379daa8cb 100644 --- a/packages/dev/s2-docs/pages/react-aria/useLongPress.mdx +++ b/packages/dev/s2-docs/pages/react-aria/useLongPress.mdx @@ -13,6 +13,8 @@ import {FunctionAPI} from '../../src/FunctionAPI'; import docs from 'docs:@react-aria/interactions'; import typesDocs from 'docs:@react-types/shared/src/events.d.ts'; import {InterfaceType} from '../../src/types'; +import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; + export const section = 'Interactions'; export const description = 'Handles long press interactions across mouse and touch devices.'; @@ -20,38 +22,18 @@ export const description = 'Handles long press interactions across mouse and tou {docs.exports.useLongPress.description} -## Features - -A long press is triggered when a user presses and holds their pointer over a target for a minimum period of time. If the user moves their pointer off of the target before the time threshold, the interaction is canceled. Once a long press event is triggered, other pointer interactions that may be active such as `usePress` and `useMove` will be canceled so that only the long press is activated. - -* Handles mouse and touch events -* Uses pointer events where available, with fallbacks to mouse and touch events -* Ignores emulated mouse events in mobile browsers -* Prevents text selection on touch devices while long pressing -* Prevents browser and OS context menus from appearing while long pressing -* Customizable time threshold for long press -* Supports an accessibility description to indicate to assistive technology users that a long press action is available - -## Example - -This example shows a button that has both a normal press action using [usePress](usePress), as well as a long -press action using `useLongPress`. Pressing the button will set the mode to "Normal speed", and long pressing it will -set the mode to "Hyper speed". All of the emitted events are also logged below. Note that when long pressing the button, -only a long press is emitted, and no normal press is emitted on pointer up. - -**Note**: this example does not have a keyboard accessible way to trigger the long press action. Because the method of triggering -this action will differ depending on the component, it is outside the scope of `useLongPress`. Make sure to implement a keyboard -friendly alternative to all long press interactions if you are using this hook directly. - ```tsx render "use client"; import React from 'react'; -import {mergeProps} from '@react-aria/utils'; +import {mergeProps} from 'react-aria'; import {useLongPress, usePress} from '@react-aria/interactions'; function Example() { let [events, setEvents] = React.useState([]); let [mode, setMode] = React.useState('Activate'); + + /*- begin focus -*/ + // Long press to activate "Hyper speed" let {longPressProps} = useLongPress({ accessibilityDescription: 'Long press to activate hyper speed', onLongPressStart: e => setEvents( @@ -67,7 +49,9 @@ function Example() { ); } }); + /*- end focus -*/ + // Short press to activate "Normal speed" let {pressProps} = usePress({ onPress: e => { setMode('Normal speed'); @@ -92,6 +76,23 @@ function Example() { } ``` + + Accessibility + This example does not have a keyboard accessible way to trigger the long press action. Because the method of triggering +this action will differ depending on the component, it is outside the scope of `useLongPress`. Make sure to implement a keyboard +friendly alternative to all long press interactions if you are using this hook directly. + + +## Features + +A long press is triggered when a user presses and holds their pointer over a target for a minimum period of time. If the user moves their pointer off of the target before the time threshold, the interaction is canceled. Once a long press event is triggered, other pointer interactions that may be active such as `usePress` and `useMove` will be canceled so that only the long press is activated. + +* Handles mouse and touch events +* Prevents text selection on touch devices while long pressing +* Prevents browser and OS context menus from appearing while long pressing +* Customizable time threshold for long press +* Supports an accessibility description to indicate to assistive technology users that a long press action is available + ## API diff --git a/packages/dev/s2-docs/pages/react-aria/useMove.mdx b/packages/dev/s2-docs/pages/react-aria/useMove.mdx index b8b1db60d4a..28a7b67373f 100644 --- a/packages/dev/s2-docs/pages/react-aria/useMove.mdx +++ b/packages/dev/s2-docs/pages/react-aria/useMove.mdx @@ -20,31 +20,10 @@ export const description = 'Handles move interactions for pointer and keyboard, {docs.exports.useMove.description} -## API - - - -## Features - -Move events are fired as the pointer moves around, and specify the distance that the pointer traveled since the last event. In addition, after a user focuses the target element, move events are fired when the user presses the arrow keys. - -* Handles mouse and touch events -* Handles arrow key presses -* Uses pointer events where available, with fallbacks to mouse and touch events -* Ignores emulated mouse events in mobile browsers -* Handles disabling text selection on mobile while the press interaction is active -* Normalizes many cross browser inconsistencies - -## Example - -This example shows a ball that can be moved by dragging with a mouse or touch, or by tabbing to it and using -the arrow keys on your keyboard. The movement is clamped so that the ball cannot be dragged outside a box. -All of the move events are logged below so that you can inspect what is going on. - ```tsx render "use client"; import React from 'react'; -import {useMove} from '@react-aria/interactions'; +import {useMove} from 'react-aria'; function Example() { const CONTAINER_SIZE = 200; @@ -132,6 +111,13 @@ function Example() { } ``` +## Features + +Move events are emitted after the user presses down and then drags the pointer around. They specify the distance that the pointer traveled since the last event. In addition, after a user focuses the target element, move events are fired when the user presses the arrow keys. + +* Handles mouse and touch events +* Handles arrow key presses +* Disables text selection while the user drags ## API @@ -147,7 +133,4 @@ function Example() { ### MoveEvent -Each of these handlers is fired with a `MoveEvent`, which exposes information about the target and the -type of event that triggered the interaction. - diff --git a/packages/dev/s2-docs/pages/react-aria/useNumberFormatter.mdx b/packages/dev/s2-docs/pages/react-aria/useNumberFormatter.mdx index 10f83ffbc1b..9a7d3004e4b 100644 --- a/packages/dev/s2-docs/pages/react-aria/useNumberFormatter.mdx +++ b/packages/dev/s2-docs/pages/react-aria/useNumberFormatter.mdx @@ -19,6 +19,8 @@ export const description = 'Wraps Intl.NumberFormat and formats numbers for the # useNumberFormatter +{docs.exports.useNumberFormatter.description} + ## Introduction `useNumberFormatter` wraps a builtin browser [Intl.NumberFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat) @@ -34,7 +36,7 @@ using the [I18nProvider](I18nProvider) to specify the locale to display. ```tsx render 'use client'; -import {I18nProvider, useNumberFormatter} from '@react-aria/i18n'; +import {I18nProvider, useNumberFormatter} from 'react-aria'; function Currency({value, currency}) { let formatter = useNumberFormatter({ diff --git a/packages/dev/s2-docs/pages/react-aria/useObjectRef.mdx b/packages/dev/s2-docs/pages/react-aria/useObjectRef.mdx index 895bdb83666..d56d6a5601f 100644 --- a/packages/dev/s2-docs/pages/react-aria/useObjectRef.mdx +++ b/packages/dev/s2-docs/pages/react-aria/useObjectRef.mdx @@ -29,9 +29,7 @@ callback ref or object ref was passed in. This is useful for passing refs to Rea ```tsx render 'use client'; import React from 'react'; -import {useObjectRef} from '@react-aria/utils'; -import {useButton} from '@react-aria/button'; -import {AriaButtonProps} from '@react-types/button'; +import {useObjectRef, useButton, AriaButtonProps} from 'react-aria'; let Button = React.forwardRef((props: AriaButtonProps, ref: React.ForwardedRef) => { let objRef = useObjectRef(ref); diff --git a/packages/dev/s2-docs/pages/react-aria/usePress.mdx b/packages/dev/s2-docs/pages/react-aria/usePress.mdx index bb9a856b2f2..4d34195ee7b 100644 --- a/packages/dev/s2-docs/pages/react-aria/usePress.mdx +++ b/packages/dev/s2-docs/pages/react-aria/usePress.mdx @@ -20,35 +20,10 @@ export const description = 'Handles press interactions across mouse, touch, and {docs.exports.usePress.description} -## Features - -`usePress` returns the current press state, which can be used to adjust -the visual appearance of the target. If the pointer is released over the target, then an `onPress` event is fired. - -* Handles mouse and touch events -* Handles Enter or Space key presses -* Handles screen reader virtual clicks -* Uses pointer events where available, with fallbacks to mouse and touch events -* Normalizes focus behavior on mouse and touch interactions across browsers -* Handles disabling text selection on mobile while the press interaction is active -* Handles canceling press interactions on scroll -* Normalizes many cross browser inconsistencies - -Read our [blog post](blog/building-a-button-part-1) about the complexities of press event handling to learn more. - -## Example - -This example shows a simple target that handles press events with `usePress` and logs them to a list below. -It also uses the `isPressed` state to adjust the background color when the target is pressed. -Press down on the target and drag your pointer off and over to see when the events are fired, and try focusing -the target with a keyboard and pressing the Enter or Space keys to trigger events as well. - -**NOTE: for more advanced button functionality, see [useButton](https://react-spectrum.adobe.com/react-aria/useButton.html).** - ```tsx render "use client"; import React from 'react'; -import {usePress} from '@react-aria/interactions'; +import {usePress} from 'react-aria'; function Example() { let [events, setEvents] = React.useState([]); @@ -72,7 +47,8 @@ function Example() { background: isPressed ? 'darkgreen' : 'green', color: 'white', display: 'inline-block', - padding: 4, + padding: '8px 12px', + borderRadius: 8, cursor: 'pointer' }} role="button" @@ -91,6 +67,20 @@ function Example() { } ``` +## Features + +`usePress` returns the current press state, which can be used to adjust the visual appearance of the target. If the pointer is released over the target, then an `onPress` event is fired. + +* Handles mouse and touch events +* Handles Enter or Space key presses +* Handles screen reader virtual clicks +* Normalizes focus behavior on mouse and touch interactions across browsers +* Disables text selection while the press interaction is active +* Cancels press interactions on scroll +* Normalizes many cross browser inconsistencies + +Read our [blog post](blog/building-a-button-part-1) learn more. + ## API @@ -105,7 +95,4 @@ function Example() { ### PressEvent -Each of these handlers is fired with a `PressEvent`, which exposes information about the target and the -type of event that triggered the interaction. - - \ No newline at end of file + diff --git a/packages/dev/s2-docs/pages/s2/Breadcrumbs.mdx b/packages/dev/s2-docs/pages/s2/Breadcrumbs.mdx index 3752b9f87ab..65545da20eb 100644 --- a/packages/dev/s2-docs/pages/s2/Breadcrumbs.mdx +++ b/packages/dev/s2-docs/pages/s2/Breadcrumbs.mdx @@ -15,8 +15,8 @@ export const description = 'Display a hierarchy of links to the current page or import {Breadcrumbs, Breadcrumb} from '@react-spectrum/s2'; - Home - React Spectrum + Home + React Spectrum Breadcrumbs ``` diff --git a/packages/dev/s2-docs/pages/s2/Calendar.mdx b/packages/dev/s2-docs/pages/s2/Calendar.mdx index b73fa52f311..00c8e6c0796 100644 --- a/packages/dev/s2-docs/pages/s2/Calendar.mdx +++ b/packages/dev/s2-docs/pages/s2/Calendar.mdx @@ -3,7 +3,6 @@ export default Layout; import {Calendar} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['date']; export const description = 'Allows a user to select a single date from a date grid.'; @@ -30,7 +29,7 @@ export const description = 'Allows a user to select a single date from a date gr ## Value -Use the `value` or `defaultValue` prop to set the date value, using objects in the @internationalized/date package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. +Use the `value` or `defaultValue` prop to set the date value, using objects in the [@internationalized/date](react-aria:internationalized/date/) package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. ```tsx render "use client"; @@ -71,7 +70,7 @@ import {parseDate} from '@internationalized/date'; ### Custom calendar systems -`Calendar` also supports custom calendar systems that implement custom business rules, for example a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar. See the @internationalized/date docs for an example implementation. +`Calendar` also supports custom calendar systems that implement custom business rules, for example a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar. See the [@internationalized/date docs](react-aria:internationalized/date/Calendar#custom-calendars) for an example implementation. ```tsx render "use client"; @@ -143,8 +142,7 @@ Use the `minValue` and `maxValue` props to set the valid date range. The `isDate ```tsx render docs={docs.exports.Calendar} links={docs.links} props={['isInvalid', 'errorMessage']} wide type="s2" "use client"; import {isWeekend, today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; -import {Calendar} from '@react-spectrum/s2'; +import {Calendar, useLocale} from '@react-spectrum/s2'; function Example(props) { let {locale} = useLocale(); diff --git a/packages/dev/s2-docs/pages/s2/CardView.mdx b/packages/dev/s2-docs/pages/s2/CardView.mdx index 2b3a4262968..f0608c8e887 100644 --- a/packages/dev/s2-docs/pages/s2/CardView.mdx +++ b/packages/dev/s2-docs/pages/s2/CardView.mdx @@ -400,8 +400,7 @@ Use the `loadingState` and `onLoadMore` props to enable async loading and infini ```tsx render docs={docs.exports.CardView} links={docs.links} props={['loadingState']} initialProps={{selectionMode: 'multiple'}} type="s2" wide "use client"; -import {CardView, Collection, SkeletonCollection, Card, CardPreview, Image, Content, Text, Avatar} from '@react-spectrum/s2'; -import {useAsyncList} from 'react-stately'; +import {CardView, Collection, SkeletonCollection, Card, CardPreview, Image, Content, Text, Avatar, useAsyncList} from '@react-spectrum/s2'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; export default function Example(props) { diff --git a/packages/dev/s2-docs/pages/s2/ComboBox.mdx b/packages/dev/s2-docs/pages/s2/ComboBox.mdx index 259a3a8cf26..d06b3387064 100644 --- a/packages/dev/s2-docs/pages/s2/ComboBox.mdx +++ b/packages/dev/s2-docs/pages/s2/ComboBox.mdx @@ -156,8 +156,7 @@ Use the `loadingState` and `onLoadMore` props to enable async loading and infini ```tsx render "use client"; -import {ComboBox, ComboBoxItem} from '@react-spectrum/s2'; -import {useAsyncList} from 'react-stately'; +import {ComboBox, ComboBoxItem, useAsyncList} from '@react-spectrum/s2'; function Example() { let list = useAsyncList({ diff --git a/packages/dev/s2-docs/pages/s2/DateField.mdx b/packages/dev/s2-docs/pages/s2/DateField.mdx index 103a1ed184c..9a966c8362f 100644 --- a/packages/dev/s2-docs/pages/s2/DateField.mdx +++ b/packages/dev/s2-docs/pages/s2/DateField.mdx @@ -3,7 +3,6 @@ export default Layout; import {DateField} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['calendar']; export const description = 'Allows a user to enter and edit date values using a keyboard.'; @@ -22,7 +21,7 @@ export const description = 'Allows a user to enter and edit date values using a ## Value -Use the `value` or `defaultValue` prop to set the date value, using objects in the @internationalized/date package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. +Use the `value` or `defaultValue` prop to set the date value, using objects in the [@internationalized/date](react-aria:internationalized/date/) package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. ```tsx render "use client"; diff --git a/packages/dev/s2-docs/pages/s2/DatePicker.mdx b/packages/dev/s2-docs/pages/s2/DatePicker.mdx index aa7616696ea..747569f160a 100644 --- a/packages/dev/s2-docs/pages/s2/DatePicker.mdx +++ b/packages/dev/s2-docs/pages/s2/DatePicker.mdx @@ -3,7 +3,6 @@ export default Layout; import {DatePicker} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['calendar']; export const description = 'Combines a DateField and a Calendar popover.'; @@ -22,7 +21,7 @@ export const description = 'Combines a DateField and a Calendar popover.'; ## Value -Use the `value` or `defaultValue` prop to set the date value, using objects in the @internationalized/date package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. +Use the `value` or `defaultValue` prop to set the date value, using objects in the [@internationalized/date](react-aria:internationalized/date/) package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. ```tsx render "use client"; @@ -83,8 +82,7 @@ Use the `name` prop to submit the selected date to the server as an [ISO 8601](h ```tsx render docs={docs.exports.DatePicker} links={docs.links} props={['isRequired', 'necessityIndicator']} initialProps={{isRequired: true}} type="s2" wide "use client"; import {isWeekend, today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; -import {DatePicker, Form, Button} from '@react-spectrum/s2'; +import {DatePicker, Form, Button, useLocale} from '@react-spectrum/s2'; function Example(props) { let {locale} = useLocale(); diff --git a/packages/dev/s2-docs/pages/s2/DateRangePicker.mdx b/packages/dev/s2-docs/pages/s2/DateRangePicker.mdx index 011061a7c6e..72966a7652e 100644 --- a/packages/dev/s2-docs/pages/s2/DateRangePicker.mdx +++ b/packages/dev/s2-docs/pages/s2/DateRangePicker.mdx @@ -3,7 +3,6 @@ export default Layout; import {DateRangePicker} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['calendar']; export const description = 'Combines two DateFields and a RangeCalendar popover.'; @@ -28,7 +27,7 @@ export const description = 'Combines two DateFields and a RangeCalendar popover. ## Value -Use the `value` or `defaultValue` prop to set the selected date range, using objects in the @internationalized/date package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. +Use the `value` or `defaultValue` prop to set the selected date range, using objects in the [@internationalized/date](react-aria:internationalized/date/) package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. ```tsx render "use client"; @@ -102,8 +101,7 @@ Use the `name` prop to submit the selected date to the server as an [ISO 8601](h ```tsx render docs={docs.exports.DateRangePicker} links={docs.links} props={['allowsNonContiguousRanges', 'isRequired', 'necessityIndicator']} initialProps={{isRequired: true}} wide type="s2" "use client"; import {isWeekend, today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; -import {DateRangePicker, Form, Button} from '@react-spectrum/s2'; +import {DateRangePicker, Form, Button, useLocale} from '@react-spectrum/s2'; function Example(props) { let {locale} = useLocale(); diff --git a/packages/dev/s2-docs/pages/s2/Picker.mdx b/packages/dev/s2-docs/pages/s2/Picker.mdx index c86cf901ca3..6701775636b 100644 --- a/packages/dev/s2-docs/pages/s2/Picker.mdx +++ b/packages/dev/s2-docs/pages/s2/Picker.mdx @@ -153,8 +153,7 @@ Use the `loadingState` and `onLoadMore` props to enable async loading and infini ```tsx render "use client"; -import {Picker, PickerItem} from '@react-spectrum/s2'; -import {useAsyncList} from 'react-stately'; +import {Picker, PickerItem, useAsyncList} from '@react-spectrum/s2'; interface Character { name: string diff --git a/packages/dev/s2-docs/pages/s2/Provider.mdx b/packages/dev/s2-docs/pages/s2/Provider.mdx index 36cd2fc6843..6a1454145b0 100644 --- a/packages/dev/s2-docs/pages/s2/Provider.mdx +++ b/packages/dev/s2-docs/pages/s2/Provider.mdx @@ -84,7 +84,7 @@ By default, React Spectrum chooses the locale matching the user’s browser/oper ## Client side routing -The Provider component accepts an optional `router` prop. This enables React Spectrum components that render links to perform client side navigation using your application or framework's client side router. See the client side routing guide for details on how to set this up. +The Provider component accepts an optional `router` prop. This enables React Spectrum components that render links to perform client side navigation using your application or framework's client side router. See the [client side routing guide](routing) for details on how to set this up. ```tsx let navigate = useNavigateFromYourRouter(); diff --git a/packages/dev/s2-docs/pages/s2/RangeCalendar.mdx b/packages/dev/s2-docs/pages/s2/RangeCalendar.mdx index cfa4b8e2146..e79d28f14c2 100644 --- a/packages/dev/s2-docs/pages/s2/RangeCalendar.mdx +++ b/packages/dev/s2-docs/pages/s2/RangeCalendar.mdx @@ -3,7 +3,6 @@ export default Layout; import {RangeCalendar} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['calendar']; export const description = 'Allows a user to select a contiguous range of dates.'; @@ -30,7 +29,7 @@ export const description = 'Allows a user to select a contiguous range of dates. ## Value -Use the `value` or `defaultValue` prop to set the selected date range, using objects in the @internationalized/date package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. +Use the `value` or `defaultValue` prop to set the selected date range, using objects in the [@internationalized/date](react-aria:internationalized/date/) package. This library supports parsing date strings in multiple formats, manipulation across international calendar systems, time zones, etc. ```tsx render "use client"; @@ -83,7 +82,7 @@ import {parseDate} from '@internationalized/date'; ### Custom calendar systems -`RangeCalendar` also supports custom calendar systems that implement custom business rules, for example a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar. See the @internationalized/date docs for an example implementation. +`RangeCalendar` also supports custom calendar systems that implement custom business rules, for example a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar. See the [@internationalized/date docs](react-aria:internationalized/date/Calendar#custom-calendars) for an example implementation. ```tsx render "use client"; @@ -155,8 +154,7 @@ Use the `minValue` and `maxValue` props to set the valid date range. The `isDate ```tsx render docs={docs.exports.RangeCalendar} links={docs.links} props={['allowsNonContiguousRanges']} wide type="s2" "use client"; import {today, getLocalTimeZone} from '@internationalized/date'; -import {useLocale} from 'react-aria'; -import {RangeCalendar} from '@react-spectrum/s2'; +import {RangeCalendar, useLocale} from '@react-spectrum/s2'; import {useState} from 'react'; function Example(props) { diff --git a/packages/dev/s2-docs/pages/s2/TableView.mdx b/packages/dev/s2-docs/pages/s2/TableView.mdx index 1225eebf580..3e024ad7414 100644 --- a/packages/dev/s2-docs/pages/s2/TableView.mdx +++ b/packages/dev/s2-docs/pages/s2/TableView.mdx @@ -135,9 +135,8 @@ Use the `loadingState` and `onLoadMore` props to enable async loading and infini ```tsx render type="s2" "use client"; -import {TableView, TableHeader, Column, TableBody, Row, Cell} from '@react-spectrum/s2'; +import {TableView, TableHeader, Column, TableBody, Row, Cell, useAsyncList} from '@react-spectrum/s2'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; -import {useAsyncList} from 'react-stately'; interface Character { name: string; @@ -294,7 +293,7 @@ function TableWithDividers() { return ( + styles={style({width: 400, maxWidth: 'full'})}> {(column) => ( + {(column) => ( ` instead of a `` to allow users to edit the value. Editing is triggered by an `` and occurs in a popover that can contain one or more inputs. + +```tsx render type="s2" +"use client"; +import {TableView, TableHeader, Column, TableBody, Row, Cell, EditableCell, TextField, ActionButton, Picker, PickerItem, Text, type Key} from '@react-spectrum/s2'; +import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; +import User from '@react-spectrum/s2/icons/User'; +import Edit from '@react-spectrum/s2/icons/Edit'; +import {useCallback} from 'react'; +import {useListData} from '@react-stately/data'; + +///- begin collapse -/// +let defaultItems = [ + {id: 1, fruits: 'Apples', task: 'Collect', farmer: 'Eva'}, + {id: 2, fruits: 'Oranges', task: 'Collect', farmer: 'Steven'}, + {id: 3, fruits: 'Pears', task: 'Collect', farmer: 'Michael'}, + {id: 4, fruits: 'Cherries', task: 'Collect', farmer: 'Sara'}, + {id: 5, fruits: 'Dates', task: 'Collect', farmer: 'Karina'}, + {id: 6, fruits: 'Bananas', task: 'Collect', farmer: 'Otto'}, + {id: 7, fruits: 'Melons', task: 'Collect', farmer: 'Matt'}, + {id: 8, fruits: 'Figs', task: 'Collect', farmer: 'Emily'}, + {id: 9, fruits: 'Blueberries', task: 'Collect', farmer: 'Amelia'}, + {id: 10, fruits: 'Blackberries', task: 'Collect', farmer: 'Isla'} +]; +///- end collapse -/// + +///- begin collapse -/// +let editableColumns: Array & {name: string}> = [ + {name: 'Fruits', id: 'fruits', isRowHeader: true, width: '2fr', minWidth: 200}, + {name: 'Task', id: 'task', width: '1fr', minWidth: 150}, + {name: 'Farmer', id: 'farmer', width: '1fr', minWidth: 150} +]; +///- end collapse -/// + +export default function EditableTable(props) { + let columns = editableColumns; + let data = useListData({initialItems: defaultItems}); + + let onChange = useCallback((id: Key, columnId: Key, values: any) => { + let value = values[columnId]; + if (value == null) { + return; + } + data.update(id, (prevItem) => ({...prevItem, [columnId]: value})); + }, [data]); + + return ( + + + {(column) => ( + {column.name} + )} + + + {item => ( + + {(column) => { + if (column.id === 'fruits') { + ///- begin highlight -/// + return ( + { + e.preventDefault(); + let formData = new FormData(e.target as HTMLFormElement); + let values = Object.fromEntries(formData.entries()); + onChange(item.id, column.id!, values); + }} + renderEditing={() => ( + value.length > 0 ? null : 'Fruit name is required'} + styles={style({flexGrow: 1, flexShrink: 1, minWidth: 0})} + defaultValue={item[column.id!]} + name={column.id! as string} /> + )}> +
+ {item[column.id]} + + + +
+
+ ); + ///- end highlight -/// + } + if (column.id === 'farmer') { + return ( + { + e.preventDefault(); + let formData = new FormData(e.target as HTMLFormElement); + let values = Object.fromEntries(formData.entries()); + onChange(item.id, column.id!, values); + }} + renderEditing={() => ( + + {item => ( + + + {item.farmer} + + )} + + )}> +
+ {item[column.id]} + +
+
+ ); + } + return {item[column.id!]}; + }} +
+ )} +
+
+ ); +} +``` + ## Selection and actions Use `selectionMode` to enable single or multiple selection, and `selectedKeys` (matching each row's `id`) to control the selected rows. Return an [ActionBar](ActionBar) from `renderActionBar` to handle bulk actions, and use `onAction` for row navigation. Disable rows with `isDisabled`. See the [selection guide](selection) for details. @@ -517,7 +652,7 @@ function SortableTable() { return ( + {/*- begin highlight -*/} @@ -629,7 +764,7 @@ export default function ResizableTable() { ///- begin highlight -/// onResize={setColumnWidths} ///- end highlight -/// - styles={style({width: 400})}> + styles={style({width: 400, maxWidth: 'full'})}> {column => ( & {name: string}> = [ - {name: 'Fruits', id: 'fruits', isRowHeader: true, width: '6fr', minWidth: 300}, - {name: 'Task', id: 'task', width: '2fr', minWidth: 100}, - {name: 'Farmer', id: 'farmer', width: '2fr', minWidth: 150} -]; -///- end collapse -/// - -export default function EditableTable(props) { - let columns = editableColumns; - let data = useListData({initialItems: defaultItems}); - - let onChange = useCallback((id: Key, columnId: Key, values: any) => { - let value = values[columnId]; - if (value == null) { - return; - } - data.update(id, (prevItem) => ({...prevItem, [columnId]: value})); - }, [data]); - - return ( - - - {(column) => ( - {column.name} - )} - - - {item => ( - - {(column) => { - if (column.id === 'fruits') { - ///- begin highlight -/// - return ( - { - e.preventDefault(); - let formData = new FormData(e.target as HTMLFormElement); - let values = Object.fromEntries(formData.entries()); - onChange(item.id, column.id!, values); - }} - renderEditing={() => ( - value.length > 0 ? null : 'Fruit name is required'} - styles={style({flexGrow: 1, flexShrink: 1, minWidth: 0})} - defaultValue={item[column.id!]} - name={column.id! as string} /> - )}> -
- {item[column.id]} - - - -
-
- ); - ///- end highlight -/// - } - if (column.id === 'farmer') { - ///- begin highlight -/// - return ( - { - e.preventDefault(); - let formData = new FormData(e.target as HTMLFormElement); - let values = Object.fromEntries(formData.entries()); - onChange(item.id, column.id!, values); - }} - renderEditing={() => ( - - - - Eva - - - - Steven - - - - Michael - - - - Sara - - - - Karina - - - - Otto - - - - Matt - - - - Emily - - - - Amelia - - - - Isla - - - )}> -
- {item[column.id]} - -
-
- ); - ///- end highlight -/// - } - return {item[column.id!]}; - }} -
- )} -
-
- ); -} -``` - ## API -```tsx links={{TableView: '#tableview', TableHeader: '#tableheader', Column: '#column', TableBody: '#tablebody', Row: '#row', Cell: '#cell'}} +```tsx links={{TableView: '#tableview', TableHeader: '#tableheader', Column: '#column', TableBody: '#tablebody', Row: '#row', Cell: '#cell', 'EditableCell': '#editablecell'}} @@ -872,6 +835,7 @@ export default function EditableTable(props) { + diff --git a/packages/dev/s2-docs/pages/s2/Tabs.mdx b/packages/dev/s2-docs/pages/s2/Tabs.mdx index 1250663e0e2..bb84f694fb3 100644 --- a/packages/dev/s2-docs/pages/s2/Tabs.mdx +++ b/packages/dev/s2-docs/pages/s2/Tabs.mdx @@ -62,6 +62,8 @@ import Settings from '@react-spectrum/s2/illustrations/gradient/generic1/GearSet import {ActionButton, ActionButtonGroup, Tabs, TabList, Tab, TabPanel, Collection} from '@react-spectrum/s2'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; import {useState} from 'react'; +import AddCircle from '@react-spectrum/s2/icons/AddCircle'; +import RemoveCircle from '@react-spectrum/s2/icons/RemoveCircle'; function Example() { let [tabs, setTabs] = useState([ @@ -98,8 +100,12 @@ function Example() { {item => {item.title}} - Add tab - Remove tab + + + + + +
{/*- begin highlight -*/} diff --git a/packages/dev/s2-docs/pages/s2/TagGroup.mdx b/packages/dev/s2-docs/pages/s2/TagGroup.mdx index 06bfaf288de..318e460c49d 100644 --- a/packages/dev/s2-docs/pages/s2/TagGroup.mdx +++ b/packages/dev/s2-docs/pages/s2/TagGroup.mdx @@ -42,8 +42,7 @@ import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; ```tsx render type="s2" "use client"; -import {TagGroup, Tag, TextField, Button} from '@react-spectrum/s2'; -import {useListData} from 'react-stately'; +import {TagGroup, Tag, TextField, Button, useListData} from '@react-spectrum/s2'; import {useState} from 'react'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; diff --git a/packages/dev/s2-docs/pages/s2/TimeField.mdx b/packages/dev/s2-docs/pages/s2/TimeField.mdx index 50ef4889477..9bad9790770 100644 --- a/packages/dev/s2-docs/pages/s2/TimeField.mdx +++ b/packages/dev/s2-docs/pages/s2/TimeField.mdx @@ -3,7 +3,6 @@ export default Layout; import {TimeField} from '@react-spectrum/s2'; import docs from 'docs:@react-spectrum/s2'; -import {getBaseUrl} from '../../src/pageUtils'; export const tags = ['date', 'input']; export const description = 'Allows a user to enter and edit time values using a keyboard.'; @@ -23,7 +22,7 @@ export const description = 'Allows a user to enter and edit time values using a ## Value -Use the `value` or `defaultValue` prop to set the time value, using objects in the @internationalized/date package. `TimeField` accepts plain Time, CalendarDateTime, or ZonedDateTime, but only displays the time. +Use the `value` or `defaultValue` prop to set the time value, using objects in the [@internationalized/date](react-aria:internationalized/date/) package. `TimeField` accepts plain [Time](react-aria:internationalized/date/Time), [CalendarDateTime](react-aria:internationalized/date/CalendarDateTime), or [ZonedDateTime](react-aria:internationalized/date/ZonedDateTime), but only displays the time. ```tsx render "use client"; diff --git a/packages/dev/s2-docs/pages/s2/TreeView.mdx b/packages/dev/s2-docs/pages/s2/TreeView.mdx index 699d49edf16..dad6b2065d2 100644 --- a/packages/dev/s2-docs/pages/s2/TreeView.mdx +++ b/packages/dev/s2-docs/pages/s2/TreeView.mdx @@ -146,9 +146,8 @@ Use [renderEmptyState](#empty-state) to display a spinner during initial load. T ```tsx render "use client"; -import {TreeView, TreeViewItem, TreeViewItemContent, TreeViewLoadMoreItem, Collection} from '@react-spectrum/s2'; +import {TreeView, TreeViewItem, TreeViewItemContent, TreeViewLoadMoreItem, Collection, useAsyncList} from '@react-spectrum/s2'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; -import {useAsyncList} from 'react-stately'; interface Character { name: string diff --git a/packages/dev/s2-docs/pages/s2/collections.mdx b/packages/dev/s2-docs/pages/s2/collections.mdx index fd3b8e61807..84a5d89af77 100644 --- a/packages/dev/s2-docs/pages/s2/collections.mdx +++ b/packages/dev/s2-docs/pages/s2/collections.mdx @@ -280,9 +280,8 @@ Many components support infinite scrolling via the `loadingState` and `onLoadMor ```tsx render type="s2" "use client"; -import {TableView, TableHeader, Column, TableBody, Row, Cell} from '@react-spectrum/s2'; +import {TableView, TableHeader, Column, TableBody, Row, Cell, useAsyncList} from '@react-spectrum/s2'; import {style} from '@react-spectrum/s2/style' with {type: 'macro'}; -import {useAsyncList} from 'react-stately'; interface Character { name: string diff --git a/packages/dev/s2-docs/pages/s2/Illustrations.mdx b/packages/dev/s2-docs/pages/s2/illustrations.mdx similarity index 93% rename from packages/dev/s2-docs/pages/s2/Illustrations.mdx rename to packages/dev/s2-docs/pages/s2/illustrations.mdx index 52f502bd93c..809f24cfb64 100644 --- a/packages/dev/s2-docs/pages/s2/Illustrations.mdx +++ b/packages/dev/s2-docs/pages/s2/illustrations.mdx @@ -48,7 +48,9 @@ Then, add it to your `.parcelrc`: Now you can import illustration SVGs using the `illustration:` [pipeline](https://parceljs.org/features/plugins/#named-pipelines): -`import Illustration from 'illustration:./path/to/Illustration.svg';` +```tsx +import Illustration from 'illustration:./path/to/Illustration.svg'; +``` Note that you must use the name `illustration` for the pipeline. @@ -59,4 +61,7 @@ The `@react-spectrum/s2-icon-builder` CLI tool can be used to pre-process a fold This outputs a folder of TSX files with names corresponding to the input SVG files. You may rename them as you wish. To use them in your application, import them like normal components. -`import Illustration from './path/to/destination/Illustration';` + +```tsx +import Illustration from './path/to/destination/Illustration'; +``` diff --git a/packages/dev/s2-docs/pages/s2/migrating.mdx b/packages/dev/s2-docs/pages/s2/migrating.mdx index d02a6a4c67d..7fbc8d02f2a 100644 --- a/packages/dev/s2-docs/pages/s2/migrating.mdx +++ b/packages/dev/s2-docs/pages/s2/migrating.mdx @@ -28,7 +28,7 @@ The following arguments are also available: For cases that the upgrade assistant doesn't handle automatically or where you'd rather upgrade some components manually, use the guide below. -Note that indicates that future changes will occur before the final release, and the current solution should be considered temporary. +Note that indicates that future changes are expected, and the current solution should be considered temporary. ## Components diff --git a/packages/dev/s2-docs/pages/s2/testing.mdx b/packages/dev/s2-docs/pages/s2/testing.mdx index 5ddd86b5c45..5d7c06e9f31 100644 --- a/packages/dev/s2-docs/pages/s2/testing.mdx +++ b/packages/dev/s2-docs/pages/s2/testing.mdx @@ -11,7 +11,7 @@ export const description = 'Writing tests for apps built with React Spectrum'; # Testing -This page describes how to test an application built with React Spectrum. It documents the available testing utilities available for each aria pattern and how they can be used to simulate common user interactions. +Learn how to test an application built with React Spectrum using test utilities to simulate common user interactions. ## Testing semantics diff --git a/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs b/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs index 709f003f6ec..d04e6c34772 100644 --- a/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs +++ b/packages/dev/s2-docs/scripts/generateMarkdownDocs.mjs @@ -3,6 +3,7 @@ import * as babel from '@babel/parser'; import {fileURLToPath} from 'url'; import fs from 'fs'; +import {getBaseUrl} from '../src/pageUtils.ts'; import glob from 'fast-glob'; import path from 'path'; import {Project} from 'ts-morph'; @@ -1141,6 +1142,11 @@ function remarkDocsComponentsToMarkdown() { } } + if (href && (href.startsWith('s2:') || href.startsWith('react-aria:'))) { + let url = new URL(href); + href = getBaseUrl(url.protocol.slice(0, -1)) + '/' + url.pathname; + } + // Convert .html links to .md for relative links if (href && !href.startsWith('http') && !href.startsWith('//') && href.endsWith('.html')) { href = href.replace(/\.html$/, '.md'); diff --git a/packages/dev/s2-docs/src/CodeBlock.tsx b/packages/dev/s2-docs/src/CodeBlock.tsx index 01cb3ba2cfe..7f50e756042 100644 --- a/packages/dev/s2-docs/src/CodeBlock.tsx +++ b/packages/dev/s2-docs/src/CodeBlock.tsx @@ -161,7 +161,14 @@ function TruncatedCode({children, maxLines = 6, ...props}: TruncatedCodeProps) { ) : ( -
+
         {children}
       
diff --git a/packages/dev/s2-docs/src/CodeFold.tsx b/packages/dev/s2-docs/src/CodeFold.tsx index 731b34b79ba..4c515d71287 100644 --- a/packages/dev/s2-docs/src/CodeFold.tsx +++ b/packages/dev/s2-docs/src/CodeFold.tsx @@ -93,6 +93,7 @@ export function CodeFold({tokens}) { } let index = c.lastIndexOf('\n'); if (index === 0) { + lastLine.unshift(c); break; } else if (index > 0) { lastLine.unshift(c.slice(index)); diff --git a/packages/dev/s2-docs/src/ComponentCardView.tsx b/packages/dev/s2-docs/src/ComponentCardView.tsx index d8b106d5500..07eec383ff6 100644 --- a/packages/dev/s2-docs/src/ComponentCardView.tsx +++ b/packages/dev/s2-docs/src/ComponentCardView.tsx @@ -34,6 +34,7 @@ export function ComponentCardView({items, ariaLabel = 'Items', size = 'S', curre linkBehavior="override" autoFocus={!!currentUrl} selectedKeys={currentUrl ? [currentUrl] : []} + disallowEmptySelection onAction={onAction} className={style({ display: { diff --git a/packages/dev/s2-docs/src/Layout.tsx b/packages/dev/s2-docs/src/Layout.tsx index 883eb40983a..2f1ac72d42a 100644 --- a/packages/dev/s2-docs/src/Layout.tsx +++ b/packages/dev/s2-docs/src/Layout.tsx @@ -233,7 +233,7 @@ export async function Layout(props: PageProps & {children: ReactElement}) { } })}>
- } /> + } />