From 3d02a6b1803c23345538a529168b21cd89391db1 Mon Sep 17 00:00:00 2001 From: Daniel Lautzenheiser Date: Thu, 31 Aug 2023 11:29:29 -0400 Subject: [PATCH] fix: list widget validation when removing invalid item --- packages/core/src/actions/entries.ts | 15 +++++++++- .../editor-control-pane/EditorControl.tsx | 6 ++++ packages/core/src/constants.ts | 1 + packages/core/src/interface.ts | 1 + packages/core/src/reducers/entryDraft.ts | 28 +++++++++++++++++++ .../core/src/widgets/list/ListControl.tsx | 5 +++- 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/packages/core/src/actions/entries.ts b/packages/core/src/actions/entries.ts index 8bf2ce48..8ee8517f 100644 --- a/packages/core/src/actions/entries.ts +++ b/packages/core/src/actions/entries.ts @@ -4,8 +4,8 @@ import { currentBackend } from '../backend'; import { ADD_DRAFT_ENTRY_MEDIA_FILE, CHANGE_VIEW_STYLE, - DRAFT_UPDATE, DRAFT_CHANGE_FIELD, + DRAFT_CLEAR_CHILD_VALIDATION, DRAFT_CREATE_DUPLICATE_FROM_ENTRY, DRAFT_CREATE_EMPTY, DRAFT_CREATE_FROM_ENTRY, @@ -13,6 +13,7 @@ import { DRAFT_DISCARD, DRAFT_LOCAL_BACKUP_DELETE, DRAFT_LOCAL_BACKUP_RETRIEVED, + DRAFT_UPDATE, DRAFT_VALIDATION_ERRORS, ENTRIES_FAILURE, ENTRIES_REQUEST, @@ -485,6 +486,17 @@ export function changeDraftField({ } as const; } +export function clearDraftFieldChildValidation( + path: string, + i18n?: I18nSettings, + isMeta?: boolean, +) { + return { + type: DRAFT_CLEAR_CHILD_VALIDATION, + payload: { path, i18n, isMeta }, + } as const; +} + export function changeDraftFieldValidation( path: string, errors: FieldError[], @@ -1135,6 +1147,7 @@ export type EntriesAction = ReturnType< | typeof discardDraft | typeof updateDraft | typeof changeDraftField + | typeof clearDraftFieldChildValidation | typeof changeDraftFieldValidation | typeof localBackupRetrieved | typeof loadLocalBackup diff --git a/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx b/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx index 3150620e..850c8400 100644 --- a/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx +++ b/packages/core/src/components/entry-editor/editor-control-pane/EditorControl.tsx @@ -7,6 +7,7 @@ import { connect } from 'react-redux'; import { changeDraftField as changeDraftFieldAction, changeDraftFieldValidation, + clearDraftFieldChildValidation, } from '@staticcms/core/actions/entries'; import { query as queryAction } from '@staticcms/core/actions/search'; import useDebouncedCallback from '@staticcms/core/lib/hooks/useDebouncedCallback'; @@ -127,6 +128,10 @@ const EditorControl = ({ i18nDisabled, ]); + const clearChildValidation = useCallback(() => { + dispatch(clearDraftFieldChildValidation(path, i18n, isMeta)); + }, [dispatch, i18n, isMeta, path]); + const handleChangeDraftField = useCallback( async (value: ValueOrNestedValue) => { setDirty( @@ -191,6 +196,7 @@ const EditorControl = ({ label: getFieldLabel(field, t), locale, onChange: handleDebouncedChangeDraftField, + clearChildValidation, path, query, t, diff --git a/packages/core/src/constants.ts b/packages/core/src/constants.ts index 0cc520de..65785bc3 100644 --- a/packages/core/src/constants.ts +++ b/packages/core/src/constants.ts @@ -49,6 +49,7 @@ export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY'; export const DRAFT_DISCARD = 'DRAFT_DISCARD'; export const DRAFT_UPDATE = 'DRAFT_UPDATE'; export const DRAFT_CHANGE_FIELD = 'DRAFT_CHANGE_FIELD'; +export const DRAFT_CLEAR_CHILD_VALIDATION = 'DRAFT_CLEAR_CHILD_VALIDATION'; export const DRAFT_VALIDATION_ERRORS = 'DRAFT_VALIDATION_ERRORS'; export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED'; export const DRAFT_LOCAL_BACKUP_DELETE = 'DRAFT_LOCAL_BACKUP_DELETE'; diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index bbca6359..ec8ff042 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -317,6 +317,7 @@ export interface WidgetControlProps void; + clearChildValidation: () => void; i18n: I18nSettings | undefined; hasErrors: boolean; errors: FieldError[]; diff --git a/packages/core/src/reducers/entryDraft.ts b/packages/core/src/reducers/entryDraft.ts index 8879d500..776c3f19 100644 --- a/packages/core/src/reducers/entryDraft.ts +++ b/packages/core/src/reducers/entryDraft.ts @@ -4,6 +4,7 @@ import { v4 as uuid } from 'uuid'; import { ADD_DRAFT_ENTRY_MEDIA_FILE, DRAFT_CHANGE_FIELD, + DRAFT_CLEAR_CHILD_VALIDATION, DRAFT_CREATE_DUPLICATE_FROM_ENTRY, DRAFT_CREATE_EMPTY, DRAFT_CREATE_FROM_ENTRY, @@ -218,6 +219,33 @@ function entryDraftReducer( }; } + case DRAFT_CLEAR_CHILD_VALIDATION: { + const { path, i18n, isMeta } = action.payload; + const fieldsErrors = { ...state.fieldsErrors }; + + const dataPath = isMeta + ? ['meta'] + : (i18n && getDataPath(i18n.currentLocale, i18n.defaultLocale)) || ['data']; + const fullPath = `${dataPath.join('.')}.${path}`; + + const pathsToDelete: string[] = []; + + Object.keys(fieldsErrors).forEach(p => { + if (p === fullPath || p.startsWith(fullPath)) { + pathsToDelete.push(p); + } + }); + + pathsToDelete.forEach(p => { + delete fieldsErrors[p]; + }); + + return { + ...state, + fieldsErrors, + }; + } + case DRAFT_VALIDATION_ERRORS: { const { path, errors, i18n, isMeta } = action.payload; const fieldsErrors = { ...state.fieldsErrors }; diff --git a/packages/core/src/widgets/list/ListControl.tsx b/packages/core/src/widgets/list/ListControl.tsx index 49204e25..32079540 100644 --- a/packages/core/src/widgets/list/ListControl.tsx +++ b/packages/core/src/widgets/list/ListControl.tsx @@ -189,6 +189,7 @@ const ListControl: FC> = pro errors, forSingleList, onChange, + clearChildValidation, t, } = props; @@ -267,10 +268,12 @@ const ListControl: FC> = pro newKeys.splice(index, 1); newValue.splice(index, 1); + clearChildValidation(); + setKeys(newKeys); onChange(newValue as string[] | ObjectValue[]); }, - [onChange, internalValue, keys], + [keys, internalValue, clearChildValidation, onChange], ); const handleDragEnd = useCallback(