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
2 changes: 1 addition & 1 deletion packages/react/MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ npm install --save-dev @types/react@^19 @types/react-dom@^19
```

Follow the [official React 19 upgrade guide](https://react.dev/blog/2024/04/25/react-19-upgrade-guide) for the changes
it requires in your own code. The components and hooks of the library keep their current API.
it requires in your own code.

## 0.16.0 → 0.17.0

Expand Down
5 changes: 4 additions & 1 deletion packages/react/src/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AlertProps } from './Alert.types';

import clsx from 'clsx';
Expand All @@ -17,7 +19,7 @@ const defaultIconMapping = {
/**
* An alert displays a short, important message in a way that attracts the user's attention without interrupting the user's task.
*/
export const Alert = (inProps: AlertProps) => {
export const Alert = ({ ref, ...inProps }: AlertProps & RefAttributes<HTMLDivElement>) => {
const {
className,
children,
Expand All @@ -38,6 +40,7 @@ export const Alert = (inProps: AlertProps) => {

return (
<div
ref={ref}
className={clsx(
className,
'es-alert',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AlertActionsProps } from './AlertActions.types';

import clsx from 'clsx';
Expand All @@ -9,14 +11,14 @@ import { useDefaultProps } from '../../../theming';
/**
* @see `Alert`
*/
export const AlertActions = (inProps: AlertActionsProps) => {
export const AlertActions = ({ ref, ...inProps }: AlertActionsProps & RefAttributes<HTMLDivElement>) => {
const { className, children, style } = useDefaultProps({
props: inProps,
name: 'ESAlertActions',
});

return (
<div className={clsx(className, 'es-alert-actions')} style={style}>
<div ref={ref} className={clsx(className, 'es-alert-actions')} style={style}>
{children}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AlertCloseProps } from './AlertClose.types';

import clsx from 'clsx';
Expand All @@ -11,7 +13,7 @@ import { Button } from '../../Button';
/**
* @see `Alert`
*/
export const AlertClose = (inProps: AlertCloseProps) => {
export const AlertClose = ({ ref, ...inProps }: AlertCloseProps & RefAttributes<HTMLButtonElement>) => {
const {
className,
style,
Expand All @@ -27,6 +29,7 @@ export const AlertClose = (inProps: AlertCloseProps) => {

return (
<Button
ref={ref}
aria-label={label}
className={clsx(className, 'es-alert-close')}
color="tertiary"
Expand Down
6 changes: 4 additions & 2 deletions packages/react/src/components/Alert/AlertTitle/AlertTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AlertTitleProps } from './AlertTitle.types';

import clsx from 'clsx';
Expand All @@ -9,14 +11,14 @@ import { useDefaultProps } from '../../../theming';
/**
* @see `Alert`
*/
export const AlertTitle = (inProps: AlertTitleProps) => {
export const AlertTitle = ({ ref, ...inProps }: AlertTitleProps & RefAttributes<HTMLDivElement>) => {
const { className, children, style } = useDefaultProps({
props: inProps,
name: 'ESAlertTitle',
});

return (
<div className={clsx(className, 'es-alert-title', 'body100-w50')} style={style}>
<div ref={ref} className={clsx(className, 'es-alert-title', 'body100-w50')} style={style}>
{children}
</div>
);
Expand Down
9 changes: 5 additions & 4 deletions packages/react/src/components/AppBar/AppBar.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
'use client';

import { useRef, useState } from 'react';
import { RefAttributes, useRef, useState } from 'react';

import { AppBarProps } from './AppBar.types';

import clsx from 'clsx';

import { useIntersectionObserver, useResizeObserver, useStuckSentinel } from '../../hooks';
import { useForkRef, useIntersectionObserver, useResizeObserver, useStuckSentinel } from '../../hooks';
import { useDefaultProps } from '../../theming';

/**
* The AppBar displays information and actions relating to the current screen.
*/
export const AppBar = (inProps: AppBarProps) => {
export const AppBar = ({ ref: inRef, ...inProps }: AppBarProps & RefAttributes<HTMLDivElement>) => {
const { className, style, children, startAdornment, endAdornment, prominent } = useDefaultProps({
props: inProps,
name: 'ESAppBar',
});

const ref = useRef<HTMLDivElement | null>(null);
const prominentRef = useRef<HTMLDivElement | null>(null);
const rootRef = useForkRef(ref, inRef);

const [height, setHeight] = useState(0);
const [prominentHeight, setProminentHeight] = useState(0);
Expand Down Expand Up @@ -48,7 +49,7 @@ export const AppBar = (inProps: AppBarProps) => {
return (
<>
{sentinel}
<div ref={ref} className={clsx(className, 'es-app-bar', stuck && 'es-app-bar--stuck', 'h6')} style={style}>
<div ref={rootRef} className={clsx(className, 'es-app-bar', stuck && 'es-app-bar--stuck', 'h6')} style={style}>
{startAdornment && <div className="es-app-bar__adornment">{startAdornment}</div>}
<div className="es-app-bar__title">{!prominent || isTitleVisible ? children : '\u00A0'}</div>
{endAdornment && <div className="es-app-bar__adornment">{endAdornment}</div>}
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/components/AudioPlayer/AudioPlayer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import React, { RefAttributes, useEffect, useImperativeHandle, useRef, useState } from 'react';

import { AudioPlayerProps } from './AudioPlayer.types';

Expand Down Expand Up @@ -48,7 +48,7 @@ const AudioPlayerTimeValue = ({ time }: { time: number }) => {
/**
* This component is used to embed sound content in documents.
*/
export const AudioPlayer = (inProps: AudioPlayerProps) => {
export const AudioPlayer = ({ ref, ...inProps }: AudioPlayerProps & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand Down Expand Up @@ -645,7 +645,7 @@ export const AudioPlayer = (inProps: AudioPlayerProps) => {
const isOverTrack = duration * hover < current;

return (
<div className={clsx('es-audio-player', className)} style={style}>
<div ref={ref} className={clsx('es-audio-player', className)} style={style}>
<Button
aria-label={isPlaying ? labelPause : labelPlay}
className="es-audio-player__icon-button"
Expand Down
8 changes: 5 additions & 3 deletions packages/react/src/components/Autocomplete/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { RefAttributes, useCallback, useEffect, useMemo, useRef, useState } from 'react';

import { AutocompleteProps } from './Autocomplete.types';

Expand All @@ -13,7 +13,7 @@ import { AutocompleteMenu, AutocompleteMenuImperativeActions } from '../Autocomp
import { FormFieldAdornment, FormFieldField, useFormFieldContext } from '../FormField';

/** The autocomplete is used to choose an item from a collection of options. */
export const Autocomplete = <T,>(inProps: AutocompleteProps<T>) => {
export const Autocomplete = <T,>({ ref: inRef, ...inProps }: AutocompleteProps<T> & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand Down Expand Up @@ -71,6 +71,8 @@ export const Autocomplete = <T,>(inProps: AutocompleteProps<T>) => {
setAnchorEl(node);
}, []);

const fieldRef = useForkRef(setField, inRef);

const paperRef = useRef<HTMLDivElement | null>(null);
const inputRef = useRef<HTMLDivElement | null>(null);
// The field focuses whatever sits in the context ref when its padding is clicked, so the control registers itself.
Expand Down Expand Up @@ -231,7 +233,7 @@ export const Autocomplete = <T,>(inProps: AutocompleteProps<T>) => {
return (
<>
<FormFieldField
ref={setField}
ref={fieldRef}
className={clsx(className, 'es-autocomplete')}
endAdornment={!!endAdornment && <FormFieldAdornment position="end">{endAdornment}</FormFieldAdornment>}
label={label}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AutocompleteFieldProps } from './AutocompleteField.types';

import clsx from 'clsx';
Expand All @@ -10,7 +12,10 @@ import { Autocomplete } from '../Autocomplete';
import { FormField, FormFieldHelperText, FormFieldLabel } from '../FormField';

/** The autocomplete is used to choose an item from a collection of options. */
export const AutocompleteField = <T,>(inProps: AutocompleteFieldProps<T>) => {
export const AutocompleteField = <T,>({
ref,
...inProps
}: AutocompleteFieldProps<T> & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand Down Expand Up @@ -62,6 +67,7 @@ export const AutocompleteField = <T,>(inProps: AutocompleteFieldProps<T>) => {

return (
<FormField
ref={ref}
className={clsx('es-autocomplete-field', className)}
disabled={disabled}
error={error}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
'use client';

import {
ForwardedRef,
forwardRef,
JSX,
KeyboardEvent,
MutableRefObject,
ReactNode,
RefAttributes,
RefObject,
useCallback,
useImperativeHandle,
Expand Down Expand Up @@ -45,7 +43,7 @@ import { flip, hide, limitShift, Middleware, offset as offsetMiddleware, shift }
/**
* A dropdown menu that provides the popup layer for autocomplete-style fields.
*/
export const AutocompleteMenu = forwardRef(function AutocompleteMenu(inProps, ref) {
export const AutocompleteMenu = (({ ref, ...inProps }: AutocompleteMenuProps<any> & RefAttributes<HTMLDivElement>) => {
const {
paperRef,

Expand Down Expand Up @@ -236,8 +234,8 @@ export const AutocompleteMenu = forwardRef(function AutocompleteMenu(inProps, re
ref,
childrenRef,
}: {
ref?: MutableRefObject<HTMLElement | null>;
childrenRef?: MutableRefObject<HTMLElement | null>;
ref?: RefObject<HTMLElement | null>;
childrenRef?: RefObject<HTMLElement | null>;
} = {}) => (
<MenuItem
ref={ref && (ref as RefObject<HTMLLIElement>)}
Expand Down Expand Up @@ -426,4 +424,4 @@ export const AutocompleteMenu = forwardRef(function AutocompleteMenu(inProps, re
</Popper>
);
// eslint-disable-next-line no-use-before-define
}) as <T>(props: AutocompleteMenuProps<T> & { ref?: ForwardedRef<HTMLDivElement> }) => JSX.Element;
}) as <T>(props: AutocompleteMenuProps<T> & RefAttributes<HTMLDivElement>) => JSX.Element;
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AutocompleteMenuFooterProps } from './AutocompleteMenuFooter.types';

import clsx from 'clsx';
Expand All @@ -9,7 +11,10 @@ import { useDefaultProps } from '../../../theming';
/**
* @see `AutocompleteMenu`
*/
export const AutocompleteMenuFooter = (inProps: AutocompleteMenuFooterProps) => {
export const AutocompleteMenuFooter = ({
ref,
...inProps
}: AutocompleteMenuFooterProps & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand All @@ -21,7 +26,7 @@ export const AutocompleteMenuFooter = (inProps: AutocompleteMenuFooterProps) =>
});

return (
<div className={clsx(className, 'es-autocomplete-menu-footer', 'caption')} style={style}>
<div ref={ref} className={clsx(className, 'es-autocomplete-menu-footer', 'caption')} style={style}>
{children}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use client';

import { RefAttributes } from 'react';

import { AutocompleteMenuHeaderProps } from './AutocompleteMenuHeader.types';

import clsx from 'clsx';
Expand All @@ -9,7 +11,10 @@ import { useDefaultProps } from '../../../theming';
/**
* @see `AutocompleteMenu`
*/
export const AutocompleteMenuHeader = (inProps: AutocompleteMenuHeaderProps) => {
export const AutocompleteMenuHeader = ({
ref,
...inProps
}: AutocompleteMenuHeaderProps & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand All @@ -21,7 +26,7 @@ export const AutocompleteMenuHeader = (inProps: AutocompleteMenuHeaderProps) =>
});

return (
<div className={clsx(className, 'es-autocomplete-menu-header', 'caption')} style={style}>
<div ref={ref} className={clsx(className, 'es-autocomplete-menu-header', 'caption')} style={style}>
{children}
</div>
);
Expand Down
5 changes: 3 additions & 2 deletions packages/react/src/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { CSSProperties, ReactNode, useEffect, useState } from 'react';
import { CSSProperties, ReactNode, RefAttributes, useEffect, useState } from 'react';

import { AvatarProps } from './Avatar.types';

Expand Down Expand Up @@ -48,7 +48,7 @@ function useLoaded(src: string): 'loaded' | 'error' | null {
}

/** Avatar is used to represent users or things. */
export const Avatar = (inProps: AvatarProps) => {
export const Avatar = ({ ref, ...inProps }: AvatarProps & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand Down Expand Up @@ -78,6 +78,7 @@ export const Avatar = (inProps: AvatarProps) => {

return (
<div
ref={ref}
className={clsx(className, 'es-avatar', `es-avatar--variant--${variant}`, outlined && 'es-avatar--outlined')}
style={{ '--es-avatar-size': `${size}px`, ...style } as CSSProperties}
>
Expand Down
5 changes: 3 additions & 2 deletions packages/react/src/components/AvatarGroup/AvatarGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import React, { CSSProperties } from 'react';
import React, { CSSProperties, RefAttributes } from 'react';

import { AvatarGroupProps } from './AvatarGroup.types';

Expand All @@ -13,7 +13,7 @@ import { useDefaultProps } from '../../theming';
/**
* AvatarGroup renders its children as a stack.
*/
export const AvatarGroup = (inProps: AvatarGroupProps) => {
export const AvatarGroup = ({ ref, ...inProps }: AvatarGroupProps & RefAttributes<HTMLDivElement>) => {
const {
className,
style,
Expand All @@ -29,6 +29,7 @@ export const AvatarGroup = (inProps: AvatarGroupProps) => {

return (
<div
ref={ref}
className={clsx('es-avatar-group', `es-avatar-group--direction--${direction}`, className)}
style={
{
Expand Down
Loading
Loading