Skip to content

Commit

Permalink
[core] React 19 compatibility (#605)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak authored Oct 3, 2024
1 parent 84c3be7 commit 1a29c11
Show file tree
Hide file tree
Showing 51 changed files with 155 additions and 77 deletions.
3 changes: 2 additions & 1 deletion docs/app/experiments/dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ function ReactSpringDialogDemo({ animated, keepMounted, modal, dismissible }: De
);
}

function ReactSpringTransition(props: { open: boolean; children?: React.ReactElement }) {
function ReactSpringTransition(props: { open: boolean; children?: React.ReactElement<unknown> }) {
const { open, children } = props;

const api = useSpringRef();
Expand Down Expand Up @@ -179,6 +179,7 @@ function ReactSpringTransition(props: { open: boolean; children?: React.ReactEle
}, [api, open, mounted, setMounted]);

return mounted ? (
/* @ts-ignore springAnimated.div props type does not include children and errors in React 19 */
<springAnimated.div style={springs} className={classes.springWrapper}>
{children}
</springAnimated.div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { styled } from '@mui/system';
export default function UnstyledTooltipFollowCursor() {
return (
<div style={{ display: 'flex', gap: 12 }}>
<Tooltip.Root followCursorAxis="both">
<Tooltip.Root trackCursorAxis="both">
<AnchorButton>Anchor</AnchorButton>
<Tooltip.Positioner sideOffset={5}>
<TooltipPopup>Tooltip</TooltipPopup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { styled } from '@mui/system';
export default function UnstyledTooltipFollowCursor() {
return (
<div style={{ display: 'flex', gap: 12 }}>
<Tooltip.Root followCursorAxis="both">
<Tooltip.Root trackCursorAxis="both">
<AnchorButton>Anchor</AnchorButton>
<Tooltip.Positioner sideOffset={5}>
<TooltipPopup>Tooltip</TooltipPopup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Tooltip.Root followCursorAxis="both">
<Tooltip.Root trackCursorAxis="both">
<AnchorButton>Anchor</AnchorButton>
<Tooltip.Positioner sideOffset={5}>
<TooltipPopup>Tooltip</TooltipPopup>
Expand Down
2 changes: 1 addition & 1 deletion docs/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
1 change: 1 addition & 0 deletions docs/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const nextConfig = {
: {}),
experimental: {
esmExternals: true,
workerThreads: false,
},
};

Expand Down
2 changes: 1 addition & 1 deletion docs/src/blocks/Demo/DemoSourceCopy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ export namespace DemoSourceCopy {
export interface Props extends React.ButtonHTMLAttributes<HTMLButtonElement> {
onCopied?: () => void;
onError?: (error: unknown) => void;
render?: React.ReactElement;
render?: React.ReactElement<React.ComponentPropsWithRef<'button'>>;
}
}
4 changes: 2 additions & 2 deletions docs/src/blocks/GoogleAnalytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ const GoogleAnalytics = React.memo(function GoogleAnalytics(props: GoogleAnalyti
}
}, []);

const timeout = React.useRef<NodeJS.Timeout>();
const timeout = React.useRef<ReturnType<typeof setTimeout> | null>(null);

React.useEffect(() => {
// Wait for the title to be updated.
// React fires useEffect twice in dev mode
clearTimeout(timeout.current);
clearTimeout(timeout.current ?? undefined);
timeout.current = setTimeout(() => {
// Remove hash as it's never sent to the server
// https://github.com/vercel/next.js/issues/25202
Expand Down
2 changes: 1 addition & 1 deletion docs/src/design-system/ToggleButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,4 @@ export const ToggleButtonGroup = React.forwardRef(function ToggleButtonGroup<
);
}) as <Option extends { value: string; label: string }>(
props: ToggleButtonGroupProps<Option> & { ref?: React.Ref<HTMLDivElement> },
) => JSX.Element;
) => React.JSX.Element;
2 changes: 1 addition & 1 deletion docs/src/design-system/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function Tooltip(props: Tooltip.Props) {
export namespace Tooltip {
export interface Props {
label: string;
children: React.ReactElement;
children: React.ReactElement<unknown>;
side?: 'top' | 'right' | 'bottom' | 'left';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe('<AlertDialog.Backdrop />', () => {
refInstanceof: window.HTMLDivElement,
render: (node) => {
return render(
<AlertDialog.Root open modal={false} animated={false}>
<AlertDialog.Root open animated={false}>
{node}
</AlertDialog.Root>,
);
Expand Down
3 changes: 2 additions & 1 deletion packages/mui-base/src/Checkbox/Root/useCheckboxRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
import { useFieldRootContext } from '../../Field/Root/FieldRootContext';
import { useFieldControlValidation } from '../../Field/Control/useFieldControlValidation';
import { useField } from '../../Field/useField';
import { getInertValue } from '../../utils/getInertValue';

export function useCheckboxRoot(params: UseCheckboxRoot.Parameters): UseCheckboxRoot.ReturnValue {
const {
Expand Down Expand Up @@ -126,7 +127,7 @@ export function useCheckboxRoot(params: UseCheckboxRoot.Parameters): UseCheckbox
type: 'checkbox',
'aria-hidden': true,
// @ts-ignore
inert: 'true',
inert: getInertValue(true),
onChange(event) {
// Workaround for https://github.com/facebook/react/issues/9023
if (event.nativeEvent.defaultPrevented) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ export function useCollapsibleContent(

const [height, setHeight] = React.useState(0);

const latestAnimationNameRef = React.useRef<string | undefined>('none');
const originalTransitionDurationStyleRef = React.useRef<string | undefined>();
const latestAnimationNameRef = React.useRef<string>('none');
const originalTransitionDurationStyleRef = React.useRef<string | null>(null);

const isTransitioningRef = React.useRef(false);

Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Dialog/Popup/useDialogPopup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function useDialogPopup(parameters: useDialogPopup.Parameters): useDialog
onOpenChange,
});

const popupRef = React.useRef<HTMLElement | null>(null);
const popupRef = React.useRef<HTMLElement>(null);

const dismiss = useDismiss(context, {
outsidePressEvent: 'mousedown',
Expand Down
10 changes: 5 additions & 5 deletions packages/mui-base/src/Field/Validity/FieldValidity.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('<Field.Validity />', () => {
fireEvent.change(input, { target: { value: 'test' } });
fireEvent.blur(input);

const [data] = handleValidity.args[8];
const [data] = handleValidity.lastCall.args;

expect(data.value).to.equal('test');
expect(data.validity.valueMissing).to.equal(false);
Expand All @@ -44,8 +44,8 @@ describe('<Field.Validity />', () => {
fireEvent.focus(input);
fireEvent.blur(input);

expect(handleValidity.args[6][0].error).to.equal('error');
expect(handleValidity.args[6][0].errors).to.deep.equal(['error']);
expect(handleValidity.lastCall.args[0].error).to.equal('error');
expect(handleValidity.lastCall.args[0].errors).to.deep.equal(['error']);
});

it('should correctly pass errors when validate function returns an array of strings', () => {
Expand All @@ -63,7 +63,7 @@ describe('<Field.Validity />', () => {
fireEvent.focus(input);
fireEvent.blur(input);

expect(handleValidity.args[6][0].error).to.equal('1');
expect(handleValidity.args[6][0].errors).to.deep.equal(['1', '2']);
expect(handleValidity.lastCall.args[0].error).to.equal('1');
expect(handleValidity.lastCall.args[0].errors).to.deep.equal(['1', '2']);
});
});
3 changes: 2 additions & 1 deletion packages/mui-base/src/Menu/Positioner/useMenuPositioner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
import { mergeReactProps } from '../../utils/mergeReactProps';
import { useAnchorPositioning } from '../../utils/useAnchorPositioning';
import type { GenericHTMLProps } from '../../utils/types';
import { getInertValue } from '../../utils/getInertValue';

export function useMenuPositioner(
params: useMenuPositioner.Parameters,
Expand Down Expand Up @@ -43,7 +44,7 @@ export function useMenuPositioner(
zIndex: 2147483647, // max z-index
},
'aria-hidden': !open || undefined,
inert: !open ? '' : undefined,
inert: getInertValue(!open),
});
},
[positionerStyles, open, keepMounted, hidden],
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/NoSsr/NoSsr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { NoSsrProps } from './NoSsr.types';
*
* - [NoSsr API](https://base-ui.netlify.app/components/react-no-ssr/#api-reference-NoSsr)
*/
function NoSsr(props: NoSsrProps): JSX.Element {
function NoSsr(props: NoSsrProps): React.JSX.Element {
const { children, defer = false, fallback = null } = props;
const [mountedState, setMountedState] = React.useState(false);

Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/NumberField/Root/useNumberFieldRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ export namespace UseNumberFieldRoot {
isScrubbing: boolean;
inputRef: ((instance: HTMLInputElement | null) => void) | null;
scrubHandleRef: React.RefObject<ScrubHandle | null>;
scrubAreaRef: React.RefObject<HTMLSpanElement>;
scrubAreaCursorRef: React.RefObject<HTMLSpanElement>;
scrubAreaRef: React.RefObject<HTMLSpanElement | null>;
scrubAreaCursorRef: React.RefObject<HTMLSpanElement | null>;
}
}
2 changes: 1 addition & 1 deletion packages/mui-base/src/NumberField/Root/useScrub.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export interface ScrubParams {
disabled: boolean;
readOnly: boolean;
value: number | null;
inputRef: React.RefObject<HTMLInputElement>;
inputRef: React.RefObject<HTMLInputElement | null>;
incrementValue: (amount: number, dir: 1 | -1, currentValue?: number | null) => void;
getStepAmount: () => number | undefined;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
import { mergeReactProps } from '../../utils/mergeReactProps';
import { useAnchorPositioning } from '../../utils/useAnchorPositioning';
import type { GenericHTMLProps } from '../../utils/types';
import { getInertValue } from '../../utils/getInertValue';

export function usePopoverPositioner(
params: usePopoverPositioner.Parameters,
Expand Down Expand Up @@ -38,7 +39,7 @@ export function usePopoverPositioner(
return mergeReactProps<'div'>(externalProps, {
role: 'presentation',
// @ts-ignore
inert: open ? undefined : 'true',
inert: getInertValue(open),
style: {
...positionerStyles,
...hiddenStyles,
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Popover/Root/PopoverRootContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export interface PopoverRootContext {
setTriggerElement: (el: Element | null) => void;
positionerElement: HTMLElement | null;
setPositionerElement: (el: HTMLElement | null) => void;
popupRef: React.RefObject<HTMLElement>;
popupRef: React.RefObject<HTMLElement | null>;
delay: number;
closeDelay: number;
delayType: 'rest' | 'hover';
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Popover/Root/usePopoverRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,6 @@ export namespace usePopoverRoot {
setTriggerElement: React.Dispatch<React.SetStateAction<Element | null>>;
positionerElement: HTMLElement | null;
setPositionerElement: React.Dispatch<React.SetStateAction<HTMLElement | null>>;
popupRef: React.RefObject<HTMLElement>;
popupRef: React.RefObject<HTMLElement | null>;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export interface PreviewCardRootContext {
getRootPopupProps: (externalProps?: GenericHTMLProps) => GenericHTMLProps;
floatingRootContext: FloatingRootContext;
transitionStatus: TransitionStatus;
popupRef: React.RefObject<HTMLElement>;
popupRef: React.RefObject<HTMLElement | null>;
}

export const PreviewCardRootContext = React.createContext<PreviewCardRootContext | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,6 @@ export namespace usePreviewCardRoot {
setTriggerElement: React.Dispatch<React.SetStateAction<Element | null>>;
positionerElement: HTMLElement | null;
setPositionerElement: React.Dispatch<React.SetStateAction<HTMLElement | null>>;
popupRef: React.RefObject<HTMLDivElement>;
popupRef: React.RefObject<HTMLDivElement | null>;
}
}
4 changes: 2 additions & 2 deletions packages/mui-base/src/Slider/Control/useSliderControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function useSliderControl(
const handleRootRef = useForkRef(externalRef, registerSliderControl, controlRef);

// A number that uniquely identifies the current finger in the touch session.
const touchIdRef = React.useRef<number>();
const touchIdRef = React.useRef<number | null>(null);

const moveCountRef = React.useRef(0);

Expand Down Expand Up @@ -123,7 +123,7 @@ export function useSliderControl(
onValueCommitted(newFingerValue.newValue, nativeEvent);
}

touchIdRef.current = undefined;
touchIdRef.current = null;

// eslint-disable-next-line @typescript-eslint/no-use-before-define
stopListening();
Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/Slider/Root/SliderRoot.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import type { FieldRootOwnerState } from '../../Field/Root/FieldRoot.types';

export interface SliderThumbMetadata {
inputId: string;
ref: React.RefObject<HTMLElement>;
inputRef: React.RefObject<HTMLInputElement>;
ref: React.RefObject<HTMLElement | null>;
inputRef: React.RefObject<HTMLInputElement | null>;
}

export type SliderContextValue = Omit<
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Slider/Root/useSliderRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ function useSliderRoot(parameters: UseSliderParameters): UseSliderReturnValue {

const isRtl = direction === 'rtl';

const previousIndexRef = React.useRef<number>();
const previousIndexRef = React.useRef<number | null>(null);
let axis = orientation;
if (isRtl && orientation === 'horizontal') {
axis += '-reverse';
Expand Down
20 changes: 14 additions & 6 deletions packages/mui-base/src/Slider/Thumb/SliderThumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useForkRef } from '../../utils/useForkRef';
import { useSliderContext } from '../Root/SliderProvider';
import { SliderThumbProps } from './SliderThumb.types';
import { useSliderThumb } from './useSliderThumb';
import { isReactVersionAtLeast } from '../../utils/reactVersion';

function defaultRender(
props: React.ComponentPropsWithRef<'span'>,
Expand Down Expand Up @@ -70,7 +71,12 @@ const SliderThumb = React.forwardRef(function SliderThumb(
values,
} = useSliderContext();

const mergedRef = useForkRef(typeof render === 'function' ? null : render.ref, forwardedRef);
let renderPropRef = null;
if (typeof render !== 'function') {
renderPropRef = isReactVersionAtLeast(19) ? (render.props as any).ref : render.ref;
}

const mergedRef = useForkRef(renderPropRef, forwardedRef);

const { getRootProps, getThumbInputProps, disabled, index } = useSliderThumb({
active: activeIndex,
Expand Down Expand Up @@ -114,13 +120,13 @@ const SliderThumb = React.forwardRef(function SliderThumb(
return render(thumbProps, inputProps, ownerState);
}

const { children: renderPropsChildren, ...otherRenderProps } = render.props;
const { children: renderPropsChildren, ...otherRenderProps } =
render.props as React.PropsWithChildren<unknown>;

const children = thumbProps.children ?? renderPropsChildren;

return React.cloneElement(
render,
mergeReactProps(otherRenderProps, {
return React.cloneElement(render, {
...mergeReactProps(otherRenderProps, {
...thumbProps,
children: (
<React.Fragment>
Expand All @@ -129,7 +135,9 @@ const SliderThumb = React.forwardRef(function SliderThumb(
</React.Fragment>
),
}),
);
// @ts-ignore
ref: thumbProps.ref,
});
});

SliderThumb.propTypes /* remove-proptypes */ = {
Expand Down
4 changes: 2 additions & 2 deletions packages/mui-base/src/Slider/Thumb/SliderThumb.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export interface SliderThumbProps
props: React.ComponentPropsWithRef<'span'>,
inputProps: React.ComponentPropsWithRef<'input'>,
state: SliderThumbOwnerState,
) => React.ReactElement)
| (React.ReactElement & { ref: React.Ref<Element> });
) => React.ReactElement<Record<string, unknown>>)
| (React.ReactElement<Record<string, unknown>> & { ref: React.Ref<Element> });
}

export interface UseSliderThumbParameters
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Tabs/Root/useTabsRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useControlled } from '../../utils/useControlled';
export interface TabMetadata {
disabled: boolean;
id: string | undefined;
ref: React.RefObject<HTMLElement>;
ref: React.RefObject<HTMLElement | null>;
}

type IdLookupFunction = (id: any) => string | undefined;
Expand Down
2 changes: 1 addition & 1 deletion packages/mui-base/src/Tabs/TabsList/TabsListContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as React from 'react';
export type TabsListContextValue = {
activateOnFocus: boolean;
getTabElement: (value: any) => HTMLElement | null;
tabsListRef: React.RefObject<HTMLElement>;
tabsListRef: React.RefObject<HTMLElement | null>;
};

export const TabsListContext = React.createContext<TabsListContextValue | undefined>(undefined);
Expand Down
Loading

0 comments on commit 1a29c11

Please sign in to comment.