From f234f68019f122bd46cb2af2e8f62cf07cd53c1b Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Mon, 29 Nov 2021 16:34:39 -0500 Subject: [PATCH] feat: abstracts upload component --- .../components/fields/Upload/Field/index.tsx | 37 +++- .../forms/field-types/Upload/Input.tsx | 173 ++++++++++++++++++ .../forms/field-types/Upload/index.tsx | 170 ++++------------- src/admin/components/forms/useField/index.tsx | 1 + src/fields/config/types.ts | 2 - 5 files changed, 237 insertions(+), 146 deletions(-) create mode 100644 src/admin/components/forms/field-types/Upload/Input.tsx diff --git a/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx b/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx index dd5b0f4967..f5ee4865b9 100644 --- a/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx +++ b/demo/collections/CustomComponents/components/fields/Upload/Field/index.tsx @@ -1,7 +1,9 @@ -import React, { useCallback, useState } from 'react'; -import Upload from '../../../../../../../src/admin/components/forms/field-types/Upload'; +import { useConfig } from '@payloadcms/config-provider'; +import React, { useCallback } from 'react'; +import UploadInput from '../../../../../../../src/admin/components/forms/field-types/Upload/Input'; import { Props as UploadFieldType } from '../../../../../../../src/admin/components/forms/field-types/Upload/types'; import useField from '../../../../../../../src/admin/components/forms/useField'; +import { SanitizedCollectionConfig } from '../../../../../../../src/collections/config/types'; const Text: React.FC = (props) => { const { @@ -9,30 +11,47 @@ const Text: React.FC = (props) => { name, label, relationTo, - fieldTypes + fieldTypes, } = props; const { value, - setValue + setValue, + showError, } = useField({ - path + path, }); const onChange = useCallback((incomingValue) => { - setValue(incomingValue) - }, []) + const incomingID = incomingValue?.id || incomingValue; + setValue(incomingID); + }, [setValue]); + + const { + collections, + serverURL, + routes: { + api, + }, + } = useConfig(); + + const collection = collections.find((coll) => coll.slug === relationTo) || undefined; return ( - - ) + ); }; export default Text; diff --git a/src/admin/components/forms/field-types/Upload/Input.tsx b/src/admin/components/forms/field-types/Upload/Input.tsx new file mode 100644 index 0000000000..c5636e5665 --- /dev/null +++ b/src/admin/components/forms/field-types/Upload/Input.tsx @@ -0,0 +1,173 @@ +import React, { useEffect, useState } from 'react'; +import { useModal } from '@faceless-ui/modal'; +import Button from '../../../elements/Button'; +import Label from '../../Label'; +import Error from '../../Error'; +import FileDetails from '../../../elements/FileDetails'; +import FieldDescription from '../../FieldDescription'; +import { UploadField } from '../../../../../fields/config/types'; +import { Description } from '../../FieldDescription/types'; +import { FieldTypes } from '..'; +import AddModal from './Add'; +import SelectExistingModal from './SelectExisting'; +import { SanitizedCollectionConfig } from '../../../../../collections/config/types'; + +import './index.scss'; + +const baseClass = 'upload'; + +export type UploadInputProps = Omit & { + showError: boolean + errorMessage?: string + readOnly?: boolean + path?: string + required?: boolean + value?: string + description?: Description + onChange?: (e) => void + placeholder?: string + style?: React.CSSProperties + width?: string + fieldTypes?: FieldTypes + collection?: SanitizedCollectionConfig + serverURL?: string + api?: string +} + +const UploadInput: React.FC = (props) => { + const { + path, + required, + readOnly, + style, + width, + description, + label, + relationTo, + fieldTypes, + value, + onChange, + showError, + serverURL = 'http://localhost:3000', + api = '/api', + collection, + errorMessage, + } = props; + + const { toggle } = useModal(); + + const addModalSlug = `${path}-add`; + const selectExistingModalSlug = `${path}-select-existing`; + + const [file, setFile] = useState(undefined); + const [missingFile, setMissingFile] = useState(false); + + const classes = [ + 'field-type', + baseClass, + showError && 'error', + readOnly && 'read-only', + ].filter(Boolean).join(' '); + + useEffect(() => { + if (typeof value === 'string' && value !== '') { + const fetchFile = async () => { + const response = await fetch(`${serverURL}${api}/${relationTo}/${value}`); + + if (response.ok) { + const json = await response.json(); + setFile(json); + } else { + setMissingFile(true); + setFile(undefined); + } + }; + + fetchFile(); + } else { + setFile(undefined); + } + }, [ + value, + relationTo, + api, + serverURL, + ]); + + return ( +
+ +
+ ); +}; + +export default UploadInput; diff --git a/src/admin/components/forms/field-types/Upload/index.tsx b/src/admin/components/forms/field-types/Upload/index.tsx index bb76e35c13..859bc5abbb 100644 --- a/src/admin/components/forms/field-types/Upload/index.tsx +++ b/src/admin/components/forms/field-types/Upload/index.tsx @@ -1,27 +1,21 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { useModal } from '@faceless-ui/modal'; +import React, { useCallback } from 'react'; import { useConfig } from '@payloadcms/config-provider'; import useField from '../../useField'; import withCondition from '../../withCondition'; -import Button from '../../../elements/Button'; -import Label from '../../Label'; -import Error from '../../Error'; import { upload } from '../../../../../fields/validations'; -import FileDetails from '../../../elements/FileDetails'; -import AddModal from './Add'; -import SelectExistingModal from './SelectExisting'; import { Props } from './types'; +import UploadInput from './Input'; import './index.scss'; -import FieldDescription from '../../FieldDescription'; - -const baseClass = 'upload'; const Upload: React.FC = (props) => { - const { toggle } = useModal(); - const [internalValue, setInternalValue] = useState(undefined); - const [missingFile, setMissingFile] = useState(false); - const { collections, serverURL, routes: { api } } = useConfig(); + const { + collections, + serverURL, + routes: { + api, + }, + } = useConfig(); const { path: pathFromProps, @@ -38,15 +32,11 @@ const Upload: React.FC = (props) => { validate = upload, relationTo, fieldTypes, - value: valueFromProps, - onChange: onChangeFromProps, } = props; const collection = collections.find((coll) => coll.slug === relationTo); const path = pathFromProps || name; - const addModalSlug = `${path}-add`; - const selectExistingModalSlug = `${path}-select-existing`; const memoizedValidate = useCallback((value) => { const validationResult = validate(value, { required }); @@ -60,132 +50,42 @@ const Upload: React.FC = (props) => { }); const { - value: valueFromContext, + value, showError, setValue, errorMessage, } = fieldType; - const value = valueFromProps || valueFromContext || ''; - - const classes = [ - 'field-type', - baseClass, - showError && 'error', - readOnly && 'read-only', - ].filter(Boolean).join(' '); - - useEffect(() => { - if (typeof value === 'string') { - const fetchFile = async () => { - const response = await fetch(`${serverURL}${api}/${relationTo}/${value}`); - - if (response.ok) { - const json = await response.json(); - setInternalValue(json); - } else { - setInternalValue(undefined); - setMissingFile(true); - } - }; - - fetchFile(); - } + const onChange = useCallback((incomingValue) => { + const incomingID = incomingValue?.id || incomingValue; + setValue(incomingID); }, [ - value, - relationTo, - api, - serverURL, - setValue + setValue, ]); - useEffect(() => { - const { id: incomingID } = internalValue || {}; - if (typeof onChangeFromProps === 'function') { - onChangeFromProps(incomingID) - } else { - setValue(incomingID); - } - }, [internalValue]); - - return ( -
- -
- ); + ); + } + + return null; }; export default withCondition(Upload); diff --git a/src/admin/components/forms/useField/index.tsx b/src/admin/components/forms/useField/index.tsx index d87b7c5547..2a6a924604 100644 --- a/src/admin/components/forms/useField/index.tsx +++ b/src/admin/components/forms/useField/index.tsx @@ -113,6 +113,7 @@ const useField = (options: Options): FieldType => { sendField(valueToSend); } }, [ + path, valueToSend, sendField, field, diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts index d7f6e97667..6aeec910eb 100644 --- a/src/fields/config/types.ts +++ b/src/fields/config/types.ts @@ -167,8 +167,6 @@ export type UploadField = FieldBase & { type: 'upload' relationTo: string maxDepth?: number - value?: string - onChange?: (value: string) => void } type CodeAdmin = Admin & {