From c9c7594946799e01948107c754076b8871858ed0 Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Thu, 18 Apr 2024 20:53:26 +0200 Subject: [PATCH 01/14] Add logic + doc --- docs/useInput.md | 20 ++++++++++---------- packages/ra-core/src/form/useInput.ts | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/useInput.md b/docs/useInput.md index 79fae12f116..b04ce3bd073 100644 --- a/docs/useInput.md +++ b/docs/useInput.md @@ -41,16 +41,16 @@ const TitleInput = ({ source, label }) => { ## Props -| Prop | Required | Type | Default | Description | -|----------------|----------|--------------------------------|---------|-------------------------------------------------------------------| -| `source` | Required | `string` | - | The name of the field in the record | -| `defaultValue` | Optional | `any` | - | The default value of the input | -| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | -| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | -| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | -| `id` | Optional | `string` | - | The id of the input | -| `onChange` | Optional | `Function` | - | A function to call when the input value changes | -| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | +| Prop | Required | Type | Default | Description | +|----------------|----------|--------------------------------|--------------------- |-------------------------------------------------------------------| +| `source` | Required | `string` | - | The name of the field in the record | +| `defaultValue` | Optional | `any` | - | The default value of the input | +| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | +| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | +| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | +| `id` | Optional | `string` | `use-input-[source]` | The id of the input | +| `onChange` | Optional | `Function` | - | A function to call when the input value changes | +| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | Additional props are passed to [react-hook-form's `useController` hook](https://react-hook-form.com/docs/usecontroller). diff --git a/packages/ra-core/src/form/useInput.ts b/packages/ra-core/src/form/useInput.ts index f0b56bf4356..ceb644d3a8b 100644 --- a/packages/ra-core/src/form/useInput.ts +++ b/packages/ra-core/src/form/useInput.ts @@ -132,7 +132,7 @@ export const useInput = ( }; return { - id: id || finalSource, + id: id || `use-input-${finalSource}`, field, fieldState, formState, From 97ef286b1be78dab3e5019bed058b983e79db51c Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Thu, 18 Apr 2024 20:55:42 +0200 Subject: [PATCH 02/14] WIP : how to test --- .../ra-core/src/form/useInput.stories.tsx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 packages/ra-core/src/form/useInput.stories.tsx diff --git a/packages/ra-core/src/form/useInput.stories.tsx b/packages/ra-core/src/form/useInput.stories.tsx new file mode 100644 index 00000000000..130bef255d9 --- /dev/null +++ b/packages/ra-core/src/form/useInput.stories.tsx @@ -0,0 +1,43 @@ +import * as React from 'react'; +import { CoreAdminContext } from '../core'; +import { Form } from './Form'; +import { useInput } from './useInput'; + +export default { + title: 'ra-core/form/useInput', +}; + +const Input = ({ source }) => { + const { id, field, fieldState } = useInput({ source }); + + return ( + + ); +}; + +export const Basic = () => { + const [submittedData, setSubmittedData] = React.useState(); + return ( + +
setSubmittedData(data)}> +
+ + + +
+ +
+
{JSON.stringify(submittedData, null, 2)}
+
+ ); +}; From 82680999ab78ebc254739618910b65639490ea62 Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Mon, 22 Apr 2024 18:13:06 +0200 Subject: [PATCH 03/14] fix cypress test --- cypress/support/CreatePage.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/support/CreatePage.js b/cypress/support/CreatePage.js index 584ef4ef0ac..02063506898 100644 --- a/cypress/support/CreatePage.js +++ b/cypress/support/CreatePage.js @@ -23,7 +23,7 @@ export default url => ({ title: '#react-admin-title', userMenu: 'button[aria-label="Profile"]', logout: '.logout', - nameError: '#name-helper-text', + nameError: '#use-input-name-helper-text', }, navigate() { From f11125cc8118c53029f8eda36389f5d9f4740afe Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Mon, 22 Apr 2024 18:19:55 +0200 Subject: [PATCH 04/14] fix spec tests --- .../ra-input-rich-text/src/RichTextInput.spec.tsx | 12 ++++++------ .../src/input/RadioButtonGroupInput.spec.tsx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx index 9b94f24b133..cdfb8c92de1 100644 --- a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx +++ b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx @@ -9,18 +9,18 @@ describe('', () => { const { container, rerender } = render(); await waitFor(() => { - expect(container.querySelector('#body')?.innerHTML).toEqual( - '

Hello world!

' - ); + expect( + container.querySelector('#use-input-body')?.innerHTML + ).toEqual('

Hello world!

'); }); const newRecord = { id: 123, body: '

Goodbye world!

' }; rerender(); await waitFor(() => { - expect(container.querySelector('#body')?.innerHTML).toEqual( - '

Goodbye world!

' - ); + expect( + container.querySelector('#use-input-body')?.innerHTML + ).toEqual('

Goodbye world!

'); }); }); }); diff --git a/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx b/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx index 664ba9531f6..ee3f4973e76 100644 --- a/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx @@ -86,9 +86,9 @@ describe('', () => { ); expect(screen.queryByText('People')).not.toBeNull(); const input1 = screen.getByLabelText('Leo Tolstoi'); - expect(input1.id).toBe('type_123'); + expect(input1.id).toBe('use-input-type_123'); const input2 = screen.getByLabelText('Jane Austen'); - expect(input2.id).toBe('type_456'); + expect(input2.id).toBe('use-input-type_456'); }); it('should trigger custom onChange when clicking radio button', async () => { From ddc2ff4e73c0dd783a32a813b85a053277fd86fa Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Sun, 28 Apr 2024 23:42:19 +0200 Subject: [PATCH 05/14] adapt tests --- packages/ra-core/src/form/useInput.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ra-core/src/form/useInput.spec.tsx b/packages/ra-core/src/form/useInput.spec.tsx index 603416bdb6e..e1a7e97c2e3 100644 --- a/packages/ra-core/src/form/useInput.spec.tsx +++ b/packages/ra-core/src/form/useInput.spec.tsx @@ -58,7 +58,7 @@ describe('useInput', () => { ); - expect(inputProps.id).toEqual('title'); + expect(inputProps.id).toEqual('use-input-title'); expect(inputProps.isRequired).toEqual(true); expect(inputProps.field).toBeDefined(); expect(inputProps.field.name).toEqual('title'); From 03ad6773c92a64c8e63cb48ddf1e58178604426f Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Mon, 29 Apr 2024 14:51:25 +0200 Subject: [PATCH 06/14] work with useId --- cypress/e2e/create.cy.js | 66 ++++++++++--------- cypress/support/CreatePage.js | 2 +- docs/useInput.md | 20 +++--- packages/ra-core/src/form/useInput.spec.tsx | 2 +- packages/ra-core/src/form/useInput.ts | 7 +- .../src/RichTextInput.spec.tsx | 12 ++-- .../src/input/RadioButtonGroupInput.spec.tsx | 4 +- 7 files changed, 59 insertions(+), 54 deletions(-) diff --git a/cypress/e2e/create.cy.js b/cypress/e2e/create.cy.js index d2fffde30a8..93d6f1b6d82 100644 --- a/cypress/e2e/create.cy.js +++ b/cypress/e2e/create.cy.js @@ -15,6 +15,40 @@ describe('Create Page', () => { CreatePage.waitUntilVisible(); }); + it('should validate unique fields', () => { + // wait for the page to load + cy.get('#rd-title').should('be.visible'); + CreatePage.logout(); + LoginPage.login('admin', 'password'); + + UserCreatePage.navigate(); + UserCreatePage.setValues([ + { + type: 'input', + name: 'name', + value: 'Annamarie Mayer', + }, + ]); + cy.get(UserCreatePage.elements.input('name')).blur(); + + cy.get(CreatePage.elements.nameError) + .should('exist') + .contains('Must be unique', { timeout: 10000 }); + + UserCreatePage.setValues([ + { + type: 'input', + name: 'name', + value: 'Annamarie NotMayer', + }, + ]); + cy.get(UserCreatePage.elements.input('name')).blur(); + + cy.get(CreatePage.elements.nameError) + .should('exist') + .should('not.contain', 'Must be unique', { timeout: 10000 }); + }); + it('should show the correct title in the appBar', () => { cy.get(CreatePage.elements.title).contains('Create Post'); }); @@ -373,36 +407,4 @@ describe('Create Page', () => { 'Test body' ); }); - - it('should validate unique fields', () => { - CreatePage.logout(); - LoginPage.login('admin', 'password'); - - UserCreatePage.navigate(); - UserCreatePage.setValues([ - { - type: 'input', - name: 'name', - value: 'Annamarie Mayer', - }, - ]); - cy.get(UserCreatePage.elements.input('name')).blur(); - - cy.get(CreatePage.elements.nameError) - .should('exist') - .contains('Must be unique', { timeout: 10000 }); - - UserCreatePage.setValues([ - { - type: 'input', - name: 'name', - value: 'Annamarie NotMayer', - }, - ]); - cy.get(UserCreatePage.elements.input('name')).blur(); - - cy.get(CreatePage.elements.nameError) - .should('exist') - .should('not.contain', 'Must be unique', { timeout: 10000 }); - }); }); diff --git a/cypress/support/CreatePage.js b/cypress/support/CreatePage.js index 02063506898..74c7f6bc849 100644 --- a/cypress/support/CreatePage.js +++ b/cypress/support/CreatePage.js @@ -23,7 +23,7 @@ export default url => ({ title: '#react-admin-title', userMenu: 'button[aria-label="Profile"]', logout: '.logout', - nameError: '#use-input-name-helper-text', + nameError: '#r23-name-helper-text', }, navigate() { diff --git a/docs/useInput.md b/docs/useInput.md index b04ce3bd073..fe0c76c6924 100644 --- a/docs/useInput.md +++ b/docs/useInput.md @@ -41,16 +41,16 @@ const TitleInput = ({ source, label }) => { ## Props -| Prop | Required | Type | Default | Description | -|----------------|----------|--------------------------------|--------------------- |-------------------------------------------------------------------| -| `source` | Required | `string` | - | The name of the field in the record | -| `defaultValue` | Optional | `any` | - | The default value of the input | -| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | -| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | -| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | -| `id` | Optional | `string` | `use-input-[source]` | The id of the input | -| `onChange` | Optional | `Function` | - | A function to call when the input value changes | -| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | +| Prop | Required | Type | Default | Description | +|----------------|----------|--------------------------------|--------------------------- |-------------------------------------------------------------------| +| `source` | Required | `string` | - | The name of the field in the record | +| `defaultValue` | Optional | `any` | - | The default value of the input | +| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | +| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | +| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | +| `id` | Optional | `string` | `r[input number]-[source]` | The id of the input | +| `onChange` | Optional | `Function` | - | A function to call when the input value changes | +| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | Additional props are passed to [react-hook-form's `useController` hook](https://react-hook-form.com/docs/usecontroller). diff --git a/packages/ra-core/src/form/useInput.spec.tsx b/packages/ra-core/src/form/useInput.spec.tsx index e1a7e97c2e3..ab704e4eb6a 100644 --- a/packages/ra-core/src/form/useInput.spec.tsx +++ b/packages/ra-core/src/form/useInput.spec.tsx @@ -58,7 +58,7 @@ describe('useInput', () => { ); - expect(inputProps.id).toEqual('use-input-title'); + expect(inputProps.id).toEqual('r0-title'); expect(inputProps.isRequired).toEqual(true); expect(inputProps.field).toBeDefined(); expect(inputProps.field.name).toEqual('title'); diff --git a/packages/ra-core/src/form/useInput.ts b/packages/ra-core/src/form/useInput.ts index ceb644d3a8b..a1a4178138e 100644 --- a/packages/ra-core/src/form/useInput.ts +++ b/packages/ra-core/src/form/useInput.ts @@ -1,4 +1,4 @@ -import { ReactElement, useEffect } from 'react'; +import { ReactElement, useEffect, useId } from 'react'; import { ControllerFieldState, ControllerRenderProps, @@ -44,6 +44,7 @@ export const useInput = ( const formGroupName = useFormGroupContext(); const formGroups = useFormGroups(); const record = useRecordContext(); + const defaultId = useId(); if ( !source && @@ -132,7 +133,9 @@ export const useInput = ( }; return { - id: id || `use-input-${finalSource}`, + id: + id || + `${defaultId.substring(1, defaultId.length - 1)}-${finalSource}`, field, fieldState, formState, diff --git a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx index cdfb8c92de1..818857954db 100644 --- a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx +++ b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx @@ -9,18 +9,18 @@ describe('', () => { const { container, rerender } = render(); await waitFor(() => { - expect( - container.querySelector('#use-input-body')?.innerHTML - ).toEqual('

Hello world!

'); + expect(container.querySelector('#r0-body')?.innerHTML).toEqual( + '

Hello world!

' + ); }); const newRecord = { id: 123, body: '

Goodbye world!

' }; rerender(); await waitFor(() => { - expect( - container.querySelector('#use-input-body')?.innerHTML - ).toEqual('

Goodbye world!

'); + expect(container.querySelector('#r0-body')?.innerHTML).toEqual( + '

Goodbye world!

' + ); }); }); }); diff --git a/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx b/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx index ee3f4973e76..4e254617469 100644 --- a/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx @@ -86,9 +86,9 @@ describe('', () => { ); expect(screen.queryByText('People')).not.toBeNull(); const input1 = screen.getByLabelText('Leo Tolstoi'); - expect(input1.id).toBe('use-input-type_123'); + expect(input1.id).toMatch(/r\d-type_123/); const input2 = screen.getByLabelText('Jane Austen'); - expect(input2.id).toBe('use-input-type_456'); + expect(input2.id).toMatch(/r\d-type_456/); }); it('should trigger custom onChange when clicking radio button', async () => { From 0fc59a037cb190fcf4cfb3eede9ffcc1d955d6e2 Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Mon, 29 Apr 2024 14:59:09 +0200 Subject: [PATCH 07/14] remove the 1st loading --- cypress/e2e/create.cy.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/cypress/e2e/create.cy.js b/cypress/e2e/create.cy.js index 93d6f1b6d82..fa5ec9e37de 100644 --- a/cypress/e2e/create.cy.js +++ b/cypress/e2e/create.cy.js @@ -16,8 +16,6 @@ describe('Create Page', () => { }); it('should validate unique fields', () => { - // wait for the page to load - cy.get('#rd-title').should('be.visible'); CreatePage.logout(); LoginPage.login('admin', 'password'); From 9cf621e6f0327e8e2d22217f0e26c40bfb973c8d Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Mon, 29 Apr 2024 15:15:41 +0200 Subject: [PATCH 08/14] fix cypress tests --- cypress/e2e/create.cy.js | 32 ++++++++++++++++++++++++++++++++ cypress/support/CreatePage.js | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/cypress/e2e/create.cy.js b/cypress/e2e/create.cy.js index fa5ec9e37de..6fcddc5a7fe 100644 --- a/cypress/e2e/create.cy.js +++ b/cypress/e2e/create.cy.js @@ -405,4 +405,36 @@ describe('Create Page', () => { 'Test body' ); }); + + it('should validate unique fields', () => { + CreatePage.logout(); + LoginPage.login('admin', 'password'); + + UserCreatePage.navigate(); + UserCreatePage.setValues([ + { + type: 'input', + name: 'name', + value: 'Annamarie Mayer', + }, + ]); + cy.get(UserCreatePage.elements.input('name')).blur(); + + cy.get(CreatePage.elements.nameError) + .should('exist') + .contains('Must be unique', { timeout: 10000 }); + + UserCreatePage.setValues([ + { + type: 'input', + name: 'name', + value: 'Annamarie NotMayer', + }, + ]); + cy.get(UserCreatePage.elements.input('name')).blur(); + + cy.get(CreatePage.elements.nameError) + .should('exist') + .should('not.contain', 'Must be unique', { timeout: 10000 }); + }); }); diff --git a/cypress/support/CreatePage.js b/cypress/support/CreatePage.js index 74c7f6bc849..80a517c7948 100644 --- a/cypress/support/CreatePage.js +++ b/cypress/support/CreatePage.js @@ -23,7 +23,7 @@ export default url => ({ title: '#react-admin-title', userMenu: 'button[aria-label="Profile"]', logout: '.logout', - nameError: '#r23-name-helper-text', + nameError: '.MuiFormHelperText-root', }, navigate() { From b74553e53f4cde359e585ed02903fa82244c438c Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Mon, 29 Apr 2024 17:34:03 +0200 Subject: [PATCH 09/14] just defaultId --- docs/useInput.md | 20 +++++++++---------- packages/ra-core/src/form/useInput.spec.tsx | 2 +- .../src/RichTextInput.spec.tsx | 4 ++-- .../src/input/RadioButtonGroupInput.spec.tsx | 5 +++-- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/useInput.md b/docs/useInput.md index fe0c76c6924..7d9b5480d68 100644 --- a/docs/useInput.md +++ b/docs/useInput.md @@ -41,16 +41,16 @@ const TitleInput = ({ source, label }) => { ## Props -| Prop | Required | Type | Default | Description | -|----------------|----------|--------------------------------|--------------------------- |-------------------------------------------------------------------| -| `source` | Required | `string` | - | The name of the field in the record | -| `defaultValue` | Optional | `any` | - | The default value of the input | -| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | -| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | -| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | -| `id` | Optional | `string` | `r[input number]-[source]` | The id of the input | -| `onChange` | Optional | `Function` | - | A function to call when the input value changes | -| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | +| Prop | Required | Type | Default | Description | +|----------------|----------|--------------------------------|-------------------- |-------------------------------------------------------------------| +| `source` | Required | `string` | - | The name of the field in the record | +| `defaultValue` | Optional | `any` | - | The default value of the input | +| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | +| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | +| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | +| `id` | Optional | `string` | `:r[input number]:` | The id of the input | +| `onChange` | Optional | `Function` | - | A function to call when the input value changes | +| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | Additional props are passed to [react-hook-form's `useController` hook](https://react-hook-form.com/docs/usecontroller). diff --git a/packages/ra-core/src/form/useInput.spec.tsx b/packages/ra-core/src/form/useInput.spec.tsx index ab704e4eb6a..cfdcc35330c 100644 --- a/packages/ra-core/src/form/useInput.spec.tsx +++ b/packages/ra-core/src/form/useInput.spec.tsx @@ -58,7 +58,7 @@ describe('useInput', () => { ); - expect(inputProps.id).toEqual('r0-title'); + expect(inputProps.id).toEqual(':r0:'); expect(inputProps.isRequired).toEqual(true); expect(inputProps.field).toBeDefined(); expect(inputProps.field.name).toEqual('title'); diff --git a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx index 818857954db..2e4e91dadb7 100644 --- a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx +++ b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx @@ -9,7 +9,7 @@ describe('', () => { const { container, rerender } = render(); await waitFor(() => { - expect(container.querySelector('#r0-body')?.innerHTML).toEqual( + expect(container.querySelector('#:r0:')?.innerHTML).toEqual( '

Hello world!

' ); }); @@ -18,7 +18,7 @@ describe('', () => { rerender(); await waitFor(() => { - expect(container.querySelector('#r0-body')?.innerHTML).toEqual( + expect(container.querySelector('#:r0:')?.innerHTML).toEqual( '

Goodbye world!

' ); }); diff --git a/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx b/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx index 4e254617469..5c9374b466f 100644 --- a/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/RadioButtonGroupInput.spec.tsx @@ -86,9 +86,10 @@ describe('', () => { ); expect(screen.queryByText('People')).not.toBeNull(); const input1 = screen.getByLabelText('Leo Tolstoi'); - expect(input1.id).toMatch(/r\d-type_123/); + expect(input1.id).toMatch(/:r\d:/); const input2 = screen.getByLabelText('Jane Austen'); - expect(input2.id).toMatch(/r\d-type_456/); + expect(input2.id).toMatch(/:r\d:/); + expect(input2.id).not.toEqual(input1.id); }); it('should trigger custom onChange when clicking radio button', async () => { From b125d7c5842169188a3fc78b397b794f344ee4f7 Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Tue, 30 Apr 2024 09:39:31 +0200 Subject: [PATCH 10/14] change doc default definition --- docs/useInput.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/useInput.md b/docs/useInput.md index 7d9b5480d68..389806b6168 100644 --- a/docs/useInput.md +++ b/docs/useInput.md @@ -41,16 +41,16 @@ const TitleInput = ({ source, label }) => { ## Props -| Prop | Required | Type | Default | Description | -|----------------|----------|--------------------------------|-------------------- |-------------------------------------------------------------------| -| `source` | Required | `string` | - | The name of the field in the record | -| `defaultValue` | Optional | `any` | - | The default value of the input | -| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | -| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | -| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | -| `id` | Optional | `string` | `:r[input number]:` | The id of the input | -| `onChange` | Optional | `Function` | - | A function to call when the input value changes | -| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | +| Prop | Required | Type | Default | Description | +|----------------|----------|--------------------------------|---------------- |-------------------------------------------------------------------| +| `source` | Required | `string` | - | The name of the field in the record | +| `defaultValue` | Optional | `any` | - | The default value of the input | +| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | +| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | +| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | +| `id` | Optional | `string` | `autogenerated` | The id of the input | +| `onChange` | Optional | `Function` | - | A function to call when the input value changes | +| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | Additional props are passed to [react-hook-form's `useController` hook](https://react-hook-form.com/docs/usecontroller). From d74a42ac6800c5dd9a1414b810b2f8cec2342d9d Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Tue, 30 Apr 2024 09:43:30 +0200 Subject: [PATCH 11/14] change input id --- packages/ra-core/src/form/useInput.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/ra-core/src/form/useInput.ts b/packages/ra-core/src/form/useInput.ts index a1a4178138e..9e0eca30db3 100644 --- a/packages/ra-core/src/form/useInput.ts +++ b/packages/ra-core/src/form/useInput.ts @@ -133,9 +133,7 @@ export const useInput = ( }; return { - id: - id || - `${defaultId.substring(1, defaultId.length - 1)}-${finalSource}`, + id: id || defaultId, field, fieldState, formState, From eca2b4d63724bb89d07af70326913613ad8c9cad Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Tue, 30 Apr 2024 09:53:18 +0200 Subject: [PATCH 12/14] fix RichTextInput tests --- packages/ra-input-rich-text/src/RichTextInput.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx index 2e4e91dadb7..8c353d17371 100644 --- a/packages/ra-input-rich-text/src/RichTextInput.spec.tsx +++ b/packages/ra-input-rich-text/src/RichTextInput.spec.tsx @@ -9,7 +9,7 @@ describe('', () => { const { container, rerender } = render(); await waitFor(() => { - expect(container.querySelector('#:r0:')?.innerHTML).toEqual( + expect(container.querySelector('.ProseMirror')?.innerHTML).toEqual( '

Hello world!

' ); }); @@ -18,7 +18,7 @@ describe('', () => { rerender(); await waitFor(() => { - expect(container.querySelector('#:r0:')?.innerHTML).toEqual( + expect(container.querySelector('.ProseMirror')?.innerHTML).toEqual( '

Goodbye world!

' ); }); From 85092d1c9b91f3be5adfd6d0983ff608a2b5a91f Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Tue, 30 Apr 2024 10:40:57 +0200 Subject: [PATCH 13/14] add an upgrade guide section --- docs/Upgrade.md | 6 ++++++ docs/useInput.md | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index 76cbb63b99d..db6e0a40ebc 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -924,6 +924,12 @@ The `` component no longer accepts a `touched` prop. This prop If you were using this prop, you can safely remove it. +## Inputs default ids are auto-generated + +In previous versions, the input default id was the source of the input. In v5, inputs defaults ids are auto-generated with [React useId()](https://react.dev/reference/react/useId). + +**Tip:** You still can pass an id as prop of any [react-admin input](./Inputs.md) or use a [reference](https://fr.react.dev/reference/react/useRef). + ## 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. diff --git a/docs/useInput.md b/docs/useInput.md index 389806b6168..bb41d2d1fbb 100644 --- a/docs/useInput.md +++ b/docs/useInput.md @@ -41,16 +41,16 @@ const TitleInput = ({ source, label }) => { ## Props -| Prop | Required | Type | Default | Description | -|----------------|----------|--------------------------------|---------------- |-------------------------------------------------------------------| -| `source` | Required | `string` | - | The name of the field in the record | -| `defaultValue` | Optional | `any` | - | The default value of the input | -| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | -| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | -| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | -| `id` | Optional | `string` | `autogenerated` | The id of the input | -| `onChange` | Optional | `Function` | - | A function to call when the input value changes | -| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | +| Prop | Required | Type | Default | Description | +|----------------|----------|--------------------------------|----------------- |-------------------------------------------------------------------| +| `source` | Required | `string` | - | The name of the field in the record | +| `defaultValue` | Optional | `any` | - | The default value of the input | +| `format` | Optional | `Function` | - | A function to format the value from the record to the input value | +| `parse` | Optional | `Function` | - | A function to parse the value from the input to the record value | +| `validate` | Optional | `Function` | `Function[]` | - | A function or an array of functions to validate the input value | +| `id` | Optional | `string` | `auto-generated` | The id of the input | +| `onChange` | Optional | `Function` | - | A function to call when the input value changes | +| `onBlur` | Optional | `Function` | - | A function to call when the input is blurred | Additional props are passed to [react-hook-form's `useController` hook](https://react-hook-form.com/docs/usecontroller). From 5cfa4b81d1284b9e01747446ef25828b2b48c529 Mon Sep 17 00:00:00 2001 From: erwanMarmelab Date: Tue, 30 Apr 2024 14:49:32 +0200 Subject: [PATCH 14/14] explain for tests --- docs/Upgrade.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Upgrade.md b/docs/Upgrade.md index c4710b5534f..765f53e88b6 100644 --- a/docs/Upgrade.md +++ b/docs/Upgrade.md @@ -934,6 +934,8 @@ In previous versions, the input default id was the source of the input. In v5, i **Tip:** You still can pass an id as prop of any [react-admin input](./Inputs.md) or use a [reference](https://fr.react.dev/reference/react/useRef). +If you were using inputs ids in your tests, you should pass your own id to the dedicated input. + ## 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.