Skip to content
Open
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/wicked-buttons-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

Add support for `ref` types from React 19 by annotating `React.RefObject` types as `React.RefObject<T | null>`
201 changes: 201 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -81,6 +82,7 @@
"globals": "^16.2.0",
"markdownlint-cli2": "^0.19.0",
"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",
Expand Down
27 changes: 15 additions & 12 deletions packages/react/src/ActionBar/ActionBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,16 +320,19 @@ export const ActionBar: React.FC<React.PropsWithChildren<ActionBarProps>> = prop
const moreMenuBtnRef = useRef<HTMLButtonElement>(null)
const containerRef = React.useRef<HTMLUListElement>(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<HTMLElement>)
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<HTMLElement | null>,
)

const isVisibleChild = useCallback(
(id: string) => {
Expand Down Expand Up @@ -554,7 +557,7 @@ const ActionBarGroupContext = React.createContext<{

export const ActionBarGroup = forwardRef(({children}: React.PropsWithChildren, forwardedRef) => {
const backupRef = useRef<HTMLDivElement>(null)
const ref = (forwardedRef ?? backupRef) as RefObject<HTMLDivElement>
const ref = (forwardedRef ?? backupRef) as RefObject<HTMLDivElement | null>
const id = useId()
const {registerChild, unregisterChild} = React.useContext(ActionBarContext)

Expand Down Expand Up @@ -586,7 +589,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<HTMLButtonElement>(null)
const ref = (forwardedRef ?? backupRef) as RefObject<HTMLButtonElement>
const ref = (forwardedRef ?? backupRef) as RefObject<HTMLButtonElement | null>
const id = useId()
const {registerChild, unregisterChild, isVisibleChild} = React.useContext(ActionBarContext)

Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/ActionList/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const UnwrappedList = <As extends React.ElementType = 'ul'>(

const ariaLabelledBy = slots.heading ? (slots.heading.props.id ?? headingId) : listLabelledBy
const listRole = role || listRoleFromContainer
const listRef = useProvidedRefOrCreate(forwardedRef as React.RefObject<HTMLUListElement>)
const listRef = useProvidedRefOrCreate(forwardedRef as React.RefObject<HTMLUListElement | null>)

let enableFocusZone = false
if (enableFocusZoneFromContainer !== undefined) enableFocusZone = enableFocusZoneFromContainer
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
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ export const CustomOverlayMenuAnchor = () => {
<FormControl.Label htmlFor="autocompleteInput" id="autocompleteLabel">
Default label
</FormControl.Label>
<div ref={menuAnchorRef as React.RefObject<HTMLDivElement>} className={classes.AnchorContainer}>
<div ref={menuAnchorRef as React.RefObject<HTMLDivElement | null>} className={classes.AnchorContainer}>
<Autocomplete>
<Autocomplete.Input
id="autocompleteInput"
Expand Down Expand Up @@ -522,7 +522,7 @@ export const InOverlayWithCustomScrollContainerRef = () => {
<div className={classes.OverlayInputBar}>
<Autocomplete.Input ref={inputRef} className={classes.OverlayInput} block aria-label="Search" />
</div>
<div ref={scrollContainerRef as RefObject<HTMLDivElement>} className={classes.OverlayScroll}>
<div ref={scrollContainerRef as RefObject<HTMLDivElement | null>} className={classes.OverlayScroll}>
<Autocomplete.Menu
items={items}
selectedItemIds={[]}
Expand Down
Loading
Loading