From a8da321d5b83fe2f4f211c1fdf4d9ffda8618717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Wed, 14 Sep 2022 10:20:19 -0300 Subject: [PATCH 01/37] Remove emptyValue prop from SelectInput --- packages/ra-ui-materialui/src/input/SelectInput.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/SelectInput.tsx b/packages/ra-ui-materialui/src/input/SelectInput.tsx index db23f23a876..1c4fe8c26c9 100644 --- a/packages/ra-ui-materialui/src/input/SelectInput.tsx +++ b/packages/ra-ui-materialui/src/input/SelectInput.tsx @@ -115,7 +115,6 @@ export const SelectInput = (props: SelectInputProps) => { defaultValue = '', disableValue, emptyText, - emptyValue, format, filter, helperText, @@ -306,7 +305,7 @@ export const SelectInput = (props: SelectInputProps) => { > {!isRequired && ( { SelectInput.propTypes = { emptyText: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), - emptyValue: PropTypes.any, choices: PropTypes.arrayOf(PropTypes.object), className: PropTypes.string, label: PropTypes.oneOfType([ @@ -347,7 +345,6 @@ SelectInput.propTypes = { SelectInput.defaultProps = { emptyText: '', - emptyValue: '', options: {}, translateChoice: true, disableValue: 'disabled', @@ -407,7 +404,6 @@ export type SelectInputProps = Omit & Omit & { disableValue?: string; emptyText?: string | ReactElement; - emptyValue?: any; resettable?: boolean; // Source is optional as AutocompleteInput can be used inside a ReferenceInput that already defines the source source?: string; From 5910a5c831c773dd50c652c3c96c1dfe1bf6a567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Wed, 14 Sep 2022 10:20:54 -0300 Subject: [PATCH 02/37] Remove emptyValue and emptyText props from AutocompleteInput --- packages/ra-core/src/form/useSuggestions.ts | 16 ++-------------- .../src/input/AutocompleteInput.tsx | 4 ---- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/packages/ra-core/src/form/useSuggestions.ts b/packages/ra-core/src/form/useSuggestions.ts index 3ad791c8d61..75d9eb758da 100644 --- a/packages/ra-core/src/form/useSuggestions.ts +++ b/packages/ra-core/src/form/useSuggestions.ts @@ -8,13 +8,11 @@ import { useTranslate } from '../i18n'; * * @param allowDuplicates A boolean indicating whether a suggestion can be added several times * @param choices An array of available choices - * @param emptyText The text to use for the empty suggestion. Defaults to an empty string - * @param emptyValue The value to use for the empty suggestion. Defaults to `null` * @param limitChoicesToValue A boolean indicating whether the initial suggestions should be limited to the currently selected one(s) * @param matchSuggestion Optional unless `optionText` is a React element. Function which check whether a choice matches a filter. Must return a boolean. * @param optionText Either a string defining the property to use to get the choice text, a function or a React element * @param optionValue The property to use to get the choice value - * @param selectedItem The currently selected item. May be an array of selected items + * @param selectedItem The currently selected item. Maybe an array of selected items * @param suggestionLimit The maximum number of suggestions returned * @param translateChoice A boolean indicating whether to option text should be translated * @@ -28,8 +26,6 @@ export const useSuggestions = ({ choices, createText = 'ra.action.create', createValue = '@@create', - emptyText = '', - emptyValue = null, limitChoicesToValue, matchSuggestion, optionText, @@ -52,8 +48,6 @@ export const useSuggestions = ({ choices, createText, createValue, - emptyText: translate(emptyText, { _: emptyText }), - emptyValue, getChoiceText, getChoiceValue, limitChoicesToValue, @@ -68,8 +62,6 @@ export const useSuggestions = ({ choices, createText, createValue, - emptyText, - emptyValue, getChoiceText, getChoiceValue, limitChoicesToValue, @@ -98,8 +90,6 @@ export interface UseSuggestionsOptions extends UseChoicesOptions { choices: any[]; createText?: string; createValue?: any; - emptyText?: string; - emptyValue?: any; limitChoicesToValue?: boolean; matchSuggestion?: ( filter: string, @@ -160,8 +150,6 @@ export const getSuggestionsFactory = ({ choices = [], createText = 'ra.action.create', createValue = '@@create', - emptyText = '', - emptyValue = null, optionText = 'name', optionValue = 'id', getChoiceText, @@ -263,7 +251,7 @@ const limitSuggestions = (suggestions: any[], limit: any = 0) => * [{ id: 1, name: 'foo'}, { id: 2, name: 'bar' }], * ); * - * // Will return [{ id: null, name: '' }, { id: 1, name: 'foo' }, , { id: 2, name: 'bar' }] + * // Will return [{ id: null, name: '' }, { id: 1, name: 'foo' }, { id: 2, name: 'bar' }] * * @param suggestions List of suggestions * @param options diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 5f01ebbe1fd..d5173340201 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -136,8 +136,6 @@ export const AutocompleteInput = < createValue, debounce: debounceDelay = 250, defaultValue = '', - emptyText, - emptyValue = '', field: fieldOverride, format, helperText, @@ -252,8 +250,6 @@ If you provided a React element for the optionText prop, you must also provide t const { getChoiceText, getChoiceValue, getSuggestions } = useSuggestions({ choices: allChoices, - emptyText, - emptyValue, limitChoicesToValue, matchSuggestion, optionText: From f89978d2a2c518e1c6706b9f36405b2ef4d0ba75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Wed, 14 Sep 2022 10:28:48 -0300 Subject: [PATCH 03/37] Update docs --- docs/AutocompleteInput.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 40e945a0493..ecee4dc9924 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -41,8 +41,6 @@ import { AutocompleteInput, ReferenceInput } from 'react-admin'; | `choices` | Optional | `Object[]` | `-` | List of items to autosuggest. Required if not inside a ReferenceInput. | | `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | | `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | -| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element | -| `emptyText` | Optional | `string` | `''` | The text to use for the empty element | | `matchSuggestion` | Optional | `Function` | `-` | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | | `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | | `optionText` | Optional | `string` | `Function` | `Component` | `name` | Field name of record to display in the suggestion item or function which accepts the correct record as argument (`(record)=> {string}`) | From 987ce9d1a651bc2a63f79a562d4e03fa1251ffac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Wed, 14 Sep 2022 11:18:10 -0300 Subject: [PATCH 04/37] Revert changes --- packages/ra-ui-materialui/src/input/SelectInput.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/SelectInput.tsx b/packages/ra-ui-materialui/src/input/SelectInput.tsx index 1c4fe8c26c9..db23f23a876 100644 --- a/packages/ra-ui-materialui/src/input/SelectInput.tsx +++ b/packages/ra-ui-materialui/src/input/SelectInput.tsx @@ -115,6 +115,7 @@ export const SelectInput = (props: SelectInputProps) => { defaultValue = '', disableValue, emptyText, + emptyValue, format, filter, helperText, @@ -305,7 +306,7 @@ export const SelectInput = (props: SelectInputProps) => { > {!isRequired && ( { SelectInput.propTypes = { emptyText: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), + emptyValue: PropTypes.any, choices: PropTypes.arrayOf(PropTypes.object), className: PropTypes.string, label: PropTypes.oneOfType([ @@ -345,6 +347,7 @@ SelectInput.propTypes = { SelectInput.defaultProps = { emptyText: '', + emptyValue: '', options: {}, translateChoice: true, disableValue: 'disabled', @@ -404,6 +407,7 @@ export type SelectInputProps = Omit & Omit & { disableValue?: string; emptyText?: string | ReactElement; + emptyValue?: any; resettable?: boolean; // Source is optional as AutocompleteInput can be used inside a ReferenceInput that already defines the source source?: string; From 74d79a2b275058334505b99816e9420f963b4cd9 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 19 Sep 2022 14:38:36 -0300 Subject: [PATCH 05/37] Add emptyText and emptyValue --- packages/ra-core/src/form/useSuggestions.ts | 21 ++++++++++- .../src/input/AutocompleteInput.stories.tsx | 35 +++++++++++++++++++ .../src/input/AutocompleteInput.tsx | 25 ++++++++----- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/packages/ra-core/src/form/useSuggestions.ts b/packages/ra-core/src/form/useSuggestions.ts index 75d9eb758da..253ed0e9429 100644 --- a/packages/ra-core/src/form/useSuggestions.ts +++ b/packages/ra-core/src/form/useSuggestions.ts @@ -8,6 +8,8 @@ import { useTranslate } from '../i18n'; * * @param allowDuplicates A boolean indicating whether a suggestion can be added several times * @param choices An array of available choices + * @param emptyText The text to use for the empty suggestion. Defaults to an empty string + * @param emptyValue The value to use for the empty suggestion. Defaults to `null` * @param limitChoicesToValue A boolean indicating whether the initial suggestions should be limited to the currently selected one(s) * @param matchSuggestion Optional unless `optionText` is a React element. Function which check whether a choice matches a filter. Must return a boolean. * @param optionText Either a string defining the property to use to get the choice text, a function or a React element @@ -26,6 +28,8 @@ export const useSuggestions = ({ choices, createText = 'ra.action.create', createValue = '@@create', + emptyText = '', + emptyValue = null, limitChoicesToValue, matchSuggestion, optionText, @@ -48,6 +52,8 @@ export const useSuggestions = ({ choices, createText, createValue, + emptyText: translate(emptyText, { _: emptyText }), + emptyValue, getChoiceText, getChoiceValue, limitChoicesToValue, @@ -62,6 +68,8 @@ export const useSuggestions = ({ choices, createText, createValue, + emptyText, + emptyValue, getChoiceText, getChoiceValue, limitChoicesToValue, @@ -90,6 +98,8 @@ export interface UseSuggestionsOptions extends UseChoicesOptions { choices: any[]; createText?: string; createValue?: any; + emptyText?: string; + emptyValue?: any; limitChoicesToValue?: boolean; matchSuggestion?: ( filter: string, @@ -150,6 +160,8 @@ export const getSuggestionsFactory = ({ choices = [], createText = 'ra.action.create', createValue = '@@create', + emptyText = '', + emptyValue = '', optionText = 'name', optionValue = 'id', getChoiceText, @@ -162,7 +174,14 @@ export const getSuggestionsFactory = ({ getChoiceText: (choice: any) => string | ReactElement; getChoiceValue: (choice: any) => string; }) => filter => { - let suggestions: any[] = []; + let suggestions: any[] = [ + getSuggestion({ + optionText, + optionValue, + text: emptyText, + value: emptyValue, + }), + ]; // if an item is selected and matches the filter if ( selectedItem && diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx index 67ac7134083..98823764cbc 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx @@ -607,3 +607,38 @@ export const VeryLargeOptionsNumber = () => { ); }; + +const BookEditWithEmptyText = () => { + const choices = [ + { id: 1, name: 'Leo Tolstoy' }, + { id: 2, name: 'Victor Hugo' }, + { id: 3, name: 'William Shakespeare' }, + { id: 4, name: 'Charles Baudelaire' }, + { id: 5, name: 'Marcel Proust' }, + ]; + return ( + { + console.log(data); + }, + }} + > + + + + + ); +}; + +export const EmptyText = () => ( + + + +); diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index d5173340201..381b53e57c5 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -136,6 +136,8 @@ export const AutocompleteInput = < createValue, debounce: debounceDelay = 250, defaultValue = '', + emptyText, + emptyValue = '', field: fieldOverride, format, helperText, @@ -190,6 +192,11 @@ export const AutocompleteInput = < const translate = useTranslate(); + const finalChoices = + isRequiredOverride || emptyText == null + ? allChoices + : [{ id: emptyValue, name: emptyText }].concat(allChoices); + const { id, field, @@ -218,7 +225,7 @@ export const AutocompleteInput = < DisableClearable, SupportCreate >(field.value, { - choices: allChoices, + choices: finalChoices, // @ts-ignore multiple, optionValue, @@ -249,7 +256,9 @@ If you provided a React element for the optionText prop, you must also provide t const getRecordRepresentation = useGetRecordRepresentation(resource); const { getChoiceText, getChoiceValue, getSuggestions } = useSuggestions({ - choices: allChoices, + choices: finalChoices, + emptyText, + emptyValue, limitChoicesToValue, matchSuggestion, optionText: @@ -399,13 +408,13 @@ If you provided a React element for the optionText prop, you must also provide t ); const doesQueryMatchSuggestion = useCallback( filter => { - const hasOption = !!allChoices - ? allChoices.some(choice => getOptionLabel(choice) === filter) + const hasOption = !!finalChoices + ? finalChoices.some(choice => getOptionLabel(choice) === filter) : false; return doesQueryMatchSelection(filter) || hasOption; }, - [allChoices, getOptionLabel, doesQueryMatchSelection] + [finalChoices, getOptionLabel, doesQueryMatchSelection] ); const filterOptions = (options, params) => { @@ -443,9 +452,9 @@ If you provided a React element for the optionText prop, you must also provide t if (matchSuggestion || limitChoicesToValue) { return getSuggestions(filterValue); } - return allChoices?.slice(0, suggestionLimit) || []; + return finalChoices?.slice(0, suggestionLimit) || []; }, [ - allChoices, + finalChoices, filterValue, getSuggestions, limitChoicesToValue, @@ -543,7 +552,7 @@ If you provided a React element for the optionText prop, you must also provide t inputValue={filterValue} loading={ isLoading && - (!allChoices || allChoices.length === 0) && + (!finalChoices || finalChoices.length === 0) && oneSecondHasPassed } value={selectedChoice} From fb26ac40c345cb729f2966d541e7124b18c85aa4 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 19 Sep 2022 14:49:46 -0300 Subject: [PATCH 06/37] Revert changes --- docs/AutocompleteInput.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index ecee4dc9924..40e945a0493 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -41,6 +41,8 @@ import { AutocompleteInput, ReferenceInput } from 'react-admin'; | `choices` | Optional | `Object[]` | `-` | List of items to autosuggest. Required if not inside a ReferenceInput. | | `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | | `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | +| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element | +| `emptyText` | Optional | `string` | `''` | The text to use for the empty element | | `matchSuggestion` | Optional | `Function` | `-` | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | | `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | | `optionText` | Optional | `string` | `Function` | `Component` | `name` | Field name of record to display in the suggestion item or function which accepts the correct record as argument (`(record)=> {string}`) | From fb800dc97ae08a51a8df5a25c7480f29ef0240e9 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 19 Sep 2022 14:55:28 -0300 Subject: [PATCH 07/37] Remove unnecessary change --- packages/ra-core/src/form/useSuggestions.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/ra-core/src/form/useSuggestions.ts b/packages/ra-core/src/form/useSuggestions.ts index 253ed0e9429..ed108aeb1d9 100644 --- a/packages/ra-core/src/form/useSuggestions.ts +++ b/packages/ra-core/src/form/useSuggestions.ts @@ -174,14 +174,7 @@ export const getSuggestionsFactory = ({ getChoiceText: (choice: any) => string | ReactElement; getChoiceValue: (choice: any) => string; }) => filter => { - let suggestions: any[] = [ - getSuggestion({ - optionText, - optionValue, - text: emptyText, - value: emptyValue, - }), - ]; + let suggestions: any[] = []; // if an item is selected and matches the filter if ( selectedItem && From 0d81e78141dcff10426ecc254c27c108d1ff9afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Tue, 20 Sep 2022 08:02:46 -0300 Subject: [PATCH 08/37] Fx empty string and i18n for emptyText --- .../ra-ui-materialui/src/input/AutocompleteInput.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 381b53e57c5..b8554e1f2c0 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -195,7 +195,15 @@ export const AutocompleteInput = < const finalChoices = isRequiredOverride || emptyText == null ? allChoices - : [{ id: emptyValue, name: emptyText }].concat(allChoices); + : [ + { + id: emptyValue, + name: + emptyText === '' + ? ' ' // em space, forces the display of an empty line of normal height + : translate(emptyText, { _: emptyText }), + }, + ].concat(allChoices); const { id, From 91302a2752c1e2e3dda4f3ef1787d804bc0d3f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Tue, 20 Sep 2022 10:02:26 -0300 Subject: [PATCH 09/37] Remove emptyValue / emptyText from useSuggestions --- packages/ra-core/src/form/useSuggestions.ts | 12 ------------ .../ra-ui-materialui/src/input/AutocompleteInput.tsx | 2 -- 2 files changed, 14 deletions(-) diff --git a/packages/ra-core/src/form/useSuggestions.ts b/packages/ra-core/src/form/useSuggestions.ts index ed108aeb1d9..75d9eb758da 100644 --- a/packages/ra-core/src/form/useSuggestions.ts +++ b/packages/ra-core/src/form/useSuggestions.ts @@ -8,8 +8,6 @@ import { useTranslate } from '../i18n'; * * @param allowDuplicates A boolean indicating whether a suggestion can be added several times * @param choices An array of available choices - * @param emptyText The text to use for the empty suggestion. Defaults to an empty string - * @param emptyValue The value to use for the empty suggestion. Defaults to `null` * @param limitChoicesToValue A boolean indicating whether the initial suggestions should be limited to the currently selected one(s) * @param matchSuggestion Optional unless `optionText` is a React element. Function which check whether a choice matches a filter. Must return a boolean. * @param optionText Either a string defining the property to use to get the choice text, a function or a React element @@ -28,8 +26,6 @@ export const useSuggestions = ({ choices, createText = 'ra.action.create', createValue = '@@create', - emptyText = '', - emptyValue = null, limitChoicesToValue, matchSuggestion, optionText, @@ -52,8 +48,6 @@ export const useSuggestions = ({ choices, createText, createValue, - emptyText: translate(emptyText, { _: emptyText }), - emptyValue, getChoiceText, getChoiceValue, limitChoicesToValue, @@ -68,8 +62,6 @@ export const useSuggestions = ({ choices, createText, createValue, - emptyText, - emptyValue, getChoiceText, getChoiceValue, limitChoicesToValue, @@ -98,8 +90,6 @@ export interface UseSuggestionsOptions extends UseChoicesOptions { choices: any[]; createText?: string; createValue?: any; - emptyText?: string; - emptyValue?: any; limitChoicesToValue?: boolean; matchSuggestion?: ( filter: string, @@ -160,8 +150,6 @@ export const getSuggestionsFactory = ({ choices = [], createText = 'ra.action.create', createValue = '@@create', - emptyText = '', - emptyValue = '', optionText = 'name', optionValue = 'id', getChoiceText, diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index b8554e1f2c0..7f78890b8b0 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -265,8 +265,6 @@ If you provided a React element for the optionText prop, you must also provide t const { getChoiceText, getChoiceValue, getSuggestions } = useSuggestions({ choices: finalChoices, - emptyText, - emptyValue, limitChoicesToValue, matchSuggestion, optionText: From ee5c76f4bd2240780f7d42dc78683c5c9334bf16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Tue, 20 Sep 2022 10:38:08 -0300 Subject: [PATCH 10/37] remove emptyValue --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 7f78890b8b0..2a8f8e3c339 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -137,7 +137,6 @@ export const AutocompleteInput = < debounce: debounceDelay = 250, defaultValue = '', emptyText, - emptyValue = '', field: fieldOverride, format, helperText, @@ -197,7 +196,7 @@ export const AutocompleteInput = < ? allChoices : [ { - id: emptyValue, + id: '', name: emptyText === '' ? ' ' // em space, forces the display of an empty line of normal height @@ -429,7 +428,7 @@ If you provided a React element for the optionText prop, you must also provide t matchSuggestion || // When using element as optionText (and matchSuggestion), options are filtered by getSuggestions, so they shouldn't be filtered here limitChoicesToValue // When limiting choices to values (why? it's legacy!), options are also filtered by getSuggestions, so they shouldn't be filtered here ? options - : defaultFilterOptions(options, params); // Otherwise we let MUI's Autocomplete do the filtering + : defaultFilterOptions(options, params); // Otherwise, we let MUI's Autocomplete do the filtering // add create option if necessary const { inputValue } = params; @@ -613,6 +612,7 @@ export interface AutocompleteInputProps< > { children?: ReactNode; debounce?: number; + emptyText?: string; filterToQuery?: (searchText: string) => any; inputText?: (option: any) => string; setFilter?: (value: string) => void; From 6ec40ac9a42636f74ab67b3c50b5ea8565f24b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Tue, 20 Sep 2022 10:51:55 -0300 Subject: [PATCH 11/37] Remove emptyValue from the docs --- docs/AutocompleteInput.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 40e945a0493..667d1fed35a 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -41,7 +41,6 @@ import { AutocompleteInput, ReferenceInput } from 'react-admin'; | `choices` | Optional | `Object[]` | `-` | List of items to autosuggest. Required if not inside a ReferenceInput. | | `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | | `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | -| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element | | `emptyText` | Optional | `string` | `''` | The text to use for the empty element | | `matchSuggestion` | Optional | `Function` | `-` | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | | `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | From a5ca08a43110c526da98769cbb9e4fa757a1d847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Tue, 20 Sep 2022 11:05:06 -0300 Subject: [PATCH 12/37] Add test --- .../src/input/AutocompleteInput.spec.tsx | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index ef01ff4dad4..5843e96f36b 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -57,6 +57,33 @@ describe('', () => { expect(screen.queryByDisplayValue('foo')).not.toBeNull(); }); + describe('emptyText', () => { + it('should allow to override the empty menu option text by passing a string', () => { + const emptyText = 'Default'; + + render( + + + + + + ); + fireEvent.mouseDown( + screen.getByLabelText('resources.users.fields.role') + ); + + expect(screen.queryAllByRole('option').length).toEqual(1); + + const input = screen.getByRole('textbox') as HTMLInputElement; + + expect(input.value).toEqual('Default'); + }); + }); + describe('optionValue', () => { it('should use optionValue as value identifier', async () => { render( From 7c5b1f510d2e4156d1c49be716c13e0cb116002a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Tue, 20 Sep 2022 11:20:59 -0300 Subject: [PATCH 13/37] Improve test description --- packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index 5843e96f36b..ae73774c6fd 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -58,7 +58,7 @@ describe('', () => { }); describe('emptyText', () => { - it('should allow to override the empty menu option text by passing a string', () => { + it('should allow to have an empty menu option text by passing a string', () => { const emptyText = 'Default'; render( From b81c9d533732a15b6d5ede7dd5f183e6f1bb7e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Thu, 22 Sep 2022 08:38:22 -0300 Subject: [PATCH 14/37] Use optionValue and optionText for empty choice --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 2a8f8e3c339..56b004f0bb1 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -192,12 +192,12 @@ export const AutocompleteInput = < const translate = useTranslate(); const finalChoices = - isRequiredOverride || emptyText == null + emptyText == null || isRequiredOverride ? allChoices : [ { - id: '', - name: + [optionValue || 'id']: '', + [typeof optionText === 'string' ? optionText : 'name']: emptyText === '' ? ' ' // em space, forces the display of an empty line of normal height : translate(emptyText, { _: emptyText }), From 8f56320c0b19b4dc4cea6d6fb4f2ff99bf8218de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Thu, 22 Sep 2022 10:23:33 -0300 Subject: [PATCH 15/37] Throw error on optionText of type React element when emptyText is passed --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 56b004f0bb1..dc83dbbb334 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -239,6 +239,12 @@ export const AutocompleteInput = < }); useEffect(() => { + // eslint-disable-next-line eqeqeq + if (isValidElement(optionText) && emptyText != undefined) { + throw new Error( + `optionText of type React element is not supported when setting emptyText` + ); + } // eslint-disable-next-line eqeqeq if (isValidElement(optionText) && inputText == undefined) { throw new Error(` @@ -249,7 +255,7 @@ If you provided a React element for the optionText prop, you must also provide t throw new Error(` If you provided a React element for the optionText prop, you must also provide the matchSuggestion prop (used to match the user input with a choice)`); } - }, [optionText, inputText, matchSuggestion]); + }, [optionText, inputText, matchSuggestion, emptyText]); useEffect(() => { warning( From 837f9318dba8d007e947bdbc60ab48ff47c7edd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 10:54:09 -0300 Subject: [PATCH 16/37] Added back emptyValue --- docs/AutocompleteInput.md | 19 ++++++++++- .../src/input/AutocompleteInput.tsx | 33 +++++++++++-------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 667d1fed35a..168ee2cd906 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -42,13 +42,14 @@ import { AutocompleteInput, ReferenceInput } from 'react-admin'; | `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | | `createItemLabel` | Optional | `string` | `ra.action.create_item` | The label for the menu item allowing users to create a new choice. Used when the filter is not empty | | `emptyText` | Optional | `string` | `''` | The text to use for the empty element | +| `emptyValue` | Optional | `any` | `''` | The value to use for the empty element | | `matchSuggestion` | Optional | `Function` | `-` | Required if `optionText` is a React element. Function returning a boolean indicating whether a choice matches the filter. `(filter, choice) => boolean` | | `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | | `optionText` | Optional | `string` | `Function` | `Component` | `name` | Field name of record to display in the suggestion item or function which accepts the correct record as argument (`(record)=> {string}`) | | `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | | `inputText` | Optional | `Function` | `-` | Required if `optionText` is a custom Component, this function must return the text displayed for the current selection. | | `filterToQuery` | Optional | `string` => `Object` | `searchText => ({ q: [searchText] })` | How to transform the searchText into a parameter for the data provider | -| `setFilter` | Optional | `Function` | `null` | A callback to inform the `searchText` has changed and new `choices` can be retrieved based on this `searchText`. Signature `searchText => void`. This function is automatically set up when using `ReferenceInput`. | +| `setFilter` | Optional | `Function` | `null` | A callback to inform the `searchText` has changed and new `choices` can be retrieved based on this `searchText`. Signature `searchText => void`. This function is automatically set up when using `ReferenceInput`. | | `shouldRenderSuggestions` | Optional | `Function` | `() => true` | A function that returns a `boolean` to determine whether or not suggestions are rendered. Use this when working with large collections of data to improve performance and user experience. This function is passed into the underlying react-autosuggest component. Ex.`(value) => value.trim().length > 2` | | `suggestionLimit` | Optional | `number` | `null` | Limits the numbers of suggestions that are shown in the dropdown list | @@ -96,6 +97,22 @@ When used inside a ``, `` doesn't need a `cho See [Using in a `ReferenceInput>`](#using-in-a-referenceinput) below for more information. +## `emptyValue` + +An empty choice is always added (with a default `''` value, which you can overwrite with the `emptyValue` prop) on top of the options. You can furthermore customize the empty choice by using the `emptyText` prop, which can receive string or a React Element, which doesn't receive any props. + +```jsx + +``` + ## `optionText` You can customize the choice field to use for the option name, thanks to the `optionText` attribute: diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index dc83dbbb334..cad09b55259 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -136,7 +136,8 @@ export const AutocompleteInput = < createValue, debounce: debounceDelay = 250, defaultValue = '', - emptyText, + emptyText = '', + emptyValue = '', field: fieldOverride, format, helperText, @@ -191,18 +192,17 @@ export const AutocompleteInput = < const translate = useTranslate(); - const finalChoices = - emptyText == null || isRequiredOverride - ? allChoices - : [ - { - [optionValue || 'id']: '', - [typeof optionText === 'string' ? optionText : 'name']: - emptyText === '' - ? ' ' // em space, forces the display of an empty line of normal height - : translate(emptyText, { _: emptyText }), - }, - ].concat(allChoices); + const finalChoices = isRequiredOverride + ? allChoices + : [ + { + [optionValue || 'id']: emptyValue, + [typeof optionText === 'string' ? optionText : 'name']: + emptyText === '' + ? ' ' // em space, forces the display of an empty line of normal height + : translate(emptyText, { _: emptyText }), + }, + ].concat(allChoices); const { id, @@ -239,6 +239,12 @@ export const AutocompleteInput = < }); useEffect(() => { + // eslint-disable-next-line eqeqeq + if (emptyValue == null) { + throw new Error( + `emptyValue being set to null or undefined is not supported` + ); + } // eslint-disable-next-line eqeqeq if (isValidElement(optionText) && emptyText != undefined) { throw new Error( @@ -619,6 +625,7 @@ export interface AutocompleteInputProps< children?: ReactNode; debounce?: number; emptyText?: string; + emptyValue?: any; filterToQuery?: (searchText: string) => any; inputText?: (option: any) => string; setFilter?: (value: string) => void; From 0c80558b545bc3f7b4ef3242878246366225c31c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 10:54:42 -0300 Subject: [PATCH 17/37] Improve stories --- .../src/input/AutocompleteInput.stories.tsx | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx index 98823764cbc..56c94d67120 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx @@ -35,6 +35,23 @@ const dataProvider = { update: (resource, params) => Promise.resolve(params), } as any; +const dataProviderEmpty = { + getOne: (resource, params) => + Promise.resolve({ + data: { + id: 1, + title: 'War and Peace', + author: 1, + authorEmpty: 1, + authorZero: 1, + summary: + "War and Peace broadly focuses on Napoleon's invasion of Russia, and the impact it had on Tsarist society. The book explores themes such as revolution, revolution and empire, the growth and decline of various states and the impact it had on their economies, culture, and society.", + year: 1869, + }, + }), + update: (resource, params) => Promise.resolve(params), +} as any; + const history = createMemoryHistory({ initialEntries: ['/books/1'] }); const BookEdit = () => { @@ -627,9 +644,26 @@ const BookEditWithEmptyText = () => { > + + @@ -638,7 +672,7 @@ const BookEditWithEmptyText = () => { }; export const EmptyText = () => ( - + ); From b9208609876f16b9043d8494eda4486ab73da8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 11:00:46 -0300 Subject: [PATCH 18/37] Improve docs --- docs/AutocompleteInput.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 168ee2cd906..6e187443a18 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -113,6 +113,9 @@ An empty choice is always added (with a default `''` value, which you can overwr /> ``` +**Note**: `emptyValue` can not be set to `undefined` or `null` since the `dataProvider` method will recibe an empty string every du to the nature of HTML inputs. + + ## `optionText` You can customize the choice field to use for the option name, thanks to the `optionText` attribute: From 202d30cbf4cc1c395c9172d0614f575c96090f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 11:02:42 -0300 Subject: [PATCH 19/37] Fix docs of SelectInput --- docs/AutocompleteInput.md | 2 +- docs/SelectInput.md | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 6e187443a18..09e05d047e8 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -113,7 +113,7 @@ An empty choice is always added (with a default `''` value, which you can overwr /> ``` -**Note**: `emptyValue` can not be set to `undefined` or `null` since the `dataProvider` method will recibe an empty string every du to the nature of HTML inputs. +**Note**: `emptyValue` can not be set to `undefined` or `null` since the `dataProvider` method will recibe an empty string on submit due to the nature of HTML inputs. ## `optionText` diff --git a/docs/SelectInput.md b/docs/SelectInput.md index 4559e3da760..c066c563779 100644 --- a/docs/SelectInput.md +++ b/docs/SelectInput.md @@ -111,13 +111,15 @@ const choices = [ An empty choice is always added (with a default `''` value, which you can overwrite with the `emptyValue` prop) on top of the options. You can furthermore customize the `MenuItem` for the empty choice by using the `emptyText` prop, which can receive either a string or a React Element, which doesn't receive any props. ```jsx - ``` +**Note**: `emptyValue` can not be set to `undefined` or `null` since the `dataProvider` method will recibe an empty string on submit due to the nature of HTML inputs. + ## `options` Use the `options` attribute if you want to override any of MUI's `` attributes: @@ -364,4 +366,4 @@ const CreateCategory = () => { ); }; ``` -{% endraw %} \ No newline at end of file +{% endraw %} From 8663d24d3056a450a6b093a601efb719fab0fa8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 11:12:11 -0300 Subject: [PATCH 20/37] Revert emptyText evaluation --- .../src/input/AutocompleteInput.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index cad09b55259..8b64d9c4241 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -136,7 +136,7 @@ export const AutocompleteInput = < createValue, debounce: debounceDelay = 250, defaultValue = '', - emptyText = '', + emptyText, emptyValue = '', field: fieldOverride, format, @@ -192,17 +192,18 @@ export const AutocompleteInput = < const translate = useTranslate(); - const finalChoices = isRequiredOverride - ? allChoices - : [ - { - [optionValue || 'id']: emptyValue, - [typeof optionText === 'string' ? optionText : 'name']: - emptyText === '' - ? ' ' // em space, forces the display of an empty line of normal height - : translate(emptyText, { _: emptyText }), - }, - ].concat(allChoices); + const finalChoices = + emptyText == null || isRequiredOverride + ? allChoices + : [ + { + [optionValue || 'id']: emptyValue, + [typeof optionText === 'string' ? optionText : 'name']: + emptyText === '' + ? ' ' // em space, forces the display of an empty line of normal height + : translate(emptyText, { _: emptyText }), + }, + ].concat(allChoices); const { id, From fb886c046ef9acdb02adeeb510d0afcffccca519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 11:53:05 -0300 Subject: [PATCH 21/37] Update docs/AutocompleteInput.md Co-authored-by: Jean-Baptiste Kaiser --- docs/AutocompleteInput.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 09e05d047e8..a2b7882a516 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -99,7 +99,7 @@ See [Using in a `ReferenceInput>`](#using-in-a-referenceinput) below for more in ## `emptyValue` -An empty choice is always added (with a default `''` value, which you can overwrite with the `emptyValue` prop) on top of the options. You can furthermore customize the empty choice by using the `emptyText` prop, which can receive string or a React Element, which doesn't receive any props. +An empty choice is always added (with a default `''` value, which you can override with the `emptyValue` prop) on top of the options. You can furthermore customize the empty choice by using the `emptyText` prop, which can receive a string or a React Element. ```jsx Date: Fri, 23 Sep 2022 11:53:28 -0300 Subject: [PATCH 22/37] Update docs/AutocompleteInput.md Co-authored-by: Jean-Baptiste Kaiser --- docs/AutocompleteInput.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index a2b7882a516..0f9ab985791 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -113,7 +113,7 @@ An empty choice is always added (with a default `''` value, which you can overri /> ``` -**Note**: `emptyValue` can not be set to `undefined` or `null` since the `dataProvider` method will recibe an empty string on submit due to the nature of HTML inputs. +**Note**: `emptyValue` cannot be set to `undefined` or `null` since the `dataProvider` method will receive an empty string on submit due to the nature of HTML inputs. ## `optionText` From 8b3d5b9d6efa0f627fd2bbf69faa05f676eb03bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 11:53:37 -0300 Subject: [PATCH 23/37] Update docs/SelectInput.md Co-authored-by: Jean-Baptiste Kaiser --- docs/SelectInput.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SelectInput.md b/docs/SelectInput.md index c066c563779..82489aa5869 100644 --- a/docs/SelectInput.md +++ b/docs/SelectInput.md @@ -118,7 +118,7 @@ An empty choice is always added (with a default `''` value, which you can overwr ]} /> ``` -**Note**: `emptyValue` can not be set to `undefined` or `null` since the `dataProvider` method will recibe an empty string on submit due to the nature of HTML inputs. +**Note**: `emptyValue` cannot be set to `undefined` or `null` since the `dataProvider` method will receive an empty string on submit due to the nature of HTML inputs. ## `options` From 6e83ea5fc50ae2ee3ee684734e445a651fae6634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Fri, 23 Sep 2022 11:54:14 -0300 Subject: [PATCH 24/37] Update docs/AutocompleteInput.md Co-authored-by: Jean-Baptiste Kaiser --- docs/AutocompleteInput.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AutocompleteInput.md b/docs/AutocompleteInput.md index 0f9ab985791..4e001cead23 100644 --- a/docs/AutocompleteInput.md +++ b/docs/AutocompleteInput.md @@ -104,7 +104,7 @@ An empty choice is always added (with a default `''` value, which you can overri ```jsx Date: Fri, 23 Sep 2022 11:55:18 -0300 Subject: [PATCH 25/37] Applied review --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 8b64d9c4241..e6ce7b508aa 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -136,7 +136,7 @@ export const AutocompleteInput = < createValue, debounce: debounceDelay = 250, defaultValue = '', - emptyText, + emptyText = '', emptyValue = '', field: fieldOverride, format, @@ -262,7 +262,7 @@ If you provided a React element for the optionText prop, you must also provide t throw new Error(` If you provided a React element for the optionText prop, you must also provide the matchSuggestion prop (used to match the user input with a choice)`); } - }, [optionText, inputText, matchSuggestion, emptyText]); + }, [optionText, inputText, matchSuggestion, emptyText, emptyValue]); useEffect(() => { warning( From 983231d495f8c212e80aad957b00ba382fa175b1 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Fri, 23 Sep 2022 13:14:40 -0300 Subject: [PATCH 26/37] Fix last commit --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index e6ce7b508aa..6972441d7cd 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -136,7 +136,7 @@ export const AutocompleteInput = < createValue, debounce: debounceDelay = 250, defaultValue = '', - emptyText = '', + emptyText, emptyValue = '', field: fieldOverride, format, From 971e59ed403a3cdf011fdbad0a63ad027d6c5c55 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Fri, 23 Sep 2022 13:35:02 -0300 Subject: [PATCH 27/37] Fix typo --- docs/SelectInput.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SelectInput.md b/docs/SelectInput.md index 82489aa5869..fa033801906 100644 --- a/docs/SelectInput.md +++ b/docs/SelectInput.md @@ -108,7 +108,7 @@ const choices = [ ## `emptyValue` -An empty choice is always added (with a default `''` value, which you can overwrite with the `emptyValue` prop) on top of the options. You can furthermore customize the `MenuItem` for the empty choice by using the `emptyText` prop, which can receive either a string or a React Element, which doesn't receive any props. +An empty choice is always added (with a default `''` value, which you can override with the `emptyValue` prop) on top of the options. You can furthermore customize the `MenuItem` for the empty choice by using the `emptyText` prop, which can receive either a string or a React Element, which doesn't receive any props. ```jsx Date: Mon, 26 Sep 2022 15:59:37 -0300 Subject: [PATCH 28/37] Improve stories --- .../src/input/AutocompleteInput.stories.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx index 56c94d67120..98e49a469cd 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx @@ -42,6 +42,7 @@ const dataProviderEmpty = { id: 1, title: 'War and Peace', author: 1, + authorNone: 1, authorEmpty: 1, authorZero: 1, summary: @@ -644,9 +645,16 @@ const BookEditWithEmptyText = () => { > + Date: Mon, 26 Sep 2022 16:03:53 -0300 Subject: [PATCH 29/37] Prevent emptyText/emptyValue usage for AutocompleteArrayInput --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 6972441d7cd..ce19c16d5cd 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -193,7 +193,7 @@ export const AutocompleteInput = < const translate = useTranslate(); const finalChoices = - emptyText == null || isRequiredOverride + emptyText == null || isRequiredOverride || multiple ? allChoices : [ { From d29138097705890195e41c9389bc4cf60d48ca6f Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 16:07:55 -0300 Subject: [PATCH 30/37] set emptyText as an empty string by default --- .../ra-ui-materialui/src/input/AutocompleteInput.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index ce19c16d5cd..c3ef55b40e8 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -192,16 +192,20 @@ export const AutocompleteInput = < const translate = useTranslate(); + const finalEmptyText = emptyText ?? ''; + const finalChoices = - emptyText == null || isRequiredOverride || multiple + isRequiredOverride || multiple ? allChoices : [ { [optionValue || 'id']: emptyValue, [typeof optionText === 'string' ? optionText : 'name']: - emptyText === '' + finalEmptyText === '' ? ' ' // em space, forces the display of an empty line of normal height - : translate(emptyText, { _: emptyText }), + : translate(finalEmptyText, { + _: finalEmptyText, + }), }, ].concat(allChoices); From 02ebeccf9478493cbcd6822561f81893093d8221 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 16:10:21 -0300 Subject: [PATCH 31/37] Use emptyValue as default value for the input --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index c3ef55b40e8..5c9648307c9 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -305,7 +305,7 @@ If you provided a React element for the optionText prop, you must also provide t ]); } } else { - field.onChange(getChoiceValue(newValue) ?? ''); + field.onChange(getChoiceValue(newValue) ?? emptyValue); } }; @@ -465,7 +465,7 @@ If you provided a React element for the optionText prop, you must also provide t newValue: any, reason: string ) => { - handleChangeWithCreateSupport(newValue != null ? newValue : ''); + handleChangeWithCreateSupport(newValue != null ? newValue : emptyValue); }; const oneSecondHasPassed = useTimeout(1000, filterValue); From 4621addb6daf35ba3f1a9bfcaaf115833c72a616 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 16:11:07 -0300 Subject: [PATCH 32/37] Fix test --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 5c9648307c9..72d4b6a6992 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -485,8 +485,7 @@ If you provided a React element for the optionText prop, you must also provide t ]); const isOptionEqualToValue = (option, value) => { - // eslint-disable-next-line eqeqeq - return getChoiceValue(option) == getChoiceValue(value); + return getChoiceValue(option) === getChoiceValue(value); }; return ( From a127352f14f615c2b87adffdbc876e7e44759699 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 16:14:07 -0300 Subject: [PATCH 33/37] Fix tests --- .../src/input/AutocompleteInput.spec.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index ae73774c6fd..9f20ec4ef86 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -344,7 +344,7 @@ describe('', () => { resource="posts" choices={choices} matchSuggestion={(filter, choice) => { - if (!filter) return true; + if (!filter || filter === ' ') return true; if ( filter === 'gugu' && choice.name === 'Angular' @@ -403,7 +403,7 @@ describe('', () => { resource="posts" choices={choices} matchSuggestion={(filter, choice) => { - if (!filter) return true; + if (!filter || filter === ' ') return true; if ( filter === 'gugu' && choice.name === 'Angular' @@ -476,7 +476,7 @@ describe('', () => { userEvent.type(input, '{backspace}'); await waitFor(() => { - expect(input.value).toEqual(''); + expect(input.value).toEqual(' '); }); }); @@ -598,7 +598,7 @@ describe('', () => { const input = screen.getByLabelText( 'resources.users.fields.role' ) as HTMLInputElement; - expect(input.value).toEqual(''); + expect(input.value).toEqual(' '); // Temporary workaround until we can upgrade testing-library in v4 input.focus(); @@ -613,7 +613,7 @@ describe('', () => { ); - expect(input.value).toEqual(''); + expect(input.value).toEqual(' '); }); it('should repopulate the suggestions after the suggestions are dismissed', async () => { @@ -946,7 +946,7 @@ describe('', () => { fireEvent.change(input, { target: { value: 'foo' } }); await waitFor( () => { - expect(screen.getByRole('listbox').children).toHaveLength(1); + expect(screen.queryAllByRole('option')).toHaveLength(1); }, { timeout: 2000 } ); @@ -1189,7 +1189,7 @@ describe('', () => { await waitFor(() => { expect( (screen.getByRole('textbox') as HTMLInputElement).value - ).toEqual(''); + ).toEqual(' '); }); expect(screen.queryByText('Leo Tolstoy')).toBeNull(); }); @@ -1298,7 +1298,7 @@ describe('', () => { fireEvent.change(input, { target: { value: 'no match' } }); fireEvent.blur(input); await waitFor(() => { - expect(input.value).toEqual(''); + expect(input.value).toEqual(' '); }); }); }); From 23717f7ffd36985709ed0f30828a6c42ed1e2695 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 16:44:58 -0300 Subject: [PATCH 34/37] Fix tests --- .../src/input/AutocompleteInput.spec.tsx | 14 ++++++------- .../src/input/AutocompleteInput.tsx | 20 ++++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index 9f20ec4ef86..224f2658d01 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -344,7 +344,7 @@ describe('', () => { resource="posts" choices={choices} matchSuggestion={(filter, choice) => { - if (!filter || filter === ' ') return true; + if (!filter) return true; if ( filter === 'gugu' && choice.name === 'Angular' @@ -403,7 +403,7 @@ describe('', () => { resource="posts" choices={choices} matchSuggestion={(filter, choice) => { - if (!filter || filter === ' ') return true; + if (!filter) return true; if ( filter === 'gugu' && choice.name === 'Angular' @@ -476,7 +476,7 @@ describe('', () => { userEvent.type(input, '{backspace}'); await waitFor(() => { - expect(input.value).toEqual(' '); + expect(input.value).toEqual(''); }); }); @@ -598,7 +598,7 @@ describe('', () => { const input = screen.getByLabelText( 'resources.users.fields.role' ) as HTMLInputElement; - expect(input.value).toEqual(' '); + expect(input.value).toEqual(''); // Temporary workaround until we can upgrade testing-library in v4 input.focus(); @@ -613,7 +613,7 @@ describe('', () => { ); - expect(input.value).toEqual(' '); + expect(input.value).toEqual(''); }); it('should repopulate the suggestions after the suggestions are dismissed', async () => { @@ -1189,7 +1189,7 @@ describe('', () => { await waitFor(() => { expect( (screen.getByRole('textbox') as HTMLInputElement).value - ).toEqual(' '); + ).toEqual(''); }); expect(screen.queryByText('Leo Tolstoy')).toBeNull(); }); @@ -1298,7 +1298,7 @@ describe('', () => { fireEvent.change(input, { target: { value: 'no match' } }); fireEvent.blur(input); await waitFor(() => { - expect(input.value).toEqual(' '); + expect(input.value).toEqual(''); }); }); }); diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 72d4b6a6992..8f8de9bb92c 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -200,12 +200,11 @@ export const AutocompleteInput = < : [ { [optionValue || 'id']: emptyValue, - [typeof optionText === 'string' ? optionText : 'name']: - finalEmptyText === '' - ? ' ' // em space, forces the display of an empty line of normal height - : translate(finalEmptyText, { - _: finalEmptyText, - }), + [typeof optionText === 'string' + ? optionText + : 'name']: translate(finalEmptyText, { + _: finalEmptyText, + }), }, ].concat(allChoices); @@ -584,7 +583,14 @@ If you provided a React element for the optionText prop, you must also provide t (props as { key: string; }).key = getChoiceValue(record); - return
  • {getOptionLabel(record, true)}
  • ; + + const optionLabel = getOptionLabel(record, true); + + return ( +
  • + {optionLabel === '' ? ' ' : optionLabel} +
  • + ); }} /> {createElement} From 04da982bf97a046a616b52e861a9f3ff13f4d748 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 18:38:39 -0300 Subject: [PATCH 35/37] Fix e2e test --- packages/ra-ui-materialui/src/input/AutocompleteInput.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 8f8de9bb92c..c07f7c2b7e1 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -368,6 +368,12 @@ If you provided a React element for the optionText prop, you must also provide t return option?.name; } + if (!isListItem && option[optionValue || 'id'] === emptyValue) { + return option[ + typeof optionText === 'string' ? optionText : 'name' + ]; + } + if (!isListItem && inputText !== undefined) { return inputText(option); } From 5f009c6f55524729726210188c980ed733d597d6 Mon Sep 17 00:00:00 2001 From: asvarcas Date: Mon, 26 Sep 2022 18:56:05 -0300 Subject: [PATCH 36/37] Fix missing dependencies in useCallback --- .../ra-ui-materialui/src/input/AutocompleteInput.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index c07f7c2b7e1..966ab403473 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -380,7 +380,14 @@ If you provided a React element for the optionText prop, you must also provide t return getChoiceText(option); }, - [getChoiceText, inputText, createId] + [ + getChoiceText, + inputText, + createId, + optionText, + optionValue, + emptyValue, + ] ); const finalOnBlur = useCallback((): void => { From abf75e129e9494725400a25528293fc3aeed6960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?An=C3=ADbal=20Svarcas?= Date: Wed, 28 Sep 2022 10:22:30 -0300 Subject: [PATCH 37/37] Update docs/SelectInput.md Co-authored-by: Jean-Baptiste Kaiser --- docs/SelectInput.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/SelectInput.md b/docs/SelectInput.md index fa033801906..bef8a55ac9a 100644 --- a/docs/SelectInput.md +++ b/docs/SelectInput.md @@ -111,7 +111,7 @@ const choices = [ An empty choice is always added (with a default `''` value, which you can override with the `emptyValue` prop) on top of the options. You can furthermore customize the `MenuItem` for the empty choice by using the `emptyText` prop, which can receive either a string or a React Element, which doesn't receive any props. ```jsx -