Skip to content

Commit

Permalink
Merge pull request #9805 from marmelab/simple-form-iterator-no-cloneE…
Browse files Browse the repository at this point in the history
…lement

Avoid Cloning Buttons In SimpleFormIterator
  • Loading branch information
fzaninotto authored May 2, 2024
2 parents ab27b44 + 4b76f27 commit 2ae79e7
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 70 deletions.
31 changes: 31 additions & 0 deletions docs/Upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,37 @@ The `BulkActionProps` has been removed as it did not contain any prop. You can s

The `data-generator-retail` package has been updated to provide types for all its records. In the process, we renamed the `commands` resource to `orders`. Accordingly, the `nb_commands` property of the `customers` resource has been renamed to `nb_orders` and the `command_id` property of the `invoices` and `reviews` resources has been renamed to `order_id`.

## `<SimpleFormIterator>` No Longer Clones Its Buttons

`<SimpleFormIterator>` used to clones the add, remove and reorder buttons and inject some props to them such as `onClick` and `className`.
If you relied on those props in your custom buttons, you should now leverage the following hooks:

- `useSimpleFormIterator` for buttons that are not tied to an item such as the add button.

```diff
- import { Button, ButtonProps } from 'react-admin';
+ import { Button, ButtonProps, useSimpleFormIterator } from 'react-admin';

export const MyAddButton = (props: ButtonProps) => {
+ const { add } = useSimpleFormIterator();
- return <Button {...props}>Add</Button>;
+ return <Button {...props} onClick={() => add()}>Add</Button>;
}
```

- `useSimpleFormIteratorItem` for buttons that are tied to an item such as the remove and reorder buttons.

```diff
- import { Button, ButtonProps } from 'react-admin';
+ import { Button, ButtonProps, useSimpleFormIteratorItem } from 'react-admin';

export const MyRemoveButton = (props: ButtonProps) => {
+ const { remove } = useSimpleFormIteratorItem();
- return <Button {...props}>Add</Button>;
+ return <Button {...props} onClick={() => remove()}>Add</Button>;
}
```

## Upgrading to v4

If you are on react-admin v3, follow the [Upgrading to v4](https://marmelab.com/react-admin/doc/4.16/Upgrade.html) guide before upgrading to v5.
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import * as React from 'react';
import AddIcon from '@mui/icons-material/AddCircleOutline';
import clsx from 'clsx';
import { useSimpleFormIterator } from './useSimpleFormIterator';

import { IconButtonWithTooltip, ButtonProps } from '../../button';

export const AddItemButton = (props: ButtonProps) => {
const { add } = useSimpleFormIterator();
const { add, source } = useSimpleFormIterator();
const { className, ...rest } = props;
return (
<IconButtonWithTooltip
label="ra.action.add"
size="small"
onClick={() => add()}
color="primary"
{...props}
className={clsx(`button-add button-add-${source}`, className)}
{...rest}
>
<AddIcon fontSize="small" />
</IconButtonWithTooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import * as React from 'react';

import clsx from 'clsx';
import { IconButtonWithTooltip } from '../../button';
import ArrowUpwardIcon from '@mui/icons-material/ArrowCircleUp';
import ArrowDownwardIcon from '@mui/icons-material/ArrowCircleDown';
import { useSimpleFormIteratorItem } from './useSimpleFormIteratorItem';
import { useSimpleFormIterator } from './useSimpleFormIterator';

export const ReOrderButtons = ({ className }: { className?: string }) => {
const { index, total, reOrder } = useSimpleFormIteratorItem();
const { source } = useSimpleFormIterator();

return (
<span className={className}>
<span
className={clsx(
`button-reorder button-reorder-${source}-${index}`,
className
)}
>
<IconButtonWithTooltip
label="ra.action.move_up"
size="small"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import * as React from 'react';
import CloseIcon from '@mui/icons-material/RemoveCircleOutline';
import clsx from 'clsx';

import { IconButtonWithTooltip, ButtonProps } from '../../button';
import { useSimpleFormIteratorItem } from './useSimpleFormIteratorItem';
import { useSimpleFormIterator } from './useSimpleFormIterator';

export const RemoveItemButton = (props: Omit<ButtonProps, 'onClick'>) => {
const { remove } = useSimpleFormIteratorItem();
const { remove, index } = useSimpleFormIteratorItem();
const { source } = useSimpleFormIterator();
const { className, ...rest } = props;

return (
<IconButtonWithTooltip
label="ra.action.remove"
size="small"
onClick={() => remove()}
color="warning"
{...props}
className={clsx(
`button-remove button-remove-${source}-${index}`,
className
)}
{...rest}
>
<CloseIcon fontSize="small" />
</IconButtonWithTooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import * as React from 'react';
import {
Children,
cloneElement,
MouseEvent,
MouseEventHandler,
ReactElement,
ReactNode,
useCallback,
Expand Down Expand Up @@ -125,16 +122,6 @@ export const SimpleFormIterator = (inProps: SimpleFormIteratorProps) => {
[append, children, resetField, source, fields.length]
);

// add field and call the onClick event of the button passed as addButton prop
const handleAddButtonClick = (
originalOnClickHandler: MouseEventHandler
) => (event: MouseEvent) => {
addField();
if (originalOnClickHandler) {
originalOnClickHandler(event);
}
};

const handleReorder = useCallback(
(origin: number, destination: number) => {
move(origin, destination);
Expand Down Expand Up @@ -197,15 +184,7 @@ export const SimpleFormIterator = (inProps: SimpleFormIteratorProps) => {
<div className={SimpleFormIteratorClasses.buttons}>
{!disableAdd && (
<div className={SimpleFormIteratorClasses.add}>
{cloneElement(addButton, {
className: clsx(
'button-add',
`button-add-${source}`
),
onClick: handleAddButtonClick(
addButton.props.onClick
),
})}
{addButton}
</div>
)}
{fields.length > 0 && !disableClear && !disableRemove && (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import * as React from 'react';
import {
cloneElement,
MouseEvent,
MouseEventHandler,
ReactElement,
ReactNode,
useMemo,
} from 'react';
import { ReactElement, ReactNode, useMemo } from 'react';
import { Typography, Stack } from '@mui/material';
import clsx from 'clsx';
import {
Expand Down Expand Up @@ -41,7 +34,6 @@ export const SimpleFormIteratorItem = React.forwardRef(
record,
removeButton = <DefaultRemoveItemButton />,
reOrderButtons = <DefaultReOrderButtons />,
source,
} = props;
const resource = useResourceContext(props);
if (!resource) {
Expand All @@ -61,17 +53,6 @@ export const SimpleFormIteratorItem = React.forwardRef(
return disableRemove && disableRemove(record);
};

// remove field and call the onClick event of the button passed as removeButton prop
const handleRemoveButtonClick = (
originalOnClickHandler: MouseEventHandler,
index: number
) => (event: MouseEvent) => {
remove(index);
if (originalOnClickHandler) {
originalOnClickHandler(event);
}
};

const context = useMemo<SimpleFormIteratorItemContextValue>(
() => ({
index,
Expand Down Expand Up @@ -138,28 +119,9 @@ export const SimpleFormIteratorItem = React.forwardRef(
</SourceContextProvider>
{!disabled && (
<span className={SimpleFormIteratorClasses.action}>
{!disableReordering &&
cloneElement(reOrderButtons, {
index,
max: total,
reOrder,
className: clsx(
'button-reorder',
`button-reorder-${source}-${index}`
),
})}
{!disableReordering && reOrderButtons}

{!disableRemoveField(record) &&
cloneElement(removeButton, {
onClick: handleRemoveButtonClick(
removeButton.props.onClick,
index
),
className: clsx(
'button-remove',
`button-remove-${source}-${index}`
),
})}
{!disableRemoveField(record) && removeButton}
</span>
)}
</li>
Expand Down

0 comments on commit 2ae79e7

Please sign in to comment.