From 0f5324a7e71f811fe298e395484fd954b06196f5 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 17 Nov 2025 13:15:57 -0600 Subject: [PATCH 1/5] refactor: apply refobjects-default codemod --- packages/react/src/ActionBar/ActionBar.tsx | 6 +- packages/react/src/ActionList/List.tsx | 2 +- .../src/AnchoredOverlay/AnchoredOverlay.tsx | 4 +- .../Autocomplete.features.stories.tsx | 17 +- .../src/Autocomplete/AutocompleteOverlay.tsx | 8 +- .../react/src/AvatarStack/AvatarStack.tsx | 2 +- .../react/src/ButtonGroup/ButtonGroup.tsx | 2 +- packages/react/src/Checkbox/Checkbox.tsx | 4 +- .../ConfirmationDialog/ConfirmationDialog.tsx | 6 +- packages/react/src/Dialog/Dialog.tsx | 10 +- .../FilteredActionList/FilteredActionList.tsx | 4 +- .../FilteredActionList/useAnnouncements.tsx | 6 +- packages/react/src/LabelGroup/LabelGroup.tsx | 6 +- packages/react/src/Overlay/Overlay.tsx | 6 +- .../react/src/Overlay/Overlay.types.test.tsx | 10 +- packages/react/src/PageHeader/PageHeader.tsx | 4 +- packages/react/src/PageLayout/PageLayout.tsx | 12 +- packages/react/src/Popover/Popover.tsx | 4 +- packages/react/src/Radio/Radio.tsx | 2 +- .../react/src/SelectPanel/SelectPanel.tsx | 4 +- packages/react/src/Skeleton/SkeletonBox.tsx | 8 +- packages/react/src/TabNav/TabNav.tsx | 6 +- packages/react/src/TextInput/TextInput.tsx | 2 +- .../TextInputWithTokens.tsx | 8 +- packages/react/src/TooltipV2/Tooltip.tsx | 4 +- .../react/src/TreeView/useRovingTabIndex.ts | 4 +- packages/react/src/TreeView/useTypeahead.ts | 2 +- .../react/src/UnderlineNav/UnderlineNav.tsx | 4 +- .../src/UnderlineNav/UnderlineNavItem.tsx | 2 +- .../react/src/deprecated/DialogV1/Dialog.tsx | 4 +- .../experimental/SelectPanel2/SelectPanel.tsx | 8 +- packages/react/src/experimental/Tabs/Tabs.tsx | 4 +- .../react/src/hooks/useAnchoredPosition.ts | 10 +- packages/react/src/hooks/useDetails.tsx | 2 +- packages/react/src/hooks/useDialog.ts | 10 +- packages/react/src/hooks/useFocusTrap.ts | 8 +- packages/react/src/hooks/useFocusZone.ts | 6 +- .../react/src/hooks/useMenuInitialFocus.ts | 4 +- .../src/hooks/useMenuKeyboardNavigation.ts | 14 +- packages/react/src/hooks/useMnemonics.ts | 2 +- .../react/src/hooks/useOnOutsideClick.tsx | 4 +- .../react/src/hooks/useOpenAndCloseFocus.ts | 6 +- packages/react/src/hooks/useOverflow.ts | 2 +- packages/react/src/hooks/useOverlay.tsx | 10 +- .../react/src/hooks/useProvidedRefOrCreate.ts | 2 +- .../src/hooks/useRefObjectAsForwardedRef.ts | 2 +- .../react/src/hooks/useRenderForcingRef.ts | 2 +- packages/react/src/hooks/useResizeObserver.ts | 2 +- packages/react/src/hooks/useScrollFlash.ts | 2 +- packages/react/src/internal/hooks/useFocus.ts | 2 +- .../stories/useAnchoredPosition.stories.tsx | 82 ++- .../src/stories/useFocusTrap.stories.tsx | 282 +++++----- .../src/stories/useFocusZone.stories.tsx | 488 +++++++++--------- 53 files changed, 544 insertions(+), 573 deletions(-) diff --git a/packages/react/src/ActionBar/ActionBar.tsx b/packages/react/src/ActionBar/ActionBar.tsx index 7f2745ef218..5481974ef02 100644 --- a/packages/react/src/ActionBar/ActionBar.tsx +++ b/packages/react/src/ActionBar/ActionBar.tsx @@ -329,7 +329,7 @@ export const ActionBar: React.FC> = prop const newMenuItemIds = getMenuItems(navWidth, moreMenuWidth, childRegistry, hasActiveMenu, computedGap) if (newMenuItemIds) setMenuItemIds(newMenuItemIds) } - }, navRef as RefObject) + }, navRef as RefObject) const isVisibleChild = useCallback( (id: string) => { @@ -554,7 +554,7 @@ const ActionBarGroupContext = React.createContext<{ export const ActionBarGroup = forwardRef(({children}: React.PropsWithChildren, forwardedRef) => { const backupRef = useRef(null) - const ref = (forwardedRef ?? backupRef) as RefObject + const ref = (forwardedRef ?? backupRef) as RefObject const id = useId() const {registerChild, unregisterChild} = React.useContext(ActionBarContext) @@ -586,7 +586,7 @@ export const ActionBarGroup = forwardRef(({children}: React.PropsWithChildren, f export const ActionBarMenu = forwardRef( ({'aria-label': ariaLabel, icon, overflowIcon, items, ...props}: ActionBarMenuProps, forwardedRef) => { const backupRef = useRef(null) - const ref = (forwardedRef ?? backupRef) as RefObject + const ref = (forwardedRef ?? backupRef) as RefObject const id = useId() const {registerChild, unregisterChild, isVisibleChild} = React.useContext(ActionBarContext) diff --git a/packages/react/src/ActionList/List.tsx b/packages/react/src/ActionList/List.tsx index 0546d85abca..f629c80d7ba 100644 --- a/packages/react/src/ActionList/List.tsx +++ b/packages/react/src/ActionList/List.tsx @@ -41,7 +41,7 @@ const UnwrappedList = ( const ariaLabelledBy = slots.heading ? (slots.heading.props.id ?? headingId) : listLabelledBy const listRole = role || listRoleFromContainer - const listRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) + const listRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) let enableFocusZone = false if (enableFocusZoneFromContainer !== undefined) enableFocusZone = enableFocusZoneFromContainer diff --git a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx index ffc1ad10064..c0cd18f64ef 100644 --- a/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx +++ b/packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx @@ -27,7 +27,7 @@ interface AnchoredOverlayPropsWithAnchor { /** * An override to the internal ref that will be spread on to the renderAnchor */ - anchorRef?: React.RefObject + anchorRef?: React.RefObject /** * An override to the internal id that will be spread on to the renderAnchor @@ -46,7 +46,7 @@ interface AnchoredOverlayPropsWithoutAnchor { * An override to the internal renderAnchor ref that will be used to position the overlay. * When renderAnchor is null this can be used to make an anchor that is detached from ActionMenu. */ - anchorRef: React.RefObject + anchorRef: React.RefObject /** * An override to the internal id that will be spread on to the renderAnchor */ diff --git a/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx b/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx index 55fa35d8389..93ff44c1907 100644 --- a/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx +++ b/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx @@ -457,12 +457,12 @@ export const CustomOverlayMenuAnchor = () => { const menuAnchorRef = useRef(null) return ( -
+ ( Default label -
} className={classes.AnchorContainer}> +
} className={classes.AnchorContainer}> { The overlay menu position is anchored to the div with the black border instead of to the text input - - ) + ) + ); } export const InOverlayWithCustomScrollContainerRef = () => { @@ -504,8 +504,7 @@ export const InOverlayWithCustomScrollContainerRef = () => { } return ( -
- Selected item: {selectedItem ? selectedItem.text : 'none'} + (Selected item: {selectedItem ? selectedItem.text : 'none'} {
-
} className={classes.OverlayScroll}> +
} className={classes.OverlayScroll}> {
- - ) + ) + ); } export const InADialog = () => { diff --git a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx index d72494a7545..aa83ed23a7f 100644 --- a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx +++ b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx @@ -14,7 +14,7 @@ type AutocompleteOverlayInternalProps = { /** * The ref of the element that the position of the menu is based on. By default, the menu is positioned based on the text input */ - menuAnchorRef?: React.RefObject + menuAnchorRef?: React.RefObject /** * Props to be spread on the internal `Overlay` component. */ @@ -60,7 +60,7 @@ function AutocompleteOverlay({ preventFocusOnOpen={true} onClickOutside={closeOptionList} onEscape={closeOptionList} - ref={floatingElementRef as React.RefObject} + ref={floatingElementRef as React.RefObject} top={position?.top} left={position?.left} className={classes.Overlay} @@ -71,8 +71,8 @@ function AutocompleteOverlay({ ) : ( // HACK: This ensures AutocompleteMenu is still mounted when closing the menu and all of the hooks inside of it are still called. // A better way to do this would be to move the hooks to AutocompleteOverlay or somewhere that won't get unmounted. - - ) + () + ); } AutocompleteOverlay.displayName = 'AutocompleteOverlay' diff --git a/packages/react/src/AvatarStack/AvatarStack.tsx b/packages/react/src/AvatarStack/AvatarStack.tsx index 9661161a24c..2b62bbab72b 100644 --- a/packages/react/src/AvatarStack/AvatarStack.tsx +++ b/packages/react/src/AvatarStack/AvatarStack.tsx @@ -38,7 +38,7 @@ const AvatarStackBody = ({ }: { disableExpand: boolean | undefined hasInteractiveChildren: boolean | undefined - stackContainer: React.RefObject + stackContainer: React.RefObject } & React.ComponentPropsWithoutRef<'div'>) => { return (
{child}
) - const buttonRef = useProvidedRefOrCreate(forwardRef as React.RefObject) + const buttonRef = useProvidedRefOrCreate(forwardRef as React.RefObject) useFocusZone({ containerRef: buttonRef, diff --git a/packages/react/src/Checkbox/Checkbox.tsx b/packages/react/src/Checkbox/Checkbox.tsx index d54c372cd80..70a02ab2a5e 100644 --- a/packages/react/src/Checkbox/Checkbox.tsx +++ b/packages/react/src/Checkbox/Checkbox.tsx @@ -20,7 +20,7 @@ export type CheckboxProps = { /** * Forward a ref to the underlying input element */ - ref?: React.RefObject + ref?: React.RefObject /** * Indicates whether the checkbox must be checked */ @@ -44,7 +44,7 @@ const Checkbox = React.forwardRef( {checked, className, defaultChecked, indeterminate, disabled, onChange, required, validationStatus, value, ...rest}, ref, ): ReactElement => { - const checkboxRef = useProvidedRefOrCreate(ref as React.RefObject) + const checkboxRef = useProvidedRefOrCreate(ref as React.RefObject) const checkboxGroupContext = useContext(CheckboxGroupContext) const handleOnChange: ChangeEventHandler = e => { checkboxGroupContext.onChange && checkboxGroupContext.onChange(e) diff --git a/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx b/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx index e6e1cb6ffe3..4ec03dff67d 100644 --- a/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx +++ b/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx @@ -108,10 +108,10 @@ const ConfirmationFooter: React.FC> = ({foo // Must have exactly 2 buttons! return ( -
} className={classes.ConfirmationFooter}> + (
} className={classes.ConfirmationFooter}> -
- ) +
) + ); } /** diff --git a/packages/react/src/Dialog/Dialog.tsx b/packages/react/src/Dialog/Dialog.tsx index 301bc80304c..bb778b78b7c 100644 --- a/packages/react/src/Dialog/Dialog.tsx +++ b/packages/react/src/Dialog/Dialog.tsx @@ -44,7 +44,7 @@ export type DialogButtonProps = Omit & { * A reference to the rendered Button’s DOM node, used together with * `autoFocus` for `focusTrap`’s `initialFocus`. */ - ref?: React.RefObject + ref?: React.RefObject } /** @@ -136,12 +136,12 @@ export interface DialogProps { * Return focus to this element when the Dialog closes, * instead of the element that had focus immediately before the Dialog opened */ - returnFocusRef?: React.RefObject + returnFocusRef?: React.RefObject /** * The element to focus when the Dialog opens */ - initialFocusRef?: React.RefObject + initialFocusRef?: React.RefObject /** * Additional class names to apply to the dialog @@ -215,10 +215,10 @@ const DefaultFooter: React.FC> = ({footerBu focusInStrategy: 'closest', }) return footerButtons ? ( - }> + }> - ) : null + ) : null; } const defaultPosition = { diff --git a/packages/react/src/FilteredActionList/FilteredActionList.tsx b/packages/react/src/FilteredActionList/FilteredActionList.tsx index 4c2b59b2cc2..413867d5377 100644 --- a/packages/react/src/FilteredActionList/FilteredActionList.tsx +++ b/packages/react/src/FilteredActionList/FilteredActionList.tsx @@ -32,9 +32,9 @@ export interface FilteredActionListProps extends Partial | null) => void onListContainerRefChanged?: (ref: HTMLElement | null) => void - onInputRefChanged?: (ref: React.RefObject) => void + onInputRefChanged?: (ref: React.RefObject) => void textInputProps?: Partial> - inputRef?: React.RefObject + inputRef?: React.RefObject message?: React.ReactNode messageText?: { title: string diff --git a/packages/react/src/FilteredActionList/useAnnouncements.tsx b/packages/react/src/FilteredActionList/useAnnouncements.tsx index c3026078eba..dcc3a03062a 100644 --- a/packages/react/src/FilteredActionList/useAnnouncements.tsx +++ b/packages/react/src/FilteredActionList/useAnnouncements.tsx @@ -19,7 +19,7 @@ const useFirstRender = () => { } const getItemWithActiveDescendant = ( - listRef: React.RefObject, + listRef: React.RefObject, items: FilteredActionListProps['items'], ) => { const listElement = listRef.current @@ -40,8 +40,8 @@ const getItemWithActiveDescendant = ( export const useAnnouncements = ( items: FilteredActionListProps['items'], - listContainerRef: React.RefObject, - inputRef: React.RefObject, + listContainerRef: React.RefObject, + inputRef: React.RefObject, enabled: boolean = true, loading: boolean = false, message?: {title: string; description: string}, diff --git a/packages/react/src/LabelGroup/LabelGroup.tsx b/packages/react/src/LabelGroup/LabelGroup.tsx index 2c59e38b841..37625fa91ab 100644 --- a/packages/react/src/LabelGroup/LabelGroup.tsx +++ b/packages/react/src/LabelGroup/LabelGroup.tsx @@ -19,12 +19,12 @@ export type LabelGroupProps = { // Calculates the width of the overlay to cover the labels/tokens and the expand button. const getOverlayWidth = ( buttonClientRect: DOMRect, - containerRef: React.RefObject, + containerRef: React.RefObject, overlayPaddingPx: number, ) => overlayPaddingPx + buttonClientRect.right - (containerRef.current?.getBoundingClientRect().left || 0) const InlineToggle: React.FC<{ - collapseButtonRef: React.RefObject + collapseButtonRef: React.RefObject collapseInlineExpandedChildren: () => void expandButtonRef: React.RefCallback hiddenItemIds: string[] @@ -81,7 +81,7 @@ const OverlayToggle: React.FC< align="start" side="inside-right" // expandButtonRef satisfies React.RefObject because we manually set `.current` in the `useCallback` above - anchorRef={expandButtonRef as unknown as React.RefObject} + anchorRef={expandButtonRef as unknown as React.RefObject} anchorOffset={overlayPaddingPx * -1} alignmentOffset={overlayPaddingPx * -1} renderAnchor={props => ( diff --git a/packages/react/src/Overlay/Overlay.tsx b/packages/react/src/Overlay/Overlay.tsx index 399078d4e37..b3f3ad7d0f6 100644 --- a/packages/react/src/Overlay/Overlay.tsx +++ b/packages/react/src/Overlay/Overlay.tsx @@ -137,14 +137,14 @@ export const BaseOverlay = React.forwardRef( type ContainerProps = { anchorSide?: AnchorSide - ignoreClickRefs?: React.RefObject[] - initialFocusRef?: React.RefObject + ignoreClickRefs?: React.RefObject[] + initialFocusRef?: React.RefObject onClickOutside: (e: TouchOrMouseEvent) => void onEscape: (e: KeyboardEvent) => void portalContainerName?: string preventOverflow?: boolean preventFocusOnOpen?: boolean - returnFocusRef: React.RefObject + returnFocusRef: React.RefObject } type internalOverlayProps = Merge diff --git a/packages/react/src/Overlay/Overlay.types.test.tsx b/packages/react/src/Overlay/Overlay.types.test.tsx index 613438c8c25..210d9589c2c 100644 --- a/packages/react/src/Overlay/Overlay.types.test.tsx +++ b/packages/react/src/Overlay/Overlay.types.test.tsx @@ -1,26 +1,26 @@ import type React from 'react' import Overlay from './Overlay' -export function shouldAcceptCallWithNoProps(ref: React.RefObject) { +export function shouldAcceptCallWithNoProps(ref: React.RefObject) { return null} onEscape={() => null} /> } -export function shouldAcceptCallWithDOMProps(ref: React.RefObject) { +export function shouldAcceptCallWithDOMProps(ref: React.RefObject) { return null} onEscape={() => null} onMouseDown={() => null} /> } -export function shouldNotAcceptCallWithDOMPropsThatDontMatchElement(ref: React.RefObject) { +export function shouldNotAcceptCallWithDOMPropsThatDontMatchElement(ref: React.RefObject) { // @ts-expect-error href should not be allowed on a
return null} onEscape={() => null} href="//primer.style/" /> } -export function shouldAcceptCallWithAsAndDOMProps(ref: React.RefObject) { +export function shouldAcceptCallWithAsAndDOMProps(ref: React.RefObject) { return ( null} onEscape={() => null} href="//primer.style/" /> ) } -export function shouldNotAcceptSystemProps(ref: React.RefObject) { +export function shouldNotAcceptSystemProps(ref: React.RefObject) { return ( >( ({children, className, as: BaseComponent = 'div', 'aria-label': ariaLabel, role, hasBorder}, forwardedRef) => { - const rootRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) + const rootRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) const isInteractive = (element: HTMLElement) => { return ( @@ -205,7 +205,7 @@ export type TitleAreaProps = { const TitleArea = React.forwardRef>( ({children, className, hidden = false, variant = 'medium'}, forwardedRef) => { - const titleAreaRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) + const titleAreaRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) return (
+ paneRef: React.RefObject }>({ padding: 'normal', rowGap: 'normal', @@ -260,7 +260,7 @@ const VerticalDivider: React.FC {draggable ? ( // Drag handle -
+ />) ) : null} -
- ) +
) + ); } // ---------------------------------------------------------------------------- diff --git a/packages/react/src/Popover/Popover.tsx b/packages/react/src/Popover/Popover.tsx index 6eb3a0d9fea..a6a61d2a9cc 100644 --- a/packages/react/src/Popover/Popover.tsx +++ b/packages/react/src/Popover/Popover.tsx @@ -5,7 +5,7 @@ import React, {useRef} from 'react' import {useOnOutsideClick} from '../hooks' // Stable empty array reference to avoid unnecessary re-renders -const EMPTY_IGNORE_CLICK_REFS: React.RefObject[] = [] +const EMPTY_IGNORE_CLICK_REFS: React.RefObject[] = [] type CaretPosition = | 'top' @@ -62,7 +62,7 @@ export type PopoverContentProps = { /* * Refs to elements that should be ignored when detecting outside clicks */ - ignoreClickRefs?: React.RefObject[] + ignoreClickRefs?: React.RefObject[] } & HTMLProps const PopoverContent: React.FC> = ({ diff --git a/packages/react/src/Radio/Radio.tsx b/packages/react/src/Radio/Radio.tsx index 16a98b94a53..59ed4846be5 100644 --- a/packages/react/src/Radio/Radio.tsx +++ b/packages/react/src/Radio/Radio.tsx @@ -27,7 +27,7 @@ export type RadioProps = { /** * Forward a ref to the underlying input element */ - ref?: React.RefObject + ref?: React.RefObject /** * Indicates whether the radio button must be checked before the form can be submitted */ diff --git a/packages/react/src/SelectPanel/SelectPanel.tsx b/packages/react/src/SelectPanel/SelectPanel.tsx index cd99fc00bc2..d407a771fca 100644 --- a/packages/react/src/SelectPanel/SelectPanel.tsx +++ b/packages/react/src/SelectPanel/SelectPanel.tsx @@ -198,7 +198,7 @@ function Panel({ const loadingDelayTimeoutId = useRef(null) const loadingManagedInternally = loading === undefined const loadingManagedExternally = !loadingManagedInternally - const [inputRef, setInputRef] = React.useState | null>(null) + const [inputRef, setInputRef] = React.useState | null>(null) const [listContainerElement, setListContainerElement] = useState(null) const [needsNoItemsAnnouncement, setNeedsNoItemsAnnouncement] = useState(false) const isNarrowScreenSize = useResponsiveValue({narrow: true, regular: false, wide: false}, false) @@ -240,7 +240,7 @@ function Panel({ ) const onInputRefChanged = useCallback( - (ref: React.RefObject) => { + (ref: React.RefObject) => { setInputRef(ref) }, [setInputRef], diff --git a/packages/react/src/Skeleton/SkeletonBox.tsx b/packages/react/src/Skeleton/SkeletonBox.tsx index eb52dc7800b..b0ae116d9cc 100644 --- a/packages/react/src/Skeleton/SkeletonBox.tsx +++ b/packages/react/src/Skeleton/SkeletonBox.tsx @@ -17,11 +17,11 @@ export const SkeletonBox = React.forwardRef(funct ref, ) { return ( -
} + (
} className={clsx(className, classes.SkeletonBox)} style={{height, width, ...(style || {})}} {...props} - /> - ) + />) + ); }) diff --git a/packages/react/src/TabNav/TabNav.tsx b/packages/react/src/TabNav/TabNav.tsx index 876e130160f..eb1105774fc 100644 --- a/packages/react/src/TabNav/TabNav.tsx +++ b/packages/react/src/TabNav/TabNav.tsx @@ -51,14 +51,14 @@ function TabNav({children, 'aria-label': ariaLabel, ...rest}: TabNavProps) { ) return ( -
}> + (
}> -
- ) +
) + ); } /** diff --git a/packages/react/src/TextInput/TextInput.tsx b/packages/react/src/TextInput/TextInput.tsx index be52c1d5e2f..23353bc2807 100644 --- a/packages/react/src/TextInput/TextInput.tsx +++ b/packages/react/src/TextInput/TextInput.tsx @@ -90,7 +90,7 @@ const TextInput = React.forwardRef( ref, ) => { const [isInputFocused, setIsInputFocused] = useState(false) - const inputRef = useProvidedRefOrCreate(ref as React.RefObject) + const inputRef = useProvidedRefOrCreate(ref as React.RefObject) // this class is necessary to style FilterSearch, plz no touchy! const wrapperClasses = clsx(className, 'TextInput-wrapper') const showLeadingLoadingIndicator = diff --git a/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx b/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx index 03d90d0d25b..c705a5916b1 100644 --- a/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx +++ b/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx @@ -255,7 +255,7 @@ function TextInputWithTokensInnerComponent : LeadingVisual}
} + ref={containerRef as RefObject} className={styles.Container} data-prevent-token-wrapping={preventTokenWrapping} > @@ -327,8 +327,8 @@ function TextInputWithTokensInnerComponent {typeof TrailingVisual !== 'string' && isValidElementType(TrailingVisual) ? : TrailingVisual} - - ) + ) + ); } const TextInputWithTokens = React.forwardRef(TextInputWithTokensInnerComponent) diff --git a/packages/react/src/TooltipV2/Tooltip.tsx b/packages/react/src/TooltipV2/Tooltip.tsx index 3fba58ec114..1fea93f22cd 100644 --- a/packages/react/src/TooltipV2/Tooltip.tsx +++ b/packages/react/src/TooltipV2/Tooltip.tsx @@ -50,7 +50,7 @@ type TriggerPropsType = Pick< | 'onTouchCancel' | 'onTouchEnd' > & { - ref?: React.RefObject + ref?: React.RefObject } // map tooltip direction to anchoredPosition props @@ -124,7 +124,7 @@ export const Tooltip: ForwardRefExoticComponent< ) => { const tooltipId = useId(id) const child = Children.only(children) - const triggerRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) + const triggerRef = useProvidedRefOrCreate(forwardedRef as React.RefObject) const tooltipElRef = useRef(null) const [calculatedDirection, setCalculatedDirection] = useState(direction) diff --git a/packages/react/src/TreeView/useRovingTabIndex.ts b/packages/react/src/TreeView/useRovingTabIndex.ts index ff6af01870d..4e5abd3a20b 100644 --- a/packages/react/src/TreeView/useRovingTabIndex.ts +++ b/packages/react/src/TreeView/useRovingTabIndex.ts @@ -6,8 +6,8 @@ export function useRovingTabIndex({ containerRef, mouseDownRef, }: { - containerRef: React.RefObject - mouseDownRef: React.RefObject + containerRef: React.RefObject + mouseDownRef: React.RefObject }) { // TODO: Initialize focus to the aria-current item if it exists useFocusZone({ diff --git a/packages/react/src/TreeView/useTypeahead.ts b/packages/react/src/TreeView/useTypeahead.ts index cfd5f35f6d4..a43844eb0f6 100644 --- a/packages/react/src/TreeView/useTypeahead.ts +++ b/packages/react/src/TreeView/useTypeahead.ts @@ -3,7 +3,7 @@ import useSafeTimeout from '../hooks/useSafeTimeout' import {getAccessibleName} from './shared' type TypeaheadOptions = { - containerRef: React.RefObject + containerRef: React.RefObject onFocusChange: (element: Element) => void } diff --git a/packages/react/src/UnderlineNav/UnderlineNav.tsx b/packages/react/src/UnderlineNav/UnderlineNav.tsx index 2f6386c9a47..25621a57168 100644 --- a/packages/react/src/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNav.tsx @@ -151,7 +151,7 @@ export const UnderlineNav = forwardRef( forwardedRef, ) => { const backupRef = useRef(null) - const navRef = (forwardedRef ?? backupRef) as RefObject + const navRef = (forwardedRef ?? backupRef) as RefObject const listRef = useRef(null) const moreMenuRef = useRef(null) const moreMenuBtnRef = useRef(null) @@ -298,7 +298,7 @@ export const UnderlineNav = forwardRef( noIconChildWidthArray, updateListAndMenu, ) - }, navRef as RefObject) + }, navRef as RefObject) // Compute menuInlineStyles if needed let menuInlineStyles: React.CSSProperties = {...baseMenuInlineStyles} diff --git a/packages/react/src/UnderlineNav/UnderlineNavItem.tsx b/packages/react/src/UnderlineNav/UnderlineNavItem.tsx index 963f7c033f1..55cbef21af7 100644 --- a/packages/react/src/UnderlineNav/UnderlineNavItem.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNavItem.tsx @@ -53,7 +53,7 @@ export const UnderlineNavItem = forwardRef( forwardedRef, ) => { const backupRef = useRef(null) - const ref = (forwardedRef ?? backupRef) as RefObject + const ref = (forwardedRef ?? backupRef) as RefObject const {setChildrenWidth, setNoIconChildrenWidth, loadingCounters, iconsVisible} = useContext(UnderlineNavContext) useLayoutEffect(() => { diff --git a/packages/react/src/deprecated/DialogV1/Dialog.tsx b/packages/react/src/deprecated/DialogV1/Dialog.tsx index b39af5aa2da..caad7a4ab2c 100644 --- a/packages/react/src/deprecated/DialogV1/Dialog.tsx +++ b/packages/react/src/deprecated/DialogV1/Dialog.tsx @@ -35,8 +35,8 @@ function DialogHeader({children, className, as: Component = 'div', ...rest}: Dia type InternalDialogProps = { isOpen?: boolean onDismiss?: () => void - initialFocusRef?: React.RefObject - returnFocusRef?: React.RefObject + initialFocusRef?: React.RefObject + returnFocusRef?: React.RefObject as?: React.ElementType } & StyledDialogBaseProps & HTMLAttributes diff --git a/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx b/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx index f1fd2fcc16b..0b7290d6659 100644 --- a/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx +++ b/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx @@ -63,7 +63,7 @@ export type SelectPanelProps = { defaultOpen?: boolean open?: boolean - anchorRef?: React.RefObject + anchorRef?: React.RefObject anchoredPositionSettings?: Partial onCancel?: () => void @@ -521,10 +521,10 @@ const SecondaryLink: React.FC = ({className, ...props}) => { return ( // @ts-ignore TODO: is as prop is not recognised by button? - - ) + ) + ); } const SecondaryCheckbox: React.FC = ({id, children, className, ...props}) => { diff --git a/packages/react/src/experimental/Tabs/Tabs.tsx b/packages/react/src/experimental/Tabs/Tabs.tsx index 71fe02c0e4b..37331637910 100644 --- a/packages/react/src/experimental/Tabs/Tabs.tsx +++ b/packages/react/src/experimental/Tabs/Tabs.tsx @@ -105,7 +105,7 @@ type TabListProps = Labelled & React.HTMLAttributes function useTabList( props: TabListProps & { /** Optional ref to use for the tablist. If none is provided, one will be generated automatically */ - ref?: React.RefObject + ref?: React.RefObject }, ): { /** Props to be spread onto the tablist element */ @@ -114,7 +114,7 @@ function useTabList( 'aria-orientation': AriaAttributes['aria-orientation'] 'aria-label': AriaAttributes['aria-label'] 'aria-labelledby': AriaAttributes['aria-labelledby'] - ref: React.RefObject + ref: React.RefObject role: 'tablist' } } { diff --git a/packages/react/src/hooks/useAnchoredPosition.ts b/packages/react/src/hooks/useAnchoredPosition.ts index e4e54f61c51..32777aad1d7 100644 --- a/packages/react/src/hooks/useAnchoredPosition.ts +++ b/packages/react/src/hooks/useAnchoredPosition.ts @@ -6,8 +6,8 @@ import {useResizeObserver} from './useResizeObserver' import useLayoutEffect from '../utils/useIsomorphicLayoutEffect' export interface AnchoredPositionHookSettings extends Partial { - floatingElementRef?: React.RefObject - anchorElementRef?: React.RefObject + floatingElementRef?: React.RefObject + anchorElementRef?: React.RefObject pinPosition?: boolean onPositionChange?: (position: AnchorPosition | undefined) => void } @@ -25,8 +25,8 @@ export function useAnchoredPosition( settings?: AnchoredPositionHookSettings, dependencies: React.DependencyList = [], ): { - floatingElementRef: React.RefObject - anchorElementRef: React.RefObject + floatingElementRef: React.RefObject + anchorElementRef: React.RefObject position: AnchorPosition | undefined } { const floatingElementRef = useProvidedRefOrCreate(settings?.floatingElementRef) @@ -98,7 +98,7 @@ export function useAnchoredPosition( useLayoutEffect(updatePosition, [updatePosition]) useResizeObserver(updatePosition) // watches for changes in window size - useResizeObserver(updatePosition, floatingElementRef as React.RefObject) // watches for changes in floating element size + useResizeObserver(updatePosition, floatingElementRef as React.RefObject) // watches for changes in floating element size return { floatingElementRef, diff --git a/packages/react/src/hooks/useDetails.tsx b/packages/react/src/hooks/useDetails.tsx index 17e1daf6b87..fc6a3d5dd8c 100644 --- a/packages/react/src/hooks/useDetails.tsx +++ b/packages/react/src/hooks/useDetails.tsx @@ -1,7 +1,7 @@ import {useCallback, useEffect, useState, useRef} from 'react' type UseDetailsParameters = { - ref?: React.RefObject + ref?: React.RefObject closeOnOutsideClick?: boolean defaultOpen?: boolean onClickOutside?: (event: MouseEvent) => void diff --git a/packages/react/src/hooks/useDialog.ts b/packages/react/src/hooks/useDialog.ts index f744d3aa500..4a91b3c56c9 100644 --- a/packages/react/src/hooks/useDialog.ts +++ b/packages/react/src/hooks/useDialog.ts @@ -13,13 +13,13 @@ function focusable(el: Element) { } type UseDialogParameters = { - modalRef: React.RefObject - overlayRef: React.RefObject + modalRef: React.RefObject + overlayRef: React.RefObject isOpen?: boolean onDismiss?: () => void - initialFocusRef?: React.RefObject - closeButtonRef?: React.RefObject - returnFocusRef?: React.RefObject + initialFocusRef?: React.RefObject + closeButtonRef?: React.RefObject + returnFocusRef?: React.RefObject } function useDialog({ diff --git a/packages/react/src/hooks/useFocusTrap.ts b/packages/react/src/hooks/useFocusTrap.ts index 62fbc68e636..ab599d7fb92 100644 --- a/packages/react/src/hooks/useFocusTrap.ts +++ b/packages/react/src/hooks/useFocusTrap.ts @@ -7,13 +7,13 @@ export interface FocusTrapHookSettings { * Ref that will be used for the trapping container. If not provided, one will * be created by this hook and returned. */ - containerRef?: React.RefObject + containerRef?: React.RefObject /** * Ref for the element that should receive focus when the focus trap is first enabled. If * not provided, one will be created by this hook and returned. Its use is optional. */ - initialFocusRef?: React.RefObject + initialFocusRef?: React.RefObject /** * Set to true to disable the focus trap and clean up listeners. Can be re-enabled at @@ -33,7 +33,7 @@ export interface FocusTrapHookSettings { * * Overrides restoreFocusOnCleanUp */ - returnFocusRef?: React.RefObject + returnFocusRef?: React.RefObject } /** @@ -44,7 +44,7 @@ export interface FocusTrapHookSettings { export function useFocusTrap( settings?: FocusTrapHookSettings, dependencies: React.DependencyList = [], -): {containerRef: React.RefObject; initialFocusRef: React.RefObject} { +): {containerRef: React.RefObject; initialFocusRef: React.RefObject} { const containerRef = useProvidedRefOrCreate(settings?.containerRef) const initialFocusRef = useProvidedRefOrCreate(settings?.initialFocusRef) const disabled = settings?.disabled diff --git a/packages/react/src/hooks/useFocusZone.ts b/packages/react/src/hooks/useFocusZone.ts index 71b49a01708..6f8a1900181 100644 --- a/packages/react/src/hooks/useFocusZone.ts +++ b/packages/react/src/hooks/useFocusZone.ts @@ -10,13 +10,13 @@ export interface FocusZoneHookSettings extends Omit + containerRef?: React.RefObject /** * If using the "active descendant" focus pattern, pass `true` or a ref to the controlling * element. If a ref object is not passed, we will create one for you. */ - activeDescendantFocus?: boolean | React.RefObject + activeDescendantFocus?: boolean | React.RefObject /** * Set to true to disable the focus zone and clean up listeners. Can be re-enabled at @@ -28,7 +28,7 @@ export interface FocusZoneHookSettings extends Omit; activeDescendantControlRef: React.RefObject} { +): {containerRef: React.RefObject; activeDescendantControlRef: React.RefObject} { const containerRef = useProvidedRefOrCreate(settings.containerRef) const useActiveDescendant = !!settings.activeDescendantFocus const passedActiveDescendantRef = diff --git a/packages/react/src/hooks/useMenuInitialFocus.ts b/packages/react/src/hooks/useMenuInitialFocus.ts index 3897796d8a0..20e0af08292 100644 --- a/packages/react/src/hooks/useMenuInitialFocus.ts +++ b/packages/react/src/hooks/useMenuInitialFocus.ts @@ -3,8 +3,8 @@ import {iterateFocusableElements} from '@primer/behaviors/utils' export const useMenuInitialFocus = ( open: boolean, - containerRef?: React.RefObject, - anchorRef?: React.RefObject, + containerRef?: React.RefObject, + anchorRef?: React.RefObject, ) => { /** * We need to pick the first element to focus based on how the menu was opened, diff --git a/packages/react/src/hooks/useMenuKeyboardNavigation.ts b/packages/react/src/hooks/useMenuKeyboardNavigation.ts index 96150189354..16192f2e228 100644 --- a/packages/react/src/hooks/useMenuKeyboardNavigation.ts +++ b/packages/react/src/hooks/useMenuKeyboardNavigation.ts @@ -14,8 +14,8 @@ import type {MenuCloseHandler} from '../ActionMenu' export const useMenuKeyboardNavigation = ( open: boolean, onClose: MenuCloseHandler | undefined, - containerRef?: React.RefObject, - anchorRef?: React.RefObject, + containerRef?: React.RefObject, + anchorRef?: React.RefObject, isSubmenu: boolean = false, ) => { useMenuInitialFocus(open, containerRef, anchorRef) @@ -32,8 +32,8 @@ export const useMenuKeyboardNavigation = ( const useCloseMenuOnTab = ( open: boolean, onClose: MenuCloseHandler | undefined, - containerRef?: React.RefObject, - anchorRef?: React.RefObject, + containerRef?: React.RefObject, + anchorRef?: React.RefObject, ) => { React.useEffect(() => { const container = containerRef?.current @@ -59,7 +59,7 @@ const useCloseSubmenuOnArrow = ( open: boolean, isSubmenu: boolean, onClose: MenuCloseHandler | undefined, - containerRef?: React.RefObject, + containerRef?: React.RefObject, ) => { React.useEffect(() => { const container = containerRef?.current @@ -81,8 +81,8 @@ const useCloseSubmenuOnArrow = ( */ const useMoveFocusToMenuItem = ( open: boolean, - containerRef?: React.RefObject, - anchorRef?: React.RefObject, + containerRef?: React.RefObject, + anchorRef?: React.RefObject, ) => { React.useEffect(() => { const container = containerRef?.current diff --git a/packages/react/src/hooks/useMnemonics.ts b/packages/react/src/hooks/useMnemonics.ts index 7c417a3c267..7f79e67001d 100644 --- a/packages/react/src/hooks/useMnemonics.ts +++ b/packages/react/src/hooks/useMnemonics.ts @@ -8,7 +8,7 @@ import {useProvidedRefOrCreate} from './useProvidedRefOrCreate' * typically appearing in a menu title, menu item, or the text of a button. */ -export const useMnemonics = (open: boolean, providedRef?: React.RefObject) => { +export const useMnemonics = (open: boolean, providedRef?: React.RefObject) => { const containerRef = useProvidedRefOrCreate(providedRef) React.useEffect( diff --git a/packages/react/src/hooks/useOnOutsideClick.tsx b/packages/react/src/hooks/useOnOutsideClick.tsx index 36dab73bdf9..7bbf341aa71 100644 --- a/packages/react/src/hooks/useOnOutsideClick.tsx +++ b/packages/react/src/hooks/useOnOutsideClick.tsx @@ -5,8 +5,8 @@ export type TouchOrMouseEvent = MouseEvent | TouchEvent type TouchOrMouseEventCallback = (event: TouchOrMouseEvent) => boolean | undefined export type UseOnOutsideClickSettings = { - containerRef: React.RefObject | React.RefObject - ignoreClickRefs?: React.RefObject[] + containerRef: React.RefObject | React.RefObject + ignoreClickRefs?: React.RefObject[] onClickOutside: (e: TouchOrMouseEvent) => void } diff --git a/packages/react/src/hooks/useOpenAndCloseFocus.ts b/packages/react/src/hooks/useOpenAndCloseFocus.ts index 06cccfe0f2f..fd804b30b7b 100644 --- a/packages/react/src/hooks/useOpenAndCloseFocus.ts +++ b/packages/react/src/hooks/useOpenAndCloseFocus.ts @@ -3,9 +3,9 @@ import {useEffect} from 'react' import {iterateFocusableElements} from '@primer/behaviors/utils' export type UseOpenAndCloseFocusSettings = { - initialFocusRef?: React.RefObject - containerRef: React.RefObject - returnFocusRef: React.RefObject + initialFocusRef?: React.RefObject + containerRef: React.RefObject + returnFocusRef: React.RefObject preventFocusOnOpen?: boolean } diff --git a/packages/react/src/hooks/useOverflow.ts b/packages/react/src/hooks/useOverflow.ts index 0c394d095e5..9443124fdd2 100644 --- a/packages/react/src/hooks/useOverflow.ts +++ b/packages/react/src/hooks/useOverflow.ts @@ -1,6 +1,6 @@ import {useEffect, useState} from 'react' -export function useOverflow(ref: React.RefObject) { +export function useOverflow(ref: React.RefObject) { const [hasOverflow, setHasOverflow] = useState(false) useEffect(() => { diff --git a/packages/react/src/hooks/useOverlay.tsx b/packages/react/src/hooks/useOverlay.tsx index 41fb3fc73a2..888c0dd002c 100644 --- a/packages/react/src/hooks/useOverlay.tsx +++ b/packages/react/src/hooks/useOverlay.tsx @@ -5,17 +5,17 @@ import {useOnEscapePress} from './useOnEscapePress' import {useProvidedRefOrCreate} from './useProvidedRefOrCreate' export type UseOverlaySettings = { - ignoreClickRefs?: React.RefObject[] - initialFocusRef?: React.RefObject - returnFocusRef: React.RefObject + ignoreClickRefs?: React.RefObject[] + initialFocusRef?: React.RefObject + returnFocusRef: React.RefObject onEscape: (e: KeyboardEvent) => void onClickOutside: (e: TouchOrMouseEvent) => void - overlayRef?: React.RefObject + overlayRef?: React.RefObject preventFocusOnOpen?: boolean } export type OverlayReturnProps = { - ref: React.RefObject + ref: React.RefObject } export const useOverlay = ({ diff --git a/packages/react/src/hooks/useProvidedRefOrCreate.ts b/packages/react/src/hooks/useProvidedRefOrCreate.ts index b9fa481232d..bfd209cbde2 100644 --- a/packages/react/src/hooks/useProvidedRefOrCreate.ts +++ b/packages/react/src/hooks/useProvidedRefOrCreate.ts @@ -8,7 +8,7 @@ import React from 'react' * @param providedRef The ref to use - if undefined, will use the ref from a call to React.useRef * @type TRef The type of the RefObject which should be created. */ -export function useProvidedRefOrCreate(providedRef?: React.RefObject): React.RefObject { +export function useProvidedRefOrCreate(providedRef?: React.RefObject): React.RefObject { const createdRef = React.useRef(null) return providedRef ?? createdRef } diff --git a/packages/react/src/hooks/useRefObjectAsForwardedRef.ts b/packages/react/src/hooks/useRefObjectAsForwardedRef.ts index ccaa0e8e048..11becf147d7 100644 --- a/packages/react/src/hooks/useRefObjectAsForwardedRef.ts +++ b/packages/react/src/hooks/useRefObjectAsForwardedRef.ts @@ -8,6 +8,6 @@ import {useImperativeHandle} from 'react' * * **NOTE**: The `refObject` should be passed to the underlying element, NOT the `forwardedRef`. */ -export function useRefObjectAsForwardedRef(forwardedRef: ForwardedRef, refObject: RefObject): void { +export function useRefObjectAsForwardedRef(forwardedRef: ForwardedRef, refObject: RefObject): void { useImperativeHandle(forwardedRef, () => refObject.current) } diff --git a/packages/react/src/hooks/useRenderForcingRef.ts b/packages/react/src/hooks/useRenderForcingRef.ts index a3e86d015e2..57405893c54 100644 --- a/packages/react/src/hooks/useRenderForcingRef.ts +++ b/packages/react/src/hooks/useRenderForcingRef.ts @@ -23,5 +23,5 @@ export function useRenderForcingRef(value?: TRef) { ref.current = refCurrent }, [refCurrent]) - return [ref as RefObject, setRef] as const + return [ref as RefObject, setRef] as const; } diff --git a/packages/react/src/hooks/useResizeObserver.ts b/packages/react/src/hooks/useResizeObserver.ts index c170fa5478e..704673af082 100644 --- a/packages/react/src/hooks/useResizeObserver.ts +++ b/packages/react/src/hooks/useResizeObserver.ts @@ -11,7 +11,7 @@ export interface ResizeObserverEntry { export function useResizeObserver( callback: ResizeObserverCallback, - target?: RefObject, + target?: RefObject, depsArray: unknown[] = [], ) { const [targetClientRect, setTargetClientRect] = useState(null) diff --git a/packages/react/src/hooks/useScrollFlash.ts b/packages/react/src/hooks/useScrollFlash.ts index ad8e211d42d..a22658826a6 100644 --- a/packages/react/src/hooks/useScrollFlash.ts +++ b/packages/react/src/hooks/useScrollFlash.ts @@ -4,7 +4,7 @@ import {useEffect} from 'react' * This hook will flash the scrollbars for a ref of a container that has scrollable overflow * @param scrollContainerRef The ref of the scrollable content */ -export default function useScrollFlash(scrollContainerRef: React.RefObject) { +export default function useScrollFlash(scrollContainerRef: React.RefObject) { // https://adxlv.computer/projects/flash-scrollers/ useEffect(() => { const scrollContainer = scrollContainerRef.current diff --git a/packages/react/src/internal/hooks/useFocus.ts b/packages/react/src/internal/hooks/useFocus.ts index 5ca0f4598c1..41f19988767 100644 --- a/packages/react/src/internal/hooks/useFocus.ts +++ b/packages/react/src/internal/hooks/useFocus.ts @@ -1,7 +1,7 @@ import {type RefObject, useEffect, useState} from 'react' export function useFocus() { - const [focusTarget, setFocusTarget] = useState | null>(null) + const [focusTarget, setFocusTarget] = useState | null>(null) useEffect(() => { if (focusTarget === null) { diff --git a/packages/react/src/stories/useAnchoredPosition.stories.tsx b/packages/react/src/stories/useAnchoredPosition.stories.tsx index b312d8a5348..2b98d569e44 100644 --- a/packages/react/src/stories/useAnchoredPosition.stories.tsx +++ b/packages/react/src/stories/useAnchoredPosition.stories.tsx @@ -131,13 +131,13 @@ export const UseAnchoredPosition = (args: any) => { ) return ( -
+ (
} + ref={anchorElementRef as React.RefObject} > Anchor Element @@ -146,12 +146,12 @@ export const UseAnchoredPosition = (args: any) => { left={position?.left ?? 0} width={args.floatWidth ?? 150} height={args.floatHeight ?? 150} - ref={floatingElementRef as React.RefObject} + ref={floatingElementRef as React.RefObject} > Floating element -
- ) +
) + ); } export const CenteredOnScreen = () => { const {floatingElementRef, anchorElementRef, position} = useAnchoredPosition({ @@ -160,9 +160,9 @@ export const CenteredOnScreen = () => { }) // The outer Position element simply fills all available space return ( -
}> + (
}> } + ref={floatingElementRef as React.RefObject} top={position?.top ?? 0} left={position?.left ?? 0} > @@ -173,8 +173,8 @@ export const CenteredOnScreen = () => {

-
- ) +
) + ); } export const ComplexAncestry = () => { @@ -191,37 +191,35 @@ export const ComplexAncestry = () => { }, [recalculateSignal]) // The outer Position element simply fills all available space - return ( - <> -
- Clipping container - this element has overflow set to something other than visible -
- Relatively positioned parent, but fluid height, so not the clipping parent. -
- Floating element container. Position=static and overflow=hidden to show that overflow-hidden on a - statically-positioned element will not have any effect. - } - > - Floating element - -
+ return (<> +
+ Clipping container - this element has overflow set to something other than visible +
+ Relatively positioned parent, but fluid height, so not the clipping parent. +
+ Floating element container. Position=static and overflow=hidden to show that overflow-hidden on a + statically-positioned element will not have any effect. + } + > + Floating element +
-
- Anchor element container. This element is really tall to demonstrate behavior within a scrollable clipping - container. -
}> - Anchor Element -
+
+
+ Anchor element container. This element is really tall to demonstrate behavior within a scrollable clipping + container. +
}> + Anchor Element
- - - ) +
+ + ); } const Nav = ({children, ...props}: React.ComponentPropsWithoutRef<'nav'>) => ( @@ -279,7 +277,7 @@ export const WithPortal = () => { }, [showMenu]) return ( -
+ (
- ) +
) + ); } diff --git a/packages/react/src/stories/useFocusTrap.stories.tsx b/packages/react/src/stories/useFocusTrap.stories.tsx index 6df61bdf5e6..b8304884bf7 100644 --- a/packages/react/src/stories/useFocusTrap.stories.tsx +++ b/packages/react/src/stories/useFocusTrap.stories.tsx @@ -52,27 +52,25 @@ export const FocusTrap = () => { } }, [spaceListener]) - return ( - <> - - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - Durian - Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
- - ) + return (<> + + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + Durian + Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
+ ); } export const RestoreFocus = () => { @@ -95,27 +93,25 @@ export const RestoreFocus = () => { } }, [spaceListener]) - return ( - <> - - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - Durian - Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
- - ) + return (<> + + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + Durian + Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
+ ); } export const CustomInitialFocus = () => { @@ -138,32 +134,30 @@ export const CustomInitialFocus = () => { } }, [spaceListener]) - return ( - <> - - - - This story is the same as the `Focus Trap` story, except, when the trap zone is activated, the - “Elderberry” button will receive the initial focus (if the trap zone container does not already - have focus). - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - Durian - }>Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
- - ) + return (<> + + + + This story is the same as the `Focus Trap` story, except, when the trap zone is activated, the + “Elderberry” button will receive the initial focus (if the trap zone container does not already + have focus). + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + Durian + }>Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
+ ); } function useKeyPressListener(key: string, handler: () => void, capture = false) { @@ -210,27 +204,25 @@ export const DynamicFocusTrapContents = () => { useCallback(() => setTrapEnabled(!trapEnabled), [trapEnabled]), ) - return ( - <> - - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - - - - -
- Grapefruit - Honeydew - Jackfruit -
- - ) + return (<> + + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + + + + +
+ Grapefruit + Honeydew + Jackfruit +
+ ); } export const MultipleFocusTraps = () => { @@ -259,61 +251,59 @@ export const MultipleFocusTraps = () => { } }, [keyListener]) - return ( - <> - - - - This story demonstrates the global nature of focus traps. When a focus trap is enabled, if there is already an - active focus trap, it becomes suspended and pushed onto a stack. Once the newly-active focus trap is disabled, - the most recently-suspended trap will reactivate. Suspended focus traps can be disabled, causing them to be - removed from the stack of suspended traps. - -
- Legend - -
- - Inactive -
- -
- - Suspended -
- -
- - Active -
-
- Apple - Banana - Cantaloupe -
}> - - Trap zone ({trapEnabled1 ? 'enabled' : 'disabled'})! Press 1 to toggle. - - - Durian - Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
}> - - Trap zone ({trapEnabled2 ? 'enabled' : 'disabled'})! Press 2 to toggle. - - - Kiwi - Lemon - Mango - -
- Nectarine - Orange - Peach -
- - ) + return (<> + + + + This story demonstrates the global nature of focus traps. When a focus trap is enabled, if there is already an + active focus trap, it becomes suspended and pushed onto a stack. Once the newly-active focus trap is disabled, + the most recently-suspended trap will reactivate. Suspended focus traps can be disabled, causing them to be + removed from the stack of suspended traps. + +
+ Legend + +
+ - Inactive +
+ +
+ - Suspended +
+ +
+ - Active +
+
+ Apple + Banana + Cantaloupe +
}> + + Trap zone ({trapEnabled1 ? 'enabled' : 'disabled'})! Press 1 to toggle. + + + Durian + Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
}> + + Trap zone ({trapEnabled2 ? 'enabled' : 'disabled'})! Press 2 to toggle. + + + Kiwi + Lemon + Mango + +
+ Nectarine + Orange + Peach +
+ ); } diff --git a/packages/react/src/stories/useFocusZone.stories.tsx b/packages/react/src/stories/useFocusZone.stories.tsx index d602d164e06..07da92ee74a 100644 --- a/packages/react/src/stories/useFocusZone.stories.tsx +++ b/packages/react/src/stories/useFocusZone.stories.tsx @@ -32,30 +32,28 @@ export const BasicFocusZone = () => { setFzEnabled(!fzEnabled) }, [fzEnabled]) - return ( - <> -
-
Last key pressed: {lastKey}
- - Apple - Banana - Cantaloupe -
}> - Use Up Arrow, Down Arrow, Home, and End to move focus within this box. -
- Durian - Elderberry - Fig -
+ return (<> +
+
Last key pressed: {lastKey}
+ + Apple + Banana + Cantaloupe +
}> + Use Up Arrow, Down Arrow, Home, and End to move focus within this box. +
+ Durian + Elderberry + Fig
- Kiwi - Lemon - Mango
- - ) + Kiwi + Lemon + Mango +
+ ); } export const FocusOutBehavior = () => { @@ -74,39 +72,37 @@ export const FocusOutBehavior = () => { bindKeys: FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd, }) - return ( - <> -
-
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - - Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus stops at the ends. - + return (<> +
+
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + + Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus stops at the ends. + -
- Durian - Elderberry - Fig -
+
+ Durian + Elderberry + Fig
-
}> - Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus is circular. +
+
}> + Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus is circular. -
- Grapefruit - Honeydew - Jackfruit -
+
+ Grapefruit + Honeydew + Jackfruit
- Kiwi - Lemon - Mango
- - ) + Kiwi + Lemon + Mango +
+ ); } function getSiblingIndex(element: Element) { @@ -173,31 +169,29 @@ export const CustomFocusMovement = () => { useFocusZone({containerRef, getNextFocusable}) - return ( - <> -
-
Last key pressed: {lastKey}
- Apple - -
- Use arrow keys to move focus within this box. -
}> - Banana - Cantaloupe - Durian - Elderberry - Fig - Grapefruit - Honeydew - Jackfruit - Kiwi -
+ return (<> +
+
Last key pressed: {lastKey}
+ Apple + +
+ Use arrow keys to move focus within this box. +
}> + Banana + Cantaloupe + Durian + Elderberry + Fig + Grapefruit + Honeydew + Jackfruit + Kiwi
- Lemon - Mango
- - ) + Lemon + Mango +
+ ); } export const FocusInStrategy = () => { @@ -236,50 +230,48 @@ export const FocusInStrategy = () => { focusInStrategy: customStrategy, }) - return ( - <> -
-
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - “First” strategy (focus first focusable element) -
- Banana - Cantaloupe - Durian -
+ return (<> +
+
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + “First” strategy (focus first focusable element) +
+ Banana + Cantaloupe + Durian
-
}> - “Closest” strategy (focus first or last depending on focus direction) -
- Elderberry - Fig - Grapefruit -
+
+
}> + “Closest” strategy (focus first or last depending on focus direction) +
+ Elderberry + Fig + Grapefruit
-
}> - “Previous” strategy (most recently focused element) -
- Honeydew - Jackfruit - Kiwi -
+
+
}> + “Previous” strategy (most recently focused element) +
+ Honeydew + Jackfruit + Kiwi
-
}> - “Custom” strategy (choose randomly for this example) -
- Lemon - Mango - Nectarine -
+
+
}> + “Custom” strategy (choose randomly for this example) +
+ Lemon + Mango + Nectarine
- Orange - Papaya
- - ) + Orange + Papaya +
+ ); } export const SpecialSituations = () => { @@ -300,67 +292,65 @@ export const SpecialSituations = () => { }) const {containerRef: hContainerRef} = useFocusZone({focusOutBehavior: 'wrap', bindKeys: FocusKeys.ArrowHorizontal}) - return ( - <> -
- - This story is very esoteric! It only exists to show some of the nuance of the arrow key focus behavior in - different situations. Focus treatment within your component should be evaluated for your particular UX using - the{' '} - - ARIA guidelines - - . - -
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - Bound keys: Up, Down, PageUp, PageDown, W, S, J, K, Home, End, Tab -
- - - Regular button - - -
+ return (<> +
+ + This story is very esoteric! It only exists to show some of the nuance of the arrow key focus behavior in + different situations. Focus treatment within your component should be evaluated for your particular UX using + the{' '} + + ARIA guidelines + + . + +
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + Bound keys: Up, Down, PageUp, PageDown, W, S, J, K, Home, End, Tab +
+ + + Regular button + +
-
}> - - -
- Grapefruit - - Jackfruit -
+
+
}> + + +
+ Grapefruit + + Jackfruit
- Kiwi - Lemon - Mango
- - ) + Kiwi + Lemon + Mango +
+ ); } export const ChangingSubtree = () => { @@ -390,27 +380,25 @@ export const ChangingSubtree = () => { ) } - return ( - <> -
- - This story demonstrates that focusZone is consistent even when the container’s subtree changes. - -
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - Bound keys: Arrow Up and Arrow Down -
{buttons}
-
-
- Remove Button - Add Button -
+ return (<> +
+ + This story demonstrates that focusZone is consistent even when the container’s subtree changes. + +
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + Bound keys: Arrow Up and Arrow Down +
{buttons}
- - ) +
+ Remove Button + Add Button +
+
+ ); } export const NestedZones = () => { @@ -433,32 +421,30 @@ export const NestedZones = () => { bindKeys: FocusKeys.ArrowHorizontal, }) - return ( - <> -
-
Last key pressed: {lastKey}
- Apple - Banana -
}> - Bound keys: Arrow Up and Arrow Down -
- Cantaloupe -
}> - Additional Bound keys: Arrow Left and Arrow Right -
- Durian - Elderberry - Fig - Grapefruit -
+ return (<> +
+
Last key pressed: {lastKey}
+ Apple + Banana +
}> + Bound keys: Arrow Up and Arrow Down +
+ Cantaloupe +
}> + Additional Bound keys: Arrow Left and Arrow Right +
+ Durian + Elderberry + Fig + Grapefruit
- Honeydew
- Jackfruit - Kiwi + Honeydew
- - ) + Jackfruit + Kiwi +
+ ); } export const ActiveDescendant = () => { @@ -486,46 +472,44 @@ export const ActiveDescendant = () => { focusableElementFilter: elem => elem instanceof HTMLButtonElement, }) - return ( - <> -
- - This story demonstrates using the `aria-activedescendant` pattern for managing both a focused element and an - active element. Below, you can focus the input box then use the up/down arrow keys to change the active - descendant (dark blue outline). - -
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
- -
- } - type="text" - defaultValue="Focus remains here." - aria-controls="list" - id="focus-input" - /> -
} - > - Durian - Elderberry - Fig - Grapefruit -
+ return (<> +
+ + This story demonstrates using the `aria-activedescendant` pattern for managing both a focused element and an + active element. Below, you can focus the input box then use the up/down arrow keys to change the active + descendant (dark blue outline). + +
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
+ +
+ } + type="text" + defaultValue="Focus remains here." + aria-controls="list" + id="focus-input" + /> +
} + > + Durian + Elderberry + Fig + Grapefruit
- Honeydew - Jackfruit - Kiwi
- - ) + Honeydew + Jackfruit + Kiwi +
+ ); } From e4fbddc8238a3142a384c9ea89d0fc7cc7db2eb9 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 17 Nov 2025 13:16:17 -0600 Subject: [PATCH 2/5] feat: add patch-package for @types/react support --- package-lock.json | 201 +++++++++++++++++++++++++++++ package.json | 2 + patches/@types+react+18.3.11.patch | 13 ++ 3 files changed, 216 insertions(+) create mode 100644 patches/@types+react+18.3.11.patch diff --git a/package-lock.json b/package-lock.json index cf4a50dec61..a8f3d4cb6ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "name": "primer", + "hasInstallScript": true, "license": "MIT", "workspaces": [ "packages/rollup-plugin-import-css", @@ -53,6 +54,7 @@ "globals": "^16.2.0", "markdownlint-cli2": "^0.17.2", "markdownlint-cli2-formatter-pretty": "^0.0.8", + "patch-package": "^8.0.1", "prettier": "3.4.2", "rimraf": "5.0.5", "size-limit": "11.2.0", @@ -10004,6 +10006,13 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/abbrev": { "version": "2.0.0", "dev": true, @@ -14693,6 +14702,16 @@ "node": ">=8" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/flatted": { "version": "3.3.3", "dev": true, @@ -16397,11 +16416,38 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", + "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "dev": true, "license": "MIT" }, + "node_modules/json-stable-stringify/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "dev": true, @@ -16426,6 +16472,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "dev": true, @@ -16479,6 +16535,16 @@ "node": ">=0.10.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "3.0.3", "dev": true, @@ -19022,6 +19088,131 @@ "node": ">= 0.8" } }, + "node_modules/patch-package": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^10.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.2.4", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/patch-package/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/patch-package/node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/patch-package/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/path-browserify": { "version": "1.0.1", "dev": true, @@ -24598,6 +24789,16 @@ "node": ">=14.0.0" } }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "dev": true, diff --git a/package.json b/package.json index ad3d4244a05..82eb97ed6fe 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "lint:css:fix": "npm run lint:css -- --fix", "lint:fix": "npm run lint -- --fix", "lint:md": "markdownlint-cli2 \"**/*.{md,mdx}\" \"!.github\" \"!.changeset\" \"!**/node_modules/**\" \"!**/CHANGELOG.md\"", + "postinstall": "patch-package", "test": "vitest", "test:type-check": "tsc --noEmit", "test:update": "npm run test -- -u", @@ -81,6 +82,7 @@ "globals": "^16.2.0", "markdownlint-cli2": "^0.17.2", "markdownlint-cli2-formatter-pretty": "^0.0.8", + "patch-package": "^8.0.1", "prettier": "3.4.2", "rimraf": "5.0.5", "size-limit": "11.2.0", diff --git a/patches/@types+react+18.3.11.patch b/patches/@types+react+18.3.11.patch new file mode 100644 index 00000000000..498ebf1606c --- /dev/null +++ b/patches/@types+react+18.3.11.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/@types/react/index.d.ts b/node_modules/@types/react/index.d.ts +index 45b3c12..7284073 100644 +--- a/node_modules/@types/react/index.d.ts ++++ b/node_modules/@types/react/index.d.ts +@@ -186,7 +186,7 @@ declare namespace React { + * @see {@link RefObject} + */ + +- type Ref = RefCallback | RefObject | null; ++ type Ref = RefCallback | RefObject | null; + /** + * A legacy implementation of refs where you can pass a string to a ref prop. + * From 1784f1df73290f13f34a7bd01e306fd6e8c6e54f Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 17 Nov 2025 13:17:51 -0600 Subject: [PATCH 3/5] chore: add changeset --- .changeset/wicked-buttons-wink.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/wicked-buttons-wink.md diff --git a/.changeset/wicked-buttons-wink.md b/.changeset/wicked-buttons-wink.md new file mode 100644 index 00000000000..e278ac60661 --- /dev/null +++ b/.changeset/wicked-buttons-wink.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +Add support for `ref` types from React 19 by annotating `React.RefObject` types as `React.RefObject` From 308b95cf63e051819bbca983f702e6e42b3f8caa Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 17 Nov 2025 13:21:09 -0600 Subject: [PATCH 4/5] chore: run format --- packages/react/src/ActionBar/ActionBar.tsx | 23 +- .../Autocomplete.features.stories.tsx | 13 +- .../src/Autocomplete/AutocompleteOverlay.tsx | 4 +- .../ConfirmationDialog/ConfirmationDialog.tsx | 6 +- packages/react/src/Dialog/Dialog.tsx | 2 +- packages/react/src/PageLayout/PageLayout.tsx | 10 +- packages/react/src/Skeleton/SkeletonBox.tsx | 6 +- packages/react/src/TabNav/TabNav.tsx | 6 +- .../TextInputWithTokens.tsx | 6 +- .../react/src/UnderlineNav/UnderlineNav.tsx | 29 +- .../experimental/SelectPanel2/SelectPanel.tsx | 6 +- packages/react/src/hooks/useFocusZone.ts | 5 +- .../react/src/hooks/useRenderForcingRef.ts | 2 +- .../stories/useAnchoredPosition.stories.tsx | 78 +-- .../src/stories/useFocusTrap.stories.tsx | 282 +++++----- .../src/stories/useFocusZone.stories.tsx | 488 +++++++++--------- 16 files changed, 504 insertions(+), 462 deletions(-) diff --git a/packages/react/src/ActionBar/ActionBar.tsx b/packages/react/src/ActionBar/ActionBar.tsx index 5481974ef02..7fc7801ca7d 100644 --- a/packages/react/src/ActionBar/ActionBar.tsx +++ b/packages/react/src/ActionBar/ActionBar.tsx @@ -320,16 +320,19 @@ export const ActionBar: React.FC> = prop const moreMenuBtnRef = useRef(null) const containerRef = React.useRef(null) - useResizeObserver((resizeObserverEntries: ResizeObserverEntry[]) => { - const navWidth = resizeObserverEntries[0].contentRect.width - const moreMenuWidth = moreMenuRef.current?.getBoundingClientRect().width ?? 0 - const hasActiveMenu = menuItemIds.size > 0 - - if (navWidth > 0) { - const newMenuItemIds = getMenuItems(navWidth, moreMenuWidth, childRegistry, hasActiveMenu, computedGap) - if (newMenuItemIds) setMenuItemIds(newMenuItemIds) - } - }, navRef as RefObject) + useResizeObserver( + (resizeObserverEntries: ResizeObserverEntry[]) => { + const navWidth = resizeObserverEntries[0].contentRect.width + const moreMenuWidth = moreMenuRef.current?.getBoundingClientRect().width ?? 0 + const hasActiveMenu = menuItemIds.size > 0 + + if (navWidth > 0) { + const newMenuItemIds = getMenuItems(navWidth, moreMenuWidth, childRegistry, hasActiveMenu, computedGap) + if (newMenuItemIds) setMenuItemIds(newMenuItemIds) + } + }, + navRef as RefObject, + ) const isVisibleChild = useCallback( (id: string) => { diff --git a/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx b/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx index 93ff44c1907..52ee15dcb2f 100644 --- a/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx +++ b/packages/react/src/Autocomplete/Autocomplete.features.stories.tsx @@ -457,7 +457,7 @@ export const CustomOverlayMenuAnchor = () => { const menuAnchorRef = useRef(null) return ( - (
+ Default label @@ -478,8 +478,8 @@ export const CustomOverlayMenuAnchor = () => { The overlay menu position is anchored to the div with the black border instead of to the text input -
) - ); + + ) } export const InOverlayWithCustomScrollContainerRef = () => { @@ -504,7 +504,8 @@ export const InOverlayWithCustomScrollContainerRef = () => { } return ( - (
Selected item: {selectedItem ? selectedItem.text : 'none'} + + Selected item: {selectedItem ? selectedItem.text : 'none'} {
- ) - ); + + ) } export const InADialog = () => { diff --git a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx index aa83ed23a7f..a5b9cb11317 100644 --- a/packages/react/src/Autocomplete/AutocompleteOverlay.tsx +++ b/packages/react/src/Autocomplete/AutocompleteOverlay.tsx @@ -71,8 +71,8 @@ function AutocompleteOverlay({ ) : ( // HACK: This ensures AutocompleteMenu is still mounted when closing the menu and all of the hooks inside of it are still called. // A better way to do this would be to move the hooks to AutocompleteOverlay or somewhere that won't get unmounted. - () - ); + + ) } AutocompleteOverlay.displayName = 'AutocompleteOverlay' diff --git a/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx b/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx index 4ec03dff67d..6fd39fe3335 100644 --- a/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx +++ b/packages/react/src/ConfirmationDialog/ConfirmationDialog.tsx @@ -108,10 +108,10 @@ const ConfirmationFooter: React.FC> = ({foo // Must have exactly 2 buttons! return ( - (
} className={classes.ConfirmationFooter}> +
} className={classes.ConfirmationFooter}> -
) - ); +
+ ) } /** diff --git a/packages/react/src/Dialog/Dialog.tsx b/packages/react/src/Dialog/Dialog.tsx index bb778b78b7c..1d2989ef080 100644 --- a/packages/react/src/Dialog/Dialog.tsx +++ b/packages/react/src/Dialog/Dialog.tsx @@ -218,7 +218,7 @@ const DefaultFooter: React.FC> = ({footerBu }> - ) : null; + ) : null } const defaultPosition = { diff --git a/packages/react/src/PageLayout/PageLayout.tsx b/packages/react/src/PageLayout/PageLayout.tsx index d140211428e..af2a04ff7c4 100644 --- a/packages/react/src/PageLayout/PageLayout.tsx +++ b/packages/react/src/PageLayout/PageLayout.tsx @@ -260,7 +260,7 @@ const VerticalDivider: React.FC {draggable ? ( // Drag handle - (
) + /> ) : null} -
) - ); +
+ ) } // ---------------------------------------------------------------------------- diff --git a/packages/react/src/Skeleton/SkeletonBox.tsx b/packages/react/src/Skeleton/SkeletonBox.tsx index b0ae116d9cc..8ed2d396566 100644 --- a/packages/react/src/Skeleton/SkeletonBox.tsx +++ b/packages/react/src/Skeleton/SkeletonBox.tsx @@ -17,11 +17,11 @@ export const SkeletonBox = React.forwardRef(funct ref, ) { return ( - (
} className={clsx(className, classes.SkeletonBox)} style={{height, width, ...(style || {})}} {...props} - />) - ); + /> + ) }) diff --git a/packages/react/src/TabNav/TabNav.tsx b/packages/react/src/TabNav/TabNav.tsx index eb1105774fc..a5db7c4fff4 100644 --- a/packages/react/src/TabNav/TabNav.tsx +++ b/packages/react/src/TabNav/TabNav.tsx @@ -51,14 +51,14 @@ function TabNav({children, 'aria-label': ariaLabel, ...rest}: TabNavProps) { ) return ( - (
}> +
}> -
) - ); +
+ ) } /** diff --git a/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx b/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx index c705a5916b1..b1b994f870d 100644 --- a/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx +++ b/packages/react/src/TextInputWithTokens/TextInputWithTokens.tsx @@ -255,7 +255,7 @@ function TextInputWithTokensInnerComponent {typeof TrailingVisual !== 'string' && isValidElementType(TrailingVisual) ? : TrailingVisual} - ) - ); + + ) } const TextInputWithTokens = React.forwardRef(TextInputWithTokensInnerComponent) diff --git a/packages/react/src/UnderlineNav/UnderlineNav.tsx b/packages/react/src/UnderlineNav/UnderlineNav.tsx index 25621a57168..36bace9fc22 100644 --- a/packages/react/src/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNav.tsx @@ -286,19 +286,22 @@ export const UnderlineNav = forwardRef( useOnOutsideClick({onClickOutside: closeOverlay, containerRef, ignoreClickRefs: [moreMenuBtnRef]}) - useResizeObserver((resizeObserverEntries: ResizeObserverEntry[]) => { - const navWidth = resizeObserverEntries[0].contentRect.width - const moreMenuWidth = moreMenuRef.current?.getBoundingClientRect().width ?? 0 - navWidth !== 0 && - overflowEffect( - navWidth, - moreMenuWidth, - validChildren, - childWidthArray, - noIconChildWidthArray, - updateListAndMenu, - ) - }, navRef as RefObject) + useResizeObserver( + (resizeObserverEntries: ResizeObserverEntry[]) => { + const navWidth = resizeObserverEntries[0].contentRect.width + const moreMenuWidth = moreMenuRef.current?.getBoundingClientRect().width ?? 0 + navWidth !== 0 && + overflowEffect( + navWidth, + moreMenuWidth, + validChildren, + childWidthArray, + noIconChildWidthArray, + updateListAndMenu, + ) + }, + navRef as RefObject, + ) // Compute menuInlineStyles if needed let menuInlineStyles: React.CSSProperties = {...baseMenuInlineStyles} diff --git a/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx b/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx index 0b7290d6659..96e13001cf3 100644 --- a/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx +++ b/packages/react/src/experimental/SelectPanel2/SelectPanel.tsx @@ -521,10 +521,10 @@ const SecondaryLink: React.FC = ({className, ...props}) => { return ( // @ts-ignore TODO: is as prop is not recognised by button? - () - ); + + ) } const SecondaryCheckbox: React.FC = ({id, children, className, ...props}) => { diff --git a/packages/react/src/hooks/useFocusZone.ts b/packages/react/src/hooks/useFocusZone.ts index 6f8a1900181..a4e5a7bce4d 100644 --- a/packages/react/src/hooks/useFocusZone.ts +++ b/packages/react/src/hooks/useFocusZone.ts @@ -28,7 +28,10 @@ export interface FocusZoneHookSettings extends Omit; activeDescendantControlRef: React.RefObject} { +): { + containerRef: React.RefObject + activeDescendantControlRef: React.RefObject +} { const containerRef = useProvidedRefOrCreate(settings.containerRef) const useActiveDescendant = !!settings.activeDescendantFocus const passedActiveDescendantRef = diff --git a/packages/react/src/hooks/useRenderForcingRef.ts b/packages/react/src/hooks/useRenderForcingRef.ts index 57405893c54..35ee4ce0665 100644 --- a/packages/react/src/hooks/useRenderForcingRef.ts +++ b/packages/react/src/hooks/useRenderForcingRef.ts @@ -23,5 +23,5 @@ export function useRenderForcingRef(value?: TRef) { ref.current = refCurrent }, [refCurrent]) - return [ref as RefObject, setRef] as const; + return [ref as RefObject, setRef] as const } diff --git a/packages/react/src/stories/useAnchoredPosition.stories.tsx b/packages/react/src/stories/useAnchoredPosition.stories.tsx index 2b98d569e44..e864901e420 100644 --- a/packages/react/src/stories/useAnchoredPosition.stories.tsx +++ b/packages/react/src/stories/useAnchoredPosition.stories.tsx @@ -131,7 +131,7 @@ export const UseAnchoredPosition = (args: any) => { ) return ( - (
+
{ > Floating element -
) - ); +
+ ) } export const CenteredOnScreen = () => { const {floatingElementRef, anchorElementRef, position} = useAnchoredPosition({ @@ -160,7 +160,7 @@ export const CenteredOnScreen = () => { }) // The outer Position element simply fills all available space return ( - (
}> +
}> } top={position?.top ?? 0} @@ -173,8 +173,8 @@ export const CenteredOnScreen = () => {

-
) - ); +
+ ) } export const ComplexAncestry = () => { @@ -191,35 +191,37 @@ export const ComplexAncestry = () => { }, [recalculateSignal]) // The outer Position element simply fills all available space - return (<> -
- Clipping container - this element has overflow set to something other than visible -
- Relatively positioned parent, but fluid height, so not the clipping parent. -
- Floating element container. Position=static and overflow=hidden to show that overflow-hidden on a - statically-positioned element will not have any effect. - } - > - Floating element - + return ( + <> +
+ Clipping container - this element has overflow set to something other than visible +
+ Relatively positioned parent, but fluid height, so not the clipping parent. +
+ Floating element container. Position=static and overflow=hidden to show that overflow-hidden on a + statically-positioned element will not have any effect. + } + > + Floating element + +
-
-
- Anchor element container. This element is really tall to demonstrate behavior within a scrollable clipping - container. -
}> - Anchor Element +
+ Anchor element container. This element is really tall to demonstrate behavior within a scrollable clipping + container. +
}> + Anchor Element +
-
- - ); + + + ) } const Nav = ({children, ...props}: React.ComponentPropsWithoutRef<'nav'>) => ( @@ -277,7 +279,7 @@ export const WithPortal = () => { }, [showMenu]) return ( - (
+
) - ); +
+ ) } diff --git a/packages/react/src/stories/useFocusTrap.stories.tsx b/packages/react/src/stories/useFocusTrap.stories.tsx index b8304884bf7..7834d2b2cc9 100644 --- a/packages/react/src/stories/useFocusTrap.stories.tsx +++ b/packages/react/src/stories/useFocusTrap.stories.tsx @@ -52,25 +52,27 @@ export const FocusTrap = () => { } }, [spaceListener]) - return (<> - - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - Durian - Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
- ); + return ( + <> + + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + Durian + Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
+ + ) } export const RestoreFocus = () => { @@ -93,25 +95,27 @@ export const RestoreFocus = () => { } }, [spaceListener]) - return (<> - - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - Durian - Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
- ); + return ( + <> + + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + Durian + Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
+ + ) } export const CustomInitialFocus = () => { @@ -134,30 +138,32 @@ export const CustomInitialFocus = () => { } }, [spaceListener]) - return (<> - - - - This story is the same as the `Focus Trap` story, except, when the trap zone is activated, the - “Elderberry” button will receive the initial focus (if the trap zone container does not already - have focus). - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - Durian - }>Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
- ); + return ( + <> + + + + This story is the same as the `Focus Trap` story, except, when the trap zone is activated, the + “Elderberry” button will receive the initial focus (if the trap zone container does not already + have focus). + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + Durian + }>Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
+ + ) } function useKeyPressListener(key: string, handler: () => void, capture = false) { @@ -204,25 +210,27 @@ export const DynamicFocusTrapContents = () => { useCallback(() => setTrapEnabled(!trapEnabled), [trapEnabled]), ) - return (<> - - - Apple - Banana - Cantaloupe -
}> - Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. - - - - - -
- Grapefruit - Honeydew - Jackfruit -
- ); + return ( + <> + + + Apple + Banana + Cantaloupe +
}> + Trap zone! Press SPACE to {trapEnabled ? 'deactivate' : 'activate'}. + + + + + +
+ Grapefruit + Honeydew + Jackfruit +
+ + ) } export const MultipleFocusTraps = () => { @@ -251,59 +259,61 @@ export const MultipleFocusTraps = () => { } }, [keyListener]) - return (<> - - - - This story demonstrates the global nature of focus traps. When a focus trap is enabled, if there is already an - active focus trap, it becomes suspended and pushed onto a stack. Once the newly-active focus trap is disabled, - the most recently-suspended trap will reactivate. Suspended focus traps can be disabled, causing them to be - removed from the stack of suspended traps. - -
- Legend - -
- - Inactive -
- -
- - Suspended -
- -
- - Active -
-
- Apple - Banana - Cantaloupe -
}> - - Trap zone ({trapEnabled1 ? 'enabled' : 'disabled'})! Press 1 to toggle. - - - Durian - Elderberry - Fig - -
- Grapefruit - Honeydew - Jackfruit -
}> - - Trap zone ({trapEnabled2 ? 'enabled' : 'disabled'})! Press 2 to toggle. - - - Kiwi - Lemon - Mango - -
- Nectarine - Orange - Peach -
- ); + return ( + <> + + + + This story demonstrates the global nature of focus traps. When a focus trap is enabled, if there is already an + active focus trap, it becomes suspended and pushed onto a stack. Once the newly-active focus trap is disabled, + the most recently-suspended trap will reactivate. Suspended focus traps can be disabled, causing them to be + removed from the stack of suspended traps. + +
+ Legend + +
+ - Inactive +
+ +
+ - Suspended +
+ +
+ - Active +
+
+ Apple + Banana + Cantaloupe +
}> + + Trap zone ({trapEnabled1 ? 'enabled' : 'disabled'})! Press 1 to toggle. + + + Durian + Elderberry + Fig + +
+ Grapefruit + Honeydew + Jackfruit +
}> + + Trap zone ({trapEnabled2 ? 'enabled' : 'disabled'})! Press 2 to toggle. + + + Kiwi + Lemon + Mango + +
+ Nectarine + Orange + Peach +
+ + ) } diff --git a/packages/react/src/stories/useFocusZone.stories.tsx b/packages/react/src/stories/useFocusZone.stories.tsx index 07da92ee74a..3ea4c20e71d 100644 --- a/packages/react/src/stories/useFocusZone.stories.tsx +++ b/packages/react/src/stories/useFocusZone.stories.tsx @@ -32,28 +32,30 @@ export const BasicFocusZone = () => { setFzEnabled(!fzEnabled) }, [fzEnabled]) - return (<> -
-
Last key pressed: {lastKey}
- - Apple - Banana - Cantaloupe -
}> - Use Up Arrow, Down Arrow, Home, and End to move focus within this box. -
- Durian - Elderberry - Fig + return ( + <> +
+
Last key pressed: {lastKey}
+ + Apple + Banana + Cantaloupe +
}> + Use Up Arrow, Down Arrow, Home, and End to move focus within this box. +
+ Durian + Elderberry + Fig +
+ Kiwi + Lemon + Mango
- Kiwi - Lemon - Mango -
- ); + + ) } export const FocusOutBehavior = () => { @@ -72,37 +74,39 @@ export const FocusOutBehavior = () => { bindKeys: FocusKeys.ArrowHorizontal | FocusKeys.HomeAndEnd, }) - return (<> -
-
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - - Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus stops at the ends. - + return ( + <> +
+
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + + Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus stops at the ends. + -
- Durian - Elderberry - Fig +
+ Durian + Elderberry + Fig +
-
-
}> - Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus is circular. +
}> + Use Left Arrow, Right Arrow, Home, and End to move focus within this box. Focus is circular. -
- Grapefruit - Honeydew - Jackfruit +
+ Grapefruit + Honeydew + Jackfruit +
+ Kiwi + Lemon + Mango
- Kiwi - Lemon - Mango -
- ); + + ) } function getSiblingIndex(element: Element) { @@ -169,29 +173,31 @@ export const CustomFocusMovement = () => { useFocusZone({containerRef, getNextFocusable}) - return (<> -
-
Last key pressed: {lastKey}
- Apple - -
- Use arrow keys to move focus within this box. -
}> - Banana - Cantaloupe - Durian - Elderberry - Fig - Grapefruit - Honeydew - Jackfruit - Kiwi + return ( + <> +
+
Last key pressed: {lastKey}
+ Apple + +
+ Use arrow keys to move focus within this box. +
}> + Banana + Cantaloupe + Durian + Elderberry + Fig + Grapefruit + Honeydew + Jackfruit + Kiwi +
+ Lemon + Mango
- Lemon - Mango -
- ); + + ) } export const FocusInStrategy = () => { @@ -230,48 +236,50 @@ export const FocusInStrategy = () => { focusInStrategy: customStrategy, }) - return (<> -
-
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - “First” strategy (focus first focusable element) -
- Banana - Cantaloupe - Durian + return ( + <> +
+
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + “First” strategy (focus first focusable element) +
+ Banana + Cantaloupe + Durian +
-
-
}> - “Closest” strategy (focus first or last depending on focus direction) -
- Elderberry - Fig - Grapefruit +
}> + “Closest” strategy (focus first or last depending on focus direction) +
+ Elderberry + Fig + Grapefruit +
-
-
}> - “Previous” strategy (most recently focused element) -
- Honeydew - Jackfruit - Kiwi +
}> + “Previous” strategy (most recently focused element) +
+ Honeydew + Jackfruit + Kiwi +
-
-
}> - “Custom” strategy (choose randomly for this example) -
- Lemon - Mango - Nectarine +
}> + “Custom” strategy (choose randomly for this example) +
+ Lemon + Mango + Nectarine +
+ Orange + Papaya
- Orange - Papaya -
- ); + + ) } export const SpecialSituations = () => { @@ -292,65 +300,67 @@ export const SpecialSituations = () => { }) const {containerRef: hContainerRef} = useFocusZone({focusOutBehavior: 'wrap', bindKeys: FocusKeys.ArrowHorizontal}) - return (<> -
- - This story is very esoteric! It only exists to show some of the nuance of the arrow key focus behavior in - different situations. Focus treatment within your component should be evaluated for your particular UX using - the{' '} - - ARIA guidelines - - . - -
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - Bound keys: Up, Down, PageUp, PageDown, W, S, J, K, Home, End, Tab -
- - - Regular button - - + return ( + <> +
+ + This story is very esoteric! It only exists to show some of the nuance of the arrow key focus behavior in + different situations. Focus treatment within your component should be evaluated for your particular UX using + the{' '} + + ARIA guidelines + + . + +
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + Bound keys: Up, Down, PageUp, PageDown, W, S, J, K, Home, End, Tab +
+ + + Regular button + + +
-
-
}> - - -
- Grapefruit - - Jackfruit +
}> + + +
+ Grapefruit + + Jackfruit +
+ Kiwi + Lemon + Mango
- Kiwi - Lemon - Mango -
- ); + + ) } export const ChangingSubtree = () => { @@ -380,25 +390,27 @@ export const ChangingSubtree = () => { ) } - return (<> -
- - This story demonstrates that focusZone is consistent even when the container’s subtree changes. - -
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
}> - Bound keys: Arrow Up and Arrow Down -
{buttons}
-
-
- Remove Button - Add Button + return ( + <> +
+ + This story demonstrates that focusZone is consistent even when the container’s subtree changes. + +
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
}> + Bound keys: Arrow Up and Arrow Down +
{buttons}
+
+
+ Remove Button + Add Button +
-
- ); + + ) } export const NestedZones = () => { @@ -421,30 +433,32 @@ export const NestedZones = () => { bindKeys: FocusKeys.ArrowHorizontal, }) - return (<> -
-
Last key pressed: {lastKey}
- Apple - Banana -
}> - Bound keys: Arrow Up and Arrow Down -
- Cantaloupe -
}> - Additional Bound keys: Arrow Left and Arrow Right -
- Durian - Elderberry - Fig - Grapefruit + return ( + <> +
+
Last key pressed: {lastKey}
+ Apple + Banana +
}> + Bound keys: Arrow Up and Arrow Down +
+ Cantaloupe +
}> + Additional Bound keys: Arrow Left and Arrow Right +
+ Durian + Elderberry + Fig + Grapefruit +
+ Honeydew
- Honeydew + Jackfruit + Kiwi
- Jackfruit - Kiwi -
- ); + + ) } export const ActiveDescendant = () => { @@ -472,44 +486,46 @@ export const ActiveDescendant = () => { focusableElementFilter: elem => elem instanceof HTMLButtonElement, }) - return (<> -
- - This story demonstrates using the `aria-activedescendant` pattern for managing both a focused element and an - active element. Below, you can focus the input box then use the up/down arrow keys to change the active - descendant (dark blue outline). - -
Last key pressed: {lastKey}
- Apple - Banana - Cantaloupe -
- -
- } - type="text" - defaultValue="Focus remains here." - aria-controls="list" - id="focus-input" - /> -
} - > - Durian - Elderberry - Fig - Grapefruit + return ( + <> +
+ + This story demonstrates using the `aria-activedescendant` pattern for managing both a focused element and an + active element. Below, you can focus the input box then use the up/down arrow keys to change the active + descendant (dark blue outline). + +
Last key pressed: {lastKey}
+ Apple + Banana + Cantaloupe +
+ +
+ } + type="text" + defaultValue="Focus remains here." + aria-controls="list" + id="focus-input" + /> +
} + > + Durian + Elderberry + Fig + Grapefruit +
+ Honeydew + Jackfruit + Kiwi
- Honeydew - Jackfruit - Kiwi -
- ); + + ) } From c3a6111253256974a5ea1a683e9e665a2fc2acd6 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Mon, 17 Nov 2025 14:15:30 -0600 Subject: [PATCH 5/5] chore: clean-up patch-package for react-19 --- script/setup-react-19.mts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/script/setup-react-19.mts b/script/setup-react-19.mts index ace7279d572..b0f3db6534f 100644 --- a/script/setup-react-19.mts +++ b/script/setup-react-19.mts @@ -43,4 +43,14 @@ for (const packageJsonPath of packageJsonPaths) { fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n', 'utf8') } -execSync('npm install', {stdio: 'inherit'}) +// eslint-disable-next-line no-console +console.log('Removing patches for @types/react...') +execSync('rm ./patches/@types+react+18.3.11.patch', { + stdio: 'inherit', +}) + +// eslint-disable-next-line no-console +console.log('Installing updated dependencies...') +execSync('npm install', { + stdio: 'inherit', +})