chore: stubs out data layer and simple frontend example allow explicit null value setting

This commit is contained in:
Jarrod Flesch
2022-12-19 17:02:45 -05:00
parent 0d06b8c178
commit 7b769caf78
3 changed files with 162 additions and 107 deletions

View File

@@ -105,11 +105,16 @@ export const addFieldStatePromise = async ({
await Promise.all(promises);
// Add values to field state
fieldState.value = arrayValue.length;
fieldState.initialValue = arrayValue.length;
if (valueWithDefault === null) {
fieldState.value = null;
fieldState.initialValue = null;
} else {
fieldState.value = arrayValue.length;
fieldState.initialValue = arrayValue.length;
if (arrayValue.length > 0) {
fieldState.disableFormData = true;
if (arrayValue.length > 0) {
fieldState.disableFormData = true;
}
}
// Add field to state
@@ -163,11 +168,16 @@ export const addFieldStatePromise = async ({
await Promise.all(promises);
// Add values to field state
fieldState.value = blocksValue.length;
fieldState.initialValue = blocksValue.length;
if (typeof valueWithDefault === null) {
fieldState.value = null;
fieldState.initialValue = null;
} else {
fieldState.value = blocksValue.length;
fieldState.initialValue = blocksValue.length;
if (blocksValue.length > 0) {
fieldState.disableFormData = true;
if (blocksValue.length > 0) {
fieldState.disableFormData = true;
}
}
// Add field to state

View File

@@ -24,9 +24,38 @@ import { ArrayAction } from '../../../elements/ArrayAction';
import { scrollToID } from '../../../../utilities/scrollToID';
import HiddenInput from '../HiddenInput';
import { RowLabel } from '../../RowLabel';
import { getTranslation } from '../../../../../utilities/getTranslation';
import './index.scss';
import { getTranslation } from '../../../../../utilities/getTranslation';
import Checkbox from '../Checkbox';
type Props2 = {
path: string
fieldValue: unknown
}
const NullifyField:React.FC<Props2> = ({ path, fieldValue }) => {
const { dispatchFields, setModified } = useForm();
// TODO: only allow this for non-default locales
// TODO(pending design): abstract checkbox into input component for reuse
return (
<Checkbox
onChange={(checked) => {
dispatchFields({
type: 'UPDATE',
path,
value: checked ? null : (fieldValue || []),
});
setModified(true);
}}
defaultValue={fieldValue === null}
label="Remove field in translation"
name={`${path}-nullify-field`}
/>
);
};
const baseClass = 'array-field';
@@ -252,109 +281,121 @@ const ArrayFieldType: React.FC<Props> = (props) => {
description={description}
/>
</header>
<Droppable droppableId="array-drop">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
>
{rows.length > 0 && rows.map((row, i) => {
const rowNumber = i + 1;
const fallbackLabel = `${getTranslation(labels.singular, i18n)} ${String(rowNumber).padStart(2, '0')}`;
return (
<Draggable
key={row.id}
draggableId={row.id}
index={i}
isDragDisabled={readOnly}
>
{(providedDrag) => (
<div
id={`${path}-row-${i}`}
ref={providedDrag.innerRef}
{...providedDrag.draggableProps}
<NullifyField
path={path}
fieldValue={value}
/>
{value !== null && (
<React.Fragment>
<Droppable droppableId="array-drop">
{(provided) => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
>
{rows.length > 0 && rows.map((row, i) => {
const rowNumber = i + 1;
const fallbackLabel = `${getTranslation(labels.singular, i18n)} ${String(rowNumber).padStart(2, '0')}`;
return (
<Draggable
key={row.id}
draggableId={row.id}
index={i}
isDragDisabled={readOnly}
>
<Collapsible
collapsed={row.collapsed}
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
className={`${baseClass}__row`}
key={row.id}
dragHandleProps={providedDrag.dragHandleProps}
header={(
<RowLabel
path={`${path}.${i}`}
label={CustomRowLabel || fallbackLabel}
rowNumber={rowNumber}
/>
)}
actions={!readOnly ? (
<ArrayAction
rowCount={rows.length}
duplicateRow={duplicateRow}
addRow={addRow}
moveRow={moveRow}
removeRow={removeRow}
index={i}
/>
) : undefined}
>
<HiddenInput
name={`${path}.${i}.id`}
value={row.id}
/>
<RenderFields
className={`${baseClass}__fields`}
forceRender
readOnly={readOnly}
fieldTypes={fieldTypes}
permissions={permissions?.fields}
indexPath={indexPath}
fieldSchema={fields.map((field) => ({
...field,
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
}))}
/>
{(providedDrag) => (
<div
id={`${path}-row-${i}`}
ref={providedDrag.innerRef}
{...providedDrag.draggableProps}
>
<Collapsible
collapsed={row.collapsed}
onToggle={(collapsed) => setCollapse(row.id, collapsed)}
className={`${baseClass}__row`}
key={row.id}
dragHandleProps={providedDrag.dragHandleProps}
header={(
<RowLabel
path={`${path}.${i}`}
label={CustomRowLabel || fallbackLabel}
rowNumber={rowNumber}
/>
)}
actions={!readOnly ? (
<ArrayAction
rowCount={rows.length}
duplicateRow={duplicateRow}
addRow={addRow}
moveRow={moveRow}
removeRow={removeRow}
index={i}
/>
) : undefined}
>
<HiddenInput
name={`${path}.${i}.id`}
value={row.id}
/>
<RenderFields
className={`${baseClass}__fields`}
forceRender
readOnly={readOnly}
fieldTypes={fieldTypes}
permissions={permissions?.fields}
indexPath={indexPath}
fieldSchema={fields.map((field) => ({
...field,
path: `${path}.${i}${fieldAffectsData(field) ? `.${field.name}` : ''}`,
}))}
/>
</Collapsible>
</div>
)}
</Draggable>
);
})}
{(rows.length < minRows || (required && rows.length === 0)) && (
<Banner type="error">
{t('validation:requiresAtLeast', {
count: minRows,
label: getTranslation(minRows
? labels.plural
: labels.singular,
i18n) || t(minRows > 1 ? 'general:row' : 'general:rows'),
</Collapsible>
</div>
)}
</Draggable>
);
})}
</Banner>
{(rows.length < minRows || (required && rows.length === 0)) && (
<Banner type="error">
{t('validation:requiresAtLeast', {
count: minRows,
label: getTranslation(minRows
? labels.plural
: labels.singular,
i18n) || t(minRows > 1 ? 'general:row' : 'general:rows'),
})}
</Banner>
)}
{(rows.length === 0 && readOnly) && (
<Banner>
{t('validation:fieldHasNo', { label: getTranslation(labels.plural, i18n) })}
</Banner>
)}
{provided.placeholder}
</div>
)}
{(rows.length === 0 && readOnly) && (
<Banner>
{t('validation:fieldHasNo', { label: getTranslation(labels.plural, i18n) })}
</Banner>
)}
{provided.placeholder}
</div>
)}
</Droppable>
{(!readOnly && !hasMaxRows) && (
<div className={`${baseClass}__add-button-wrap`}>
<Button
onClick={() => addRow(value as number)}
buttonStyle="icon-label"
icon="plus"
iconStyle="with-border"
iconPosition="left"
>
{t('addLabel', { label: getTranslation(labels.singular, i18n) })}
</Button>
</div>
</Droppable>
{(!readOnly && !hasMaxRows) && (
<div className={`${baseClass}__add-button-wrap`}>
<Button
onClick={() => addRow(value as number)}
buttonStyle="icon-label"
icon="plus"
iconStyle="with-border"
iconPosition="left"
>
{t('addLabel', { label: getTranslation(labels.singular, i18n) })}
</Button>
</div>
)}
</React.Fragment>
)}
</div>
</DragDropContext>
);

View File

@@ -131,10 +131,14 @@ export const promise = async ({
case 'array':
case 'blocks': {
// Handle cases of arrays being intentionally set to 0
if (siblingData[field.name] === '0' || siblingData[field.name] === 0 || siblingData[field.name] === null) {
if (siblingData[field.name] === '0' || siblingData[field.name] === 0) {
siblingData[field.name] = [];
}
if (siblingData[field.name] === null) {
siblingData[field.name] = null;
}
break;
}