diff --git a/.changeset/flat-worms-wonder.md b/.changeset/flat-worms-wonder.md new file mode 100644 index 00000000000..9727ed9008c --- /dev/null +++ b/.changeset/flat-worms-wonder.md @@ -0,0 +1,7 @@ +--- +'@keystonejs/app-admin-ui': patch +--- + +While submitting a create list form inside Admin-UI, **null** was `explicit` in the `mutation` request for blank unedited fields. +This was preventing the knex DB-level default to be applied correctly. +But omitting the blank (unchanged) and required fields, we managed to completely exclude it while making graphql mutation request, and thus respecting the knew `defaultTo` option. diff --git a/packages/app-admin-ui/client/components/CreateItemModal.js b/packages/app-admin-ui/client/components/CreateItemModal.js index b07b48b7bef..754b9efdf26 100644 --- a/packages/app-admin-ui/client/components/CreateItemModal.js +++ b/packages/app-admin-ui/client/components/CreateItemModal.js @@ -15,7 +15,13 @@ import { useToasts } from 'react-toast-notifications'; import { Button, LoadingButton } from '@arch-ui/button'; import Drawer from '@arch-ui/drawer'; -import { arrayToObject, captureSuspensePromises, countArrays } from '@keystonejs/utils'; +import { + arrayToObject, + captureSuspensePromises, + countArrays, + mapKeys, + omitBy, +} from '@keystonejs/utils'; import { gridSize } from '@arch-ui/theme'; import { AutocompleteCaptor } from '@arch-ui/input'; @@ -25,6 +31,8 @@ import { validateFields, handleCreateUpdateMutationError } from '../util'; let Render = ({ children }) => children(); +const getValues = (fieldsObject, item) => mapKeys(fieldsObject, field => field.serialize(item)); + function useEventCallback(callback) { let callbackRef = useRef(callback); let cb = useCallback((...args) => { @@ -58,12 +66,33 @@ function CreateItemModal({ prefillData = {}, isLoading, createItem, onClose, onC // it's important to remember that react events // propagate through portals as if they aren't there event.stopPropagation(); - const data = arrayToObject(creatable, 'path', field => field.serialize(item)); + + const fieldsObject = arrayToObject(creatable, 'path'); + + const initialData = list.getInitialItemData({ prefill: prefillData }); + const initialValues = getValues(fieldsObject, initialData); + const currentValues = getValues(fieldsObject, item); + + // 'null' is explicit for the blank fields when making a GraphQL + // request. This prevents the `knex` DB-level default values to be applied + // correctly, But, if we exclude the blank field altogether, default values + // (knex DB-level default) are respected. Additionally, we need to make sure + // that we don't omit the required fields for client-side input validation. + function hasnotChangedAndIsNotRequired(path) { + const hasChanged = fieldsObject[path].hasChanged(initialValues, currentValues); + const isRequired = fieldsObject[path].config.isRequired; + return !hasChanged && !isRequired; + } + + const data = omitBy(currentValues, hasnotChangedAndIsNotRequired); + + const fields = Object.values(omitBy(fieldsObject, path => !data.hasOwnProperty(path))); if (isLoading) return; + if (countArrays(validationErrors)) return; if (!countArrays(validationWarnings)) { - const { errors, warnings } = await validateFields(creatable, item, data); + const { errors, warnings } = await validateFields(fields, item, data); if (countArrays(errors) + countArrays(warnings) > 0) { setValidationErrors(errors); diff --git a/test-projects/basic/cypress/mock/upload.txt b/test-projects/basic/cypress/mock/upload.txt index 5dd1377d1ca..72a1f6b252a 100644 --- a/test-projects/basic/cypress/mock/upload.txt +++ b/test-projects/basic/cypress/mock/upload.txt @@ -1 +1 @@ -Some important content 0.6756829499009345 \ No newline at end of file +Some important content 0.8956414064436986 \ No newline at end of file