Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/three-years-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

chore - Fix type compatibility with react 19 refs
1 change: 1 addition & 0 deletions packages/react/src/ActionList/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ const UnwrappedList = <As extends React.ElementType = 'ul'>(
}}
>
{slots.heading}
{/* @ts-expect-error ref needs a non nullable ref */}
<Component
className={clsx(classes.ActionList, className)}
role={listRole}
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/AnchoredOverlay/AnchoredOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface AnchoredOverlayPropsWithAnchor {
/**
* An override to the internal ref that will be spread on to the renderAnchor
*/
anchorRef?: React.RefObject<HTMLElement>
anchorRef?: React.RefObject<HTMLElement | null>

/**
* An override to the internal id that will be spread on to the renderAnchor
Expand All @@ -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<HTMLElement>
anchorRef: React.RefObject<HTMLElement | null>
/**
* An override to the internal id that will be spread on to the renderAnchor
*/
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/ButtonGroup/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(
forwardRef,
) {
const buttons = React.Children.map(children, (child, index) => <div key={index}>{child}</div>)
const buttonRef = useProvidedRefOrCreate(forwardRef as React.RefObject<HTMLDivElement>)
const buttonRef = useProvidedRefOrCreate(forwardRef as React.RefObject<HTMLDivElement | null>)

useFocusZone({
containerRef: buttonRef,
Expand All @@ -27,6 +27,7 @@ const ButtonGroup = React.forwardRef(function ButtonGroup(
})

return (
//@ts-expect-error it needs a non nullable ref
<BaseComponent ref={buttonRef} className={clsx(className, classes.ButtonGroup)} role={role} {...rest}>
{buttons}
</BaseComponent>
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/Checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
checkbox.setAttribute('aria-checked', checkbox.checked ? 'true' : 'false')
}
})

// @ts-expect-error inputProp needs a non nullable ref
return <input {...inputProps} className={clsx(className, sharedClasses.Input, classes.Checkbox)} />
},
)
Expand Down
7 changes: 4 additions & 3 deletions packages/react/src/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export type DialogButtonProps = Omit<ButtonProps, 'content'> & {
* A reference to the rendered Button’s DOM node, used together with
* `autoFocus` for `focusTrap`’s `initialFocus`.
*/
ref?: React.RefObject<HTMLButtonElement>
ref?: React.RefObject<HTMLButtonElement | null>
}

/**
Expand Down Expand Up @@ -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<HTMLElement>
returnFocusRef?: React.RefObject<HTMLElement | null>

/**
* The element to focus when the Dialog opens
*/
initialFocusRef?: React.RefObject<HTMLElement>
initialFocusRef?: React.RefObject<HTMLElement | null>

/**
* Additional class names to apply to the dialog
Expand Down Expand Up @@ -408,6 +408,7 @@ const Buttons: React.FC<React.PropsWithChildren<{buttons: DialogButtonProps[]}>>
{...buttonProps}
// 'normal' value is equivalent to 'default', this is used for backwards compatibility
variant={buttonType === 'normal' ? 'default' : buttonType}
// @ts-expect-error it needs a non nullable ref
ref={autoFocus && autoFocusCount === 0 ? (autoFocusCount++, autoFocusRef) : null}
>
{content}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ export interface FilteredActionListProps extends Partial<Omit<GroupedListProps,
filterValue?: string
onFilterChange: (value: string, e: React.ChangeEvent<HTMLInputElement> | null) => void
onListContainerRefChanged?: (ref: HTMLElement | null) => void
onInputRefChanged?: (ref: React.RefObject<HTMLInputElement>) => void
onInputRefChanged?: (ref: React.RefObject<HTMLInputElement | null>) => void
/**
* A ref assigned to the scrollable container wrapping the ActionList
*/
scrollContainerRef?: React.Ref<HTMLDivElement | null>
textInputProps?: Partial<Omit<TextInputProps, 'onChange'>>
inputRef?: React.RefObject<HTMLInputElement>
inputRef?: React.RefObject<HTMLInputElement | null>
message?: React.ReactNode
messageText?: {
title: string
Expand Down Expand Up @@ -382,6 +382,7 @@ export function FilteredActionList({
<div ref={inputAndListContainerRef} className={clsx(className, classes.Root)} data-testid="filtered-action-list">
<div className={classes.Header}>
<TextInput
// @ts-expect-error it needs a non nullable ref
ref={inputRef}
block
width="auto"
Expand Down Expand Up @@ -418,6 +419,7 @@ export function FilteredActionList({
</label>
</div>
)}
{/* @ts-expect-error div needs a non nullable ref */}
<div ref={scrollContainerRef} className={classes.Container}>
{getBodyContent()}
</div>
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/FilteredActionList/useAnnouncements.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const useFirstRender = () => {
}

const getItemWithActiveDescendant = (
listRef: React.RefObject<HTMLUListElement>,
listRef: React.RefObject<HTMLUListElement | null>,
items: FilteredActionListProps['items'],
) => {
const listElement = listRef.current
Expand All @@ -40,8 +40,8 @@ const getItemWithActiveDescendant = (

export const useAnnouncements = (
items: FilteredActionListProps['items'],
listContainerRef: React.RefObject<HTMLUListElement>,
inputRef: React.RefObject<HTMLInputElement>,
listContainerRef: React.RefObject<HTMLUListElement | null>,
inputRef: React.RefObject<HTMLInputElement | null>,
enabled: boolean = true,
loading: boolean = false,
message?: {title: string; description: string},
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/Overlay/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,14 @@ export const BaseOverlay = React.forwardRef(

type ContainerProps = {
anchorSide?: AnchorSide
ignoreClickRefs?: React.RefObject<HTMLElement>[]
initialFocusRef?: React.RefObject<HTMLElement>
ignoreClickRefs?: React.RefObject<HTMLElement | null>[]
initialFocusRef?: React.RefObject<HTMLElement | null>
onClickOutside: (e: TouchOrMouseEvent) => void
onEscape: (e: KeyboardEvent) => void
portalContainerName?: string
preventOverflow?: boolean
preventFocusOnOpen?: boolean
returnFocusRef: React.RefObject<HTMLElement>
returnFocusRef: React.RefObject<HTMLElement | null>
}

type internalOverlayProps = Merge<OwnOverlayProps, ContainerProps>
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/PageHeader/PageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ const TitleArea = React.forwardRef<HTMLDivElement, React.PropsWithChildren<Title
return (
<div
className={clsx(classes.TitleArea, className)}
// @ts-expect-error it needs a non nullable ref
ref={titleAreaRef}
data-component="TitleArea"
{...getResponsiveAttributes('size-variant', variant)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import FormControl from '../FormControl'
import {Stack} from '../Stack'
import {Dialog} from '../experimental'
import styles from './SelectPanel.examples.stories.module.css'
import {useVirtualizer} from '@tanstack/react-virtual'
import {useVirtualizer, type VirtualItem} from '@tanstack/react-virtual'
import Checkbox from '../Checkbox'
import Label from '../Label'

Expand Down Expand Up @@ -639,7 +639,7 @@ export const Virtualized = () => {
const virtualizedItems = useMemo(
() =>
renderSubset
? virtualizer.getVirtualItems().map(virtualItem => {
? virtualizer.getVirtualItems().map((virtualItem: VirtualItem) => {
const item = filteredItems[virtualItem.index]

return {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/SelectPanel/SelectPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ function Panel({
filterValue={filterValue}
onFilterChange={onFilterChange}
onListContainerRefChanged={onListContainerRefChanged}
// @ts-expect-error it needs a non nullable ref
onInputRefChanged={onInputRefChanged}
placeholderText={placeholderText}
{...listProps}
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/TextInput/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
ref,
) => {
const [isInputFocused, setIsInputFocused] = useState<boolean>(false)
const inputRef = useProvidedRefOrCreate(ref as React.RefObject<HTMLInputElement>)
const inputRef = useProvidedRefOrCreate(ref as React.RefObject<HTMLInputElement | null>)
// this class is necessary to style FilterSearch, plz no touchy!
const wrapperClasses = clsx(className, 'TextInput-wrapper')
const showLeadingLoadingIndicator =
Expand Down Expand Up @@ -164,6 +164,7 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
{typeof LeadingVisual !== 'string' && isValidElementType(LeadingVisual) ? <LeadingVisual /> : LeadingVisual}
</TextInputInnerVisualSlot>
<UnstyledTextInput
// @ts-expect-error it needs a non nullable ref
ref={inputRef}
disabled={disabled}
onFocus={handleInputFocus}
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/TooltipV2/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ export const Tooltip: ForwardRefExoticComponent<
{React.isValidElement(child) &&
// eslint-disable-next-line react-hooks/refs
React.cloneElement(child as React.ReactElement<TriggerPropsType>, {
// @ts-expect-error it needs a non nullable ref
ref: triggerRef,
// If it is a type description, we use tooltip to describe the trigger
'aria-describedby': (() => {
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/deprecated/DialogV1/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ function DialogHeader({children, className, as: Component = 'div', ...rest}: Dia
type InternalDialogProps = {
isOpen?: boolean
onDismiss?: () => void
initialFocusRef?: React.RefObject<HTMLElement>
returnFocusRef?: React.RefObject<HTMLElement>
initialFocusRef?: React.RefObject<HTMLElement | null>
returnFocusRef?: React.RefObject<HTMLElement | null>
as?: React.ElementType
} & StyledDialogBaseProps &
HTMLAttributes<HTMLDivElement>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export type SelectPanelProps = {

defaultOpen?: boolean
open?: boolean
anchorRef?: React.RefObject<HTMLButtonElement>
anchorRef?: React.RefObject<HTMLButtonElement | null>
anchoredPositionSettings?: Partial<PositionSettings>

onCancel?: () => void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const CustomTabList = (props: React.PropsWithChildren) => {

return (
<div style={{width: '200px'}}>
{/* @ts-expect-error it needs a non nullable ref */}
<ActionList {...tabListProps}>{props.children}</ActionList>
</div>
)
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/experimental/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function useTabList<T extends HTMLElement>(
'aria-orientation': AriaAttributes['aria-orientation']
'aria-label': AriaAttributes['aria-label']
'aria-labelledby': AriaAttributes['aria-labelledby']
ref: React.RefObject<T>
ref: React.RefObject<T | null>
role: 'tablist'
}
} {
Expand Down Expand Up @@ -186,6 +186,7 @@ function TabList({children, ...rest}: TabListProps) {
const {tabListProps} = useTabList<HTMLDivElement>(rest)

return (
// @ts-expect-error it needs a non nullable ref
<div {...rest} {...tabListProps}>
{children}
</div>
Expand Down
10 changes: 5 additions & 5 deletions packages/react/src/hooks/useAnchoredPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import {useResizeObserver} from './useResizeObserver'
import useLayoutEffect from '../utils/useIsomorphicLayoutEffect'

export interface AnchoredPositionHookSettings extends Partial<PositionSettings> {
floatingElementRef?: React.RefObject<Element>
anchorElementRef?: React.RefObject<Element>
floatingElementRef?: React.RefObject<Element | null>
anchorElementRef?: React.RefObject<Element | null>
pinPosition?: boolean
onPositionChange?: (position: AnchorPosition | undefined) => void
}
Expand All @@ -25,8 +25,8 @@ export function useAnchoredPosition(
settings?: AnchoredPositionHookSettings,
dependencies: React.DependencyList = [],
): {
floatingElementRef: React.RefObject<Element>
anchorElementRef: React.RefObject<Element>
floatingElementRef: React.RefObject<Element | null>
anchorElementRef: React.RefObject<Element | null>
position: AnchorPosition | undefined
} {
const floatingElementRef = useProvidedRefOrCreate(settings?.floatingElementRef)
Expand Down Expand Up @@ -98,7 +98,7 @@ export function useAnchoredPosition(
useLayoutEffect(updatePosition, [updatePosition])

useResizeObserver(updatePosition) // watches for changes in window size
useResizeObserver(updatePosition, floatingElementRef as React.RefObject<HTMLElement>) // watches for changes in floating element size
useResizeObserver(updatePosition, floatingElementRef as React.RefObject<HTMLElement | null>) // watches for changes in floating element size

return {
floatingElementRef,
Expand Down
10 changes: 5 additions & 5 deletions packages/react/src/hooks/useDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ function focusable(el: Element) {
}

type UseDialogParameters = {
modalRef: React.RefObject<HTMLElement>
overlayRef: React.RefObject<HTMLElement>
modalRef: React.RefObject<HTMLElement | null>
overlayRef: React.RefObject<HTMLElement | null>
isOpen?: boolean
onDismiss?: () => void
initialFocusRef?: React.RefObject<HTMLElement>
closeButtonRef?: React.RefObject<HTMLElement>
returnFocusRef?: React.RefObject<HTMLElement>
initialFocusRef?: React.RefObject<HTMLElement | null>
closeButtonRef?: React.RefObject<HTMLElement | null>
returnFocusRef?: React.RefObject<HTMLElement | null>
}

function useDialog({
Expand Down
8 changes: 4 additions & 4 deletions packages/react/src/hooks/useFocusTrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLElement>
containerRef?: React.RefObject<HTMLElement | null>

/**
* 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<HTMLElement>
initialFocusRef?: React.RefObject<HTMLElement | null>

/**
* Set to true to disable the focus trap and clean up listeners. Can be re-enabled at
Expand All @@ -33,7 +33,7 @@ export interface FocusTrapHookSettings {
*
* Overrides restoreFocusOnCleanUp
*/
returnFocusRef?: React.RefObject<HTMLElement>
returnFocusRef?: React.RefObject<HTMLElement | null>
}

/**
Expand All @@ -44,7 +44,7 @@ export interface FocusTrapHookSettings {
export function useFocusTrap(
settings?: FocusTrapHookSettings,
dependencies: React.DependencyList = [],
): {containerRef: React.RefObject<HTMLElement>; initialFocusRef: React.RefObject<HTMLElement>} {
): {containerRef: React.RefObject<HTMLElement | null>; initialFocusRef: React.RefObject<HTMLElement | null>} {
const containerRef = useProvidedRefOrCreate(settings?.containerRef)
const initialFocusRef = useProvidedRefOrCreate(settings?.initialFocusRef)
const disabled = settings?.disabled
Expand Down
9 changes: 6 additions & 3 deletions packages/react/src/hooks/useFocusZone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export interface FocusZoneHookSettings extends Omit<FocusZoneSettings, 'activeDe
* Optional ref for the container that holds all elements participating in arrow key focus.
* If one is not passed, we will create one for you and return it from the hook.
*/
containerRef?: React.RefObject<HTMLElement>
containerRef?: React.RefObject<HTMLElement | null>

/**
* 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<HTMLElement>
activeDescendantFocus?: boolean | React.RefObject<HTMLElement | null>

/**
* Set to true to disable the focus zone and clean up listeners. Can be re-enabled at
Expand All @@ -28,7 +28,10 @@ export interface FocusZoneHookSettings extends Omit<FocusZoneSettings, 'activeDe
export function useFocusZone(
settings: FocusZoneHookSettings = {},
dependencies: React.DependencyList = [],
): {containerRef: React.RefObject<HTMLElement>; activeDescendantControlRef: React.RefObject<HTMLElement>} {
): {
containerRef: React.RefObject<HTMLElement | null>
activeDescendantControlRef: React.RefObject<HTMLElement | null>
} {
const containerRef = useProvidedRefOrCreate(settings.containerRef)
const useActiveDescendant = !!settings.activeDescendantFocus
const passedActiveDescendantRef =
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/hooks/useMenuInitialFocus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import {iterateFocusableElements} from '@primer/behaviors/utils'

export const useMenuInitialFocus = (
open: boolean,
containerRef?: React.RefObject<HTMLElement>,
anchorRef?: React.RefObject<HTMLElement>,
containerRef?: React.RefObject<HTMLElement | null>,
anchorRef?: React.RefObject<HTMLElement | null>,
) => {
/**
* We need to pick the first element to focus based on how the menu was opened,
Expand Down
Loading
Loading