fix: list widget validation when removing invalid item

This commit is contained in:
Daniel Lautzenheiser 2023-08-31 11:29:29 -04:00
parent fb72cec318
commit 3d02a6b180
6 changed files with 54 additions and 2 deletions

View File

@ -4,8 +4,8 @@ import { currentBackend } from '../backend';
import { import {
ADD_DRAFT_ENTRY_MEDIA_FILE, ADD_DRAFT_ENTRY_MEDIA_FILE,
CHANGE_VIEW_STYLE, CHANGE_VIEW_STYLE,
DRAFT_UPDATE,
DRAFT_CHANGE_FIELD, DRAFT_CHANGE_FIELD,
DRAFT_CLEAR_CHILD_VALIDATION,
DRAFT_CREATE_DUPLICATE_FROM_ENTRY, DRAFT_CREATE_DUPLICATE_FROM_ENTRY,
DRAFT_CREATE_EMPTY, DRAFT_CREATE_EMPTY,
DRAFT_CREATE_FROM_ENTRY, DRAFT_CREATE_FROM_ENTRY,
@ -13,6 +13,7 @@ import {
DRAFT_DISCARD, DRAFT_DISCARD,
DRAFT_LOCAL_BACKUP_DELETE, DRAFT_LOCAL_BACKUP_DELETE,
DRAFT_LOCAL_BACKUP_RETRIEVED, DRAFT_LOCAL_BACKUP_RETRIEVED,
DRAFT_UPDATE,
DRAFT_VALIDATION_ERRORS, DRAFT_VALIDATION_ERRORS,
ENTRIES_FAILURE, ENTRIES_FAILURE,
ENTRIES_REQUEST, ENTRIES_REQUEST,
@ -485,6 +486,17 @@ export function changeDraftField({
} as const; } 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( export function changeDraftFieldValidation(
path: string, path: string,
errors: FieldError[], errors: FieldError[],
@ -1135,6 +1147,7 @@ export type EntriesAction = ReturnType<
| typeof discardDraft | typeof discardDraft
| typeof updateDraft | typeof updateDraft
| typeof changeDraftField | typeof changeDraftField
| typeof clearDraftFieldChildValidation
| typeof changeDraftFieldValidation | typeof changeDraftFieldValidation
| typeof localBackupRetrieved | typeof localBackupRetrieved
| typeof loadLocalBackup | typeof loadLocalBackup

View File

@ -7,6 +7,7 @@ import { connect } from 'react-redux';
import { import {
changeDraftField as changeDraftFieldAction, changeDraftField as changeDraftFieldAction,
changeDraftFieldValidation, changeDraftFieldValidation,
clearDraftFieldChildValidation,
} from '@staticcms/core/actions/entries'; } from '@staticcms/core/actions/entries';
import { query as queryAction } from '@staticcms/core/actions/search'; import { query as queryAction } from '@staticcms/core/actions/search';
import useDebouncedCallback from '@staticcms/core/lib/hooks/useDebouncedCallback'; import useDebouncedCallback from '@staticcms/core/lib/hooks/useDebouncedCallback';
@ -127,6 +128,10 @@ const EditorControl = ({
i18nDisabled, i18nDisabled,
]); ]);
const clearChildValidation = useCallback(() => {
dispatch(clearDraftFieldChildValidation(path, i18n, isMeta));
}, [dispatch, i18n, isMeta, path]);
const handleChangeDraftField = useCallback( const handleChangeDraftField = useCallback(
async (value: ValueOrNestedValue) => { async (value: ValueOrNestedValue) => {
setDirty( setDirty(
@ -191,6 +196,7 @@ const EditorControl = ({
label: getFieldLabel(field, t), label: getFieldLabel(field, t),
locale, locale,
onChange: handleDebouncedChangeDraftField, onChange: handleDebouncedChangeDraftField,
clearChildValidation,
path, path,
query, query,
t, t,

View File

@ -49,6 +49,7 @@ export const DRAFT_CREATE_EMPTY = 'DRAFT_CREATE_EMPTY';
export const DRAFT_DISCARD = 'DRAFT_DISCARD'; export const DRAFT_DISCARD = 'DRAFT_DISCARD';
export const DRAFT_UPDATE = 'DRAFT_UPDATE'; export const DRAFT_UPDATE = 'DRAFT_UPDATE';
export const DRAFT_CHANGE_FIELD = 'DRAFT_CHANGE_FIELD'; 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_VALIDATION_ERRORS = 'DRAFT_VALIDATION_ERRORS';
export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED'; export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED';
export const DRAFT_LOCAL_BACKUP_DELETE = 'DRAFT_LOCAL_BACKUP_DELETE'; export const DRAFT_LOCAL_BACKUP_DELETE = 'DRAFT_LOCAL_BACKUP_DELETE';

View File

@ -317,6 +317,7 @@ export interface WidgetControlProps<T, F extends BaseField = UnknownField, EV =
label: string; label: string;
locale: string | undefined; locale: string | undefined;
onChange: (value: T | null | undefined) => void; onChange: (value: T | null | undefined) => void;
clearChildValidation: () => void;
i18n: I18nSettings | undefined; i18n: I18nSettings | undefined;
hasErrors: boolean; hasErrors: boolean;
errors: FieldError[]; errors: FieldError[];

View File

@ -4,6 +4,7 @@ import { v4 as uuid } from 'uuid';
import { import {
ADD_DRAFT_ENTRY_MEDIA_FILE, ADD_DRAFT_ENTRY_MEDIA_FILE,
DRAFT_CHANGE_FIELD, DRAFT_CHANGE_FIELD,
DRAFT_CLEAR_CHILD_VALIDATION,
DRAFT_CREATE_DUPLICATE_FROM_ENTRY, DRAFT_CREATE_DUPLICATE_FROM_ENTRY,
DRAFT_CREATE_EMPTY, DRAFT_CREATE_EMPTY,
DRAFT_CREATE_FROM_ENTRY, 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: { case DRAFT_VALIDATION_ERRORS: {
const { path, errors, i18n, isMeta } = action.payload; const { path, errors, i18n, isMeta } = action.payload;
const fieldsErrors = { ...state.fieldsErrors }; const fieldsErrors = { ...state.fieldsErrors };

View File

@ -189,6 +189,7 @@ const ListControl: FC<WidgetControlProps<ValueOrNestedValue[], ListField>> = pro
errors, errors,
forSingleList, forSingleList,
onChange, onChange,
clearChildValidation,
t, t,
} = props; } = props;
@ -267,10 +268,12 @@ const ListControl: FC<WidgetControlProps<ValueOrNestedValue[], ListField>> = pro
newKeys.splice(index, 1); newKeys.splice(index, 1);
newValue.splice(index, 1); newValue.splice(index, 1);
clearChildValidation();
setKeys(newKeys); setKeys(newKeys);
onChange(newValue as string[] | ObjectValue[]); onChange(newValue as string[] | ObjectValue[]);
}, },
[onChange, internalValue, keys], [keys, internalValue, clearChildValidation, onChange],
); );
const handleDragEnd = useCallback( const handleDragEnd = useCallback(