Skip to content

Conversation

djhi
Copy link
Collaborator

@djhi djhi commented Sep 23, 2025

Problem

When implementing new UI (e.g. ShadnAdminKit), we have to repeat a lot of code that could be in ra-core.

Solution

Introduce <ArrayInputBase>, <SimpleFomIteratorBase> and <SimpleFormIteratorItemBase>

How To Test

  • All tests should pass
  • All stories should behave as before

Additional Checks

  • The PR targets master for a bugfix or a documentation fix, or next for a feature
  • The PR includes unit tests (if not possible, describe why)
  • The PR includes one or several stories (if not possible, describe why)
  • The documentation is up to date

Also, please make sure to read the contributing guidelines.

});

return (
<SimpleFormIteratorBase {...props} onAddItem={handleAddItem}>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<SimpleFormIteratorBase {...props} onAddItem={handleAddItem}>
<SimpleFormIteratorBase {...props} getItemDefaults={getItemDefaults}>

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces three new base components (<ArrayInputBase>, <SimpleFormIteratorBase>, and <SimpleFormIteratorItemBase>) to facilitate code reuse when implementing custom UI libraries. These components extract the core logic from existing UI-specific components to ra-core, enabling developers to build new UI implementations without duplicating business logic.

  • Introduces new base components in ra-core for ArrayInput and SimpleFormIterator functionality
  • Refactors existing Material-UI components to use the new base components
  • Adds comprehensive test-ui components for testing the new functionality

Reviewed Changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/ra-core/src/controller/input/ArrayInputBase.tsx New base component for array input functionality
packages/ra-core/src/controller/input/SimpleFormIteratorBase.tsx New base component for form iterator logic
packages/ra-core/src/controller/input/SimpleFormIteratorItemBase.tsx New base component for iterator item functionality
packages/ra-ui-materialui/src/input/ArrayInput/ArrayInput.tsx Refactored to use ArrayInputBase
packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIterator.tsx Refactored to use SimpleFormIteratorBase
packages/ra-ui-materialui/src/input/ArrayInput/SimpleFormIteratorItem.tsx Refactored to use SimpleFormIteratorItemBase
packages/ra-core/src/test-ui/* New test UI components for testing base components
docs_headless/src/content/docs/ArrayInputBase.md Documentation for new ArrayInputBase component

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

export * from './SimpleFormIterator';
export * from './SimpleShowLayout';
export * from './TextInput';
export * from './TextInput';
Copy link
Preview

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate export of './TextInput' on lines 8 and 9. Remove one of these duplicate exports.

Suggested change
export * from './TextInput';

Copilot uses AI. Check for mistakes.


return (
<div>
{}
Copy link
Preview

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty object literal serves no purpose and should be removed.

Suggested change
{}

Copilot uses AI. Check for mistakes.

Comment on lines 98 to 100
// @deprecated use useSimpleFormIteratorItem().remove instead
onRemoveField?: (index: number) => void;
// @deprecated use useSimpleFormIteratorItem().reOrder instead
Copy link
Preview

Copilot AI Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space in deprecation comment. Should be '// @deprecated Use useSimpleFormIteratorItem().remove instead' and '// @deprecated Use useSimpleFormIteratorItem().reOrder instead'.

Suggested change
// @deprecated use useSimpleFormIteratorItem().remove instead
onRemoveField?: (index: number) => void;
// @deprecated use useSimpleFormIteratorItem().reOrder instead
// @deprecated Use useSimpleFormIteratorItem().remove instead
onRemoveField?: (index: number) => void;
// @deprecated Use useSimpleFormIteratorItem().reOrder instead

Copilot uses AI. Check for mistakes.

@djhi djhi added RFR Ready For Review and removed WIP Work In Progress labels Sep 29, 2025
@slax57 slax57 self-requested a review October 1, 2025 07:57
Comment on lines +49 to +50
<div>Tags:</div>
<ArrayInputBase source="tags">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<div>Tags:</div>
<ArrayInputBase source="tags">
<div>Items:</div>
<ArrayInputBase source="items">

)
```

**Note**: Setting [`shouldUnregister`](https://react-hook-form.com/docs/useform#shouldUnregister) on a form should be avoided when using `<ArrayInputBase>` (which internally uses `useFieldArray`) as the unregister function gets called after input unmount/remount and reorder. This limitation is mentioned in the react-hook-form [documentation](https://react-hook-form.com/docs/usecontroller#props). If you are in such a situation, you can use the [`transform`](./EditBase.html#transform) prop to manually clean the submitted values.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Note**: Setting [`shouldUnregister`](https://react-hook-form.com/docs/useform#shouldUnregister) on a form should be avoided when using `<ArrayInputBase>` (which internally uses `useFieldArray`) as the unregister function gets called after input unmount/remount and reorder. This limitation is mentioned in the react-hook-form [documentation](https://react-hook-form.com/docs/usecontroller#props). If you are in such a situation, you can use the [`transform`](./EditBase.html#transform) prop to manually clean the submitted values.
**Note**: Setting [`shouldUnregister`](https://react-hook-form.com/docs/useform#shouldUnregister) on a form should be avoided when using `<ArrayInputBase>` (which internally uses `useFieldArray`) as the unregister function gets called after input unmount/remount and reorder. This limitation is mentioned in the react-hook-form [documentation](https://react-hook-form.com/docs/usecontroller#props). If you are in such a situation, you can use the [`transform`](./EditBase.md#transform) prop to manually clean the submitted values.

Comment on lines +68 to +74
| Prop | Required | Type | Default | Description |
|-----------------| -------- |---------------------------| ------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `source` | Required | `string` | - | Name of the entity property to use for the input value |
| `defaultValue` | Optional | `any` | - | Default value of the input. |
| `readOnly` | Optional | `boolean` | `false` | If true, the input is in read-only mode. |
| `disabled` | Optional | `boolean` | `false` | If true, the input is disabled. |
| `validate` | Optional | `Function` &#124; `array` | - | Validation rules for the current property. See the [Validation Documentation](./Validation.md#per-input-validation-built-in-field-validators) for details. | No newline at end of file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isPending and loading are not documented. Is this intended?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That actually makes me wonder whether they should be there at all

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed

Comment on lines +1 to +4
---
layout: default
title: "<ArrayInputBase>"
---
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot to add this file (and the other one too) to the sidebar navigation.

{fields.map((member, index) => (
<SimpleFormIteratorItemBase
key={member.id}
fields={fields}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If my understanding is correct, passing fields here is no longer necessary since the getItemDefaults prop was introduced. Am I correct?

Comment on lines +6 to +8
export const useGetArrayInputNewItemDefaults = (
fields: ArrayInputContextValue['fields']
) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you check if this code is still necessary with latest versions of RHF?

Actually I did test adding a defaultValue on a input in the story in ra-core (i.e. which does not leverage this helper), and the default value was applied alright, which tends to indicate this whole function may no longer be necessary.

Worth checking out IMHO.

PS: this could be an interesting story to add btw

Copy link
Collaborator Author

@djhi djhi Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not really work. The should not reapply default values set at form level after removing and then re-adding one row, even with FormDataConsumer test fails because RHF restore the value of the removed item when you add a new one.
So yes, I'm afraid this code is still necessary.

Comment on lines +116 to +119
<SimpleFormIteratorClearButton
disableClear={disableClear}
disableRemove={disableRemove}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You did not report the SimpleFormIteratorClasses.clear className. Is that intended? 🤔

remove: () => remove(index),
}),
[index, total, reOrder, remove]
const record = useRecordContext();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const record = useRecordContext();
const record = useRecordContext(props);

Otherwise it's a BC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RFR Ready For Review
Development

Successfully merging this pull request may close these issues.

3 participants