From 3436e6173fb9f17ecf789ba0cae7da865ca4b8b2 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Mon, 22 Nov 2021 08:50:48 -0500 Subject: [PATCH 1/3] wip: custom onchange events for text, select, and upload fields --- .../components/fields/Select/Field/index.tsx | 27 ++++++++++ .../components/fields/Text/Field/index.tsx | 29 ++++++++++ .../components/fields/Upload/Field/index.tsx | 29 ++++++++++ demo/collections/CustomComponents/index.ts | 53 ++++++++++++++++++- .../forms/field-types/Select/index.tsx | 48 ++++++++++++----- .../forms/field-types/Text/index.tsx | 23 +++++++- .../forms/field-types/Upload/index.tsx | 28 ++++++++-- .../components/forms/useFieldType/index.tsx | 1 + src/fields/config/types.ts | 50 +++++++++-------- 9 files changed, 246 insertions(+), 42 deletions(-) create mode 100644 demo/collections/CustomComponents/components/fields/Select/Field/index.tsx create mode 100644 demo/collections/CustomComponents/components/fields/Text/Field/index.tsx create mode 100644 demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx diff --git a/demo/collections/CustomComponents/components/fields/Select/Field/index.tsx b/demo/collections/CustomComponents/components/fields/Select/Field/index.tsx new file mode 100644 index 0000000000..f5ca7cdb96 --- /dev/null +++ b/demo/collections/CustomComponents/components/fields/Select/Field/index.tsx @@ -0,0 +1,27 @@ +import React, { useState } from 'react'; +import SelectInput from '../../../../../../../src/admin/components/forms/field-types/Select'; +import { Props as SelectFieldType } from '../../../../../../../src/admin/components/forms/field-types/Select/types'; + +const Select: React.FC = (props) => { + const { + path, + name, + label, + options + } = props; + + const [internalValue, setInternalValue] = useState(''); + + return ( + + ) +}; + +export default Select; diff --git a/demo/collections/CustomComponents/components/fields/Text/Field/index.tsx b/demo/collections/CustomComponents/components/fields/Text/Field/index.tsx new file mode 100644 index 0000000000..b5af293cdc --- /dev/null +++ b/demo/collections/CustomComponents/components/fields/Text/Field/index.tsx @@ -0,0 +1,29 @@ +import React, { useCallback, useState } from 'react'; +import TextInput from '../../../../../../../src/admin/components/forms/field-types/Text'; +import { Props as TextFieldType } from '../../../../../../../src/admin/components/forms/field-types/Text/types'; + +const Text: React.FC = (props) => { + const { + path, + name, + label + } = props; + + const [internalValue, setInternalValue] = useState(''); + + const middleware = useCallback((incomingValue) => { + setInternalValue(`Hello, world: ${incomingValue}`) + }, []) + + return ( + + ) +}; + +export default Text; diff --git a/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx b/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx new file mode 100644 index 0000000000..6fe6a546bd --- /dev/null +++ b/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import Upload from '../../../../../../../src/admin/components/forms/field-types/Upload'; +import { Props as UploadFieldType } from '../../../../../../../src/admin/components/forms/field-types/Upload/types'; + +const Text: React.FC = (props) => { + const { + path, + name, + label, + relationTo, + fieldTypes + } = props; + + const [internalValue, setInternalValue] = useState(''); + + return ( + + ) +}; + +export default Text; diff --git a/demo/collections/CustomComponents/index.ts b/demo/collections/CustomComponents/index.ts index 2320a8bc1a..495b78a563 100644 --- a/demo/collections/CustomComponents/index.ts +++ b/demo/collections/CustomComponents/index.ts @@ -1,5 +1,8 @@ import { CollectionConfig } from '../../../src/collections/config/types'; import DescriptionField from './components/fields/Description/Field'; +import TextField from './components/fields/Text/Field'; +import SelectField from './components/fields/Select/Field'; +import UploadField from './components/fields/Upload/Field'; import DescriptionCell from './components/fields/Description/Cell'; import DescriptionFilter from './components/fields/Description/Filter'; import NestedArrayField from './components/fields/NestedArrayCustomField/Field'; @@ -25,11 +28,59 @@ const CustomComponents: CollectionConfig = { unique: true, localized: true, }, + { + name: 'text', + label: 'Text', + type: 'text', + required: true, + localized: true, + admin: { + components: { + Field: TextField, + }, + }, + }, + { + name: 'select', + label: 'Select', + type: 'select', + localized: true, + options: [ + { + label: 'Option 1', + value: '1', + }, + { + label: 'Option 2', + value: '2', + }, + { + label: 'Option 3', + value: '3', + }, + ], + admin: { + components: { + Field: SelectField, + }, + }, + }, + { + name: 'upload', + label: 'Upload', + type: 'upload', + relationTo: 'media', + localized: true, + admin: { + components: { + Field: UploadField, + }, + }, + }, { name: 'description', label: 'Description', type: 'textarea', - required: true, localized: true, admin: { components: { diff --git a/src/admin/components/forms/field-types/Select/index.tsx b/src/admin/components/forms/field-types/Select/index.tsx index 097da0f50f..fb946595e6 100644 --- a/src/admin/components/forms/field-types/Select/index.tsx +++ b/src/admin/components/forms/field-types/Select/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useState, useEffect } from 'react'; import withCondition from '../../withCondition'; import ReactSelect from '../../../elements/ReactSelect'; import useFieldType from '../../useFieldType'; @@ -40,6 +40,8 @@ const Select: React.FC = (props) => { description, condition, } = {}, + value: valueFromProps, + onChange: onChangeFromProps } = props; const path = pathFromProps || name; @@ -62,6 +64,38 @@ const Select: React.FC = (props) => { condition, }); + const onChange = useCallback((selectedOption) => { + if (!readOnly) { + let newValue; + if (hasMany) { + if (Array.isArray(selectedOption)) { + newValue = selectedOption.map((option) => option.value); + } else { + newValue = []; + } + } else { + newValue = selectedOption.value; + } + + if (typeof onChangeFromProps === 'function') { + onChangeFromProps(newValue); + } else { + setValue(newValue); + } + } + }, [ + readOnly, + hasMany, + onChangeFromProps, + setValue + ]) + + useEffect(() => { + if (typeof valueFromProps === 'string') { + setValue(valueFromProps); + } + }, [valueFromProps]) + const classes = [ 'field-type', baseClass, @@ -95,17 +129,7 @@ const Select: React.FC = (props) => { required={required} /> { - if (hasMany) { - if (Array.isArray(selectedOption)) { - setValue(selectedOption.map((option) => option.value)); - } else { - setValue([]); - } - } else { - setValue(selectedOption.value); - } - } : undefined} + onChange={onChange} value={valueToRender} showError={showError} isDisabled={readOnly} diff --git a/src/admin/components/forms/field-types/Text/index.tsx b/src/admin/components/forms/field-types/Text/index.tsx index 1854ca7d9b..d41b71f0d2 100644 --- a/src/admin/components/forms/field-types/Text/index.tsx +++ b/src/admin/components/forms/field-types/Text/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useCallback, useEffect } from 'react'; import useFieldType from '../../useFieldType'; import withCondition from '../../withCondition'; import Label from '../../Label'; @@ -24,6 +24,8 @@ const Text: React.FC = (props) => { description, condition, } = {}, + value: valueFromProps, + onChange: onChangeFromProps, } = props; const path = pathFromProps || name; @@ -42,6 +44,23 @@ const Text: React.FC = (props) => { errorMessage, } = fieldType; + const onChange = useCallback((e) => { + const { value: incomingValue } = e.target; + setValue(e); + if (typeof onChangeFromProps === 'function') { + onChangeFromProps(incomingValue); + } + }, [ + onChangeFromProps, + setValue, + ]); + + useEffect(() => { + if (typeof valueFromProps === 'string') { + setValue(valueFromProps); + } + }, [valueFromProps]) + const classes = [ 'field-type', 'text', @@ -68,7 +87,7 @@ const Text: React.FC = (props) => { /> = (props) => { validate = upload, relationTo, fieldTypes, + value: valueFromProps, + onChange: onChangeFromProps, } = props; const collection = collections.find((coll) => coll.slug === relationTo); @@ -88,7 +90,25 @@ const Upload: React.FC = (props) => { fetchFile(); } - }, [value, setInternalValue, relationTo, api, serverURL, setValue]); + }, [ + value, + setInternalValue, + relationTo, + api, + serverURL, + setValue + ]); + + useEffect(() => { + const { id: incomingID } = internalValue || {}; + if (typeof onChangeFromProps === 'function') { + onChangeFromProps(incomingID) + } else { + setValue(incomingID); + } + }, [internalValue]); + + const valueToUse = valueFromProps || value || ''; return (
= (props) => { }} /> )} - {(!value || missingFile) && ( + {(!valueToUse || missingFile) && (