feat: confine conditions inside list to the same local list item (#863)

This commit is contained in:
Daniel Lautzenheiser 2023-09-06 12:05:39 -04:00 committed by GitHub
parent 82a8e11ab3
commit 5602812774
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 319 additions and 60 deletions

View File

@ -569,6 +569,64 @@ collections:
- label: File - label: File
name: file name: file
widget: file widget: file
- name: typed_list_with_condition
label: Typed List With Condition
widget: list
types:
- label: Type 1 Object
name: type_1_object
widget: object
fields:
- name: template
label: template
widget: select
options:
- column
- row
- banner
default: column
- label: String
name: string
widget: string
- label: Boolean
name: boolean
widget: boolean
condition:
field: template
value: banner
- label: Text
name: text
widget: text
- label: Type 2 Object
name: type_2_object
widget: object
fields:
- label: Number
name: number
widget: number
- label: Select
name: select
widget: select
options:
- a
- b
- c
- label: Datetime
name: datetime
widget: datetime
- label: Markdown
name: markdown
widget: markdown
- label: Type 3 Object
name: type_3_object
widget: object
fields:
- label: Image
name: image
widget: image
- label: File
name: file
widget: file
- name: map - name: map
label: Map label: Map
file: _widgets/map.json file: _widgets/map.json

View File

@ -1045,7 +1045,7 @@ export class Backend<EF extends BaseField = UnknownField, BC extends BackendClas
} }
filterEntries(collection: { entries: Entry[] }, filterRule: FilterRule | FilterRule[]) { filterEntries(collection: { entries: Entry[] }, filterRule: FilterRule | FilterRule[]) {
return filterEntries(collection.entries, filterRule); return filterEntries(collection.entries, filterRule, undefined);
} }
} }

View File

@ -54,6 +54,7 @@ const EditorControl = ({
t, t,
value, value,
forList = false, forList = false,
listItemPath,
forSingleList = false, forSingleList = false,
changeDraftField, changeDraftField,
i18n, i18n,
@ -94,7 +95,7 @@ const EditorControl = ({
() => isFieldHidden(field, locale, i18n?.defaultLocale), () => isFieldHidden(field, locale, i18n?.defaultLocale),
[field, i18n?.defaultLocale, locale], [field, i18n?.defaultLocale, locale],
); );
const hidden = useHidden(field, entry); const hidden = useHidden(field, entry, listItemPath);
useEffect(() => { useEffect(() => {
if (hidden) { if (hidden) {
@ -202,6 +203,7 @@ const EditorControl = ({
t, t,
value: finalValue, value: finalValue,
forList, forList,
listItemPath,
forSingleList, forSingleList,
i18n, i18n,
hasErrors, hasErrors,
@ -231,6 +233,7 @@ const EditorControl = ({
query, query,
finalValue, finalValue,
forList, forList,
listItemPath,
forSingleList, forSingleList,
i18n, i18n,
hasErrors, hasErrors,
@ -248,6 +251,7 @@ interface EditorControlOwnProps {
parentPath: string; parentPath: string;
value: ValueOrNestedValue; value: ValueOrNestedValue;
forList?: boolean; forList?: boolean;
listItemPath?: string;
forSingleList?: boolean; forSingleList?: boolean;
i18n: I18nSettings | undefined; i18n: I18nSettings | undefined;
fieldName?: string; fieldName?: string;

View File

@ -32,6 +32,7 @@ export interface EditorControlPaneProps {
onLocaleChange?: (locale: string) => void; onLocaleChange?: (locale: string) => void;
allowDefaultLocale?: boolean; allowDefaultLocale?: boolean;
context?: 'default' | 'i18nSplit'; context?: 'default' | 'i18nSplit';
listItemPath?: string;
} }
const EditorControlPane = ({ const EditorControlPane = ({
@ -47,6 +48,7 @@ const EditorControlPane = ({
onLocaleChange, onLocaleChange,
allowDefaultLocale = false, allowDefaultLocale = false,
context = 'default', context = 'default',
listItemPath,
t, t,
}: TranslatedProps<EditorControlPaneProps>) => { }: TranslatedProps<EditorControlPaneProps>) => {
const pathField = useMemo( const pathField = useMemo(
@ -138,6 +140,7 @@ const EditorControlPane = ({
locale={locale} locale={locale}
parentPath="" parentPath=""
i18n={i18n} i18n={i18n}
listItemPath={listItemPath}
controlled controlled
isMeta isMeta
/> />
@ -156,6 +159,7 @@ const EditorControlPane = ({
locale={locale} locale={locale}
parentPath="" parentPath=""
i18n={i18n} i18n={i18n}
listItemPath={listItemPath}
/> />
); );
})} })}

View File

@ -311,6 +311,7 @@ export interface WidgetControlProps<T, F extends BaseField = UnknownField, EV =
fieldsErrors: FieldsErrors; fieldsErrors: FieldsErrors;
submitted: boolean; submitted: boolean;
forList: boolean; forList: boolean;
listItemPath: string | undefined;
forSingleList: boolean; forSingleList: boolean;
disabled: boolean; disabled: boolean;
duplicate: boolean; duplicate: boolean;

View File

@ -71,30 +71,86 @@ describe('filterEntries', () => {
describe('isHidden', () => { describe('isHidden', () => {
it('should show field by default', () => { it('should show field by default', () => {
expect(isHidden(mockTitleField, mockExternalEntry)).toBeFalsy(); expect(isHidden(mockTitleField, mockExternalEntry, undefined)).toBeFalsy();
}); });
it('should hide field if single condition is not met', () => { it('should hide field if single condition is not met', () => {
expect(isHidden(mockUrlField, mockInternalEntry)).toBeTruthy(); expect(isHidden(mockUrlField, mockInternalEntry, undefined)).toBeTruthy();
}); });
it('should show field if single condition is met', () => { it('should show field if single condition is met', () => {
expect(isHidden(mockUrlField, mockExternalEntry)).toBeFalsy(); expect(isHidden(mockUrlField, mockExternalEntry, undefined)).toBeFalsy();
}); });
it('should hide field if all multiple conditions are not met', () => { it('should hide field if all multiple conditions are not met', () => {
expect(isHidden(mockBodyField, mockExternalEntry)).toBeTruthy(); expect(isHidden(mockBodyField, mockExternalEntry, undefined)).toBeTruthy();
}); });
it('should show field if single condition is met', () => { it('should show field if single condition is met', () => {
expect(isHidden(mockBodyField, mockHasSummaryEntry)).toBeFalsy(); expect(isHidden(mockBodyField, mockHasSummaryEntry, undefined)).toBeFalsy();
expect(isHidden(mockBodyField, mockInternalEntry)).toBeFalsy(); expect(isHidden(mockBodyField, mockInternalEntry, undefined)).toBeFalsy();
}); });
it('should show field if entry is undefined', () => { it('should show field if entry is undefined', () => {
expect(isHidden(mockTitleField, undefined)).toBeFalsy(); expect(isHidden(mockTitleField, undefined, undefined)).toBeFalsy();
expect(isHidden(mockUrlField, undefined)).toBeFalsy(); expect(isHidden(mockUrlField, undefined, undefined)).toBeFalsy();
expect(isHidden(mockBodyField, undefined)).toBeFalsy(); expect(isHidden(mockBodyField, undefined, undefined)).toBeFalsy();
});
describe('inside list', () => {
const mockInsideListEntry = createMockEntry({
path: 'path/to/file-1.md',
data: {
list: [
{
title: 'I am a title',
type: 'external',
url: 'http://example.com',
hasSummary: false,
},
{
title: 'I am a title',
type: 'internal',
body: 'I am the body of your post',
hasSummary: false,
},
{
title: 'I am a title',
type: 'external',
url: 'http://example.com',
body: 'I am the body of your post',
hasSummary: true,
},
],
},
});
it('should show field by default', () => {
expect(isHidden(mockTitleField, mockInsideListEntry, 'list.0')).toBeFalsy();
});
it('should hide field if single condition is not met', () => {
expect(isHidden(mockUrlField, mockInsideListEntry, 'list.1')).toBeTruthy();
});
it('should show field if single condition is met', () => {
expect(isHidden(mockUrlField, mockInsideListEntry, 'list.0')).toBeFalsy();
});
it('should hide field if all multiple conditions are not met', () => {
expect(isHidden(mockBodyField, mockInsideListEntry, 'list.0')).toBeTruthy();
});
it('should show field if single condition is met', () => {
expect(isHidden(mockBodyField, mockInsideListEntry, 'list.2')).toBeFalsy();
expect(isHidden(mockBodyField, mockInsideListEntry, 'list.1')).toBeFalsy();
});
it('should show field if entry is undefined', () => {
expect(isHidden(mockTitleField, undefined, 'list.0')).toBeFalsy();
expect(isHidden(mockUrlField, undefined, 'list.0')).toBeFalsy();
expect(isHidden(mockBodyField, undefined, 'list.0')).toBeFalsy();
});
}); });
}); });
}); });

View File

@ -87,35 +87,36 @@ describe('filterEntries', () => {
describe('field rules', () => { describe('field rules', () => {
it('should filter fields', () => { it('should filter fields', () => {
expect(filterEntries(entries, { field: 'language', value: 'en' })).toEqual([ expect(filterEntries(entries, { field: 'language', value: 'en' }, undefined)).toEqual([
mockEnglishEntry, mockEnglishEntry,
]); ]);
expect(filterEntries(entries, { field: 'language', value: 'fr' })).toEqual([mockFrenchEntry]); expect(filterEntries(entries, { field: 'language', value: 'fr' }, undefined)).toEqual([
mockFrenchEntry,
]);
expect(filterEntries(entries, { field: 'language', value: 'gr' })).toEqual([ expect(filterEntries(entries, { field: 'language', value: 'gr' }, undefined)).toEqual([
mockIndexEntry, mockIndexEntry,
mockUnderscoreIndexEntry, mockUnderscoreIndexEntry,
mockRandomFileNameEntry, mockRandomFileNameEntry,
mockTags1and4Entry, mockTags1and4Entry,
]); ]);
expect(filterEntries(entries, { field: 'draft', value: true })).toEqual([ expect(filterEntries(entries, { field: 'draft', value: true }, undefined)).toEqual([
mockEnglishEntry, mockEnglishEntry,
mockTags1and4Entry, mockTags1and4Entry,
]); ]);
}); });
it('should filter fields if multiple filter values are provided (must match only one)', () => { it('should filter fields if multiple filter values are provided (must match only one)', () => {
expect(filterEntries(entries, { field: 'language', value: ['en', 'fr'] })).toEqual([ expect(filterEntries(entries, { field: 'language', value: ['en', 'fr'] }, undefined)).toEqual(
mockEnglishEntry, [mockEnglishEntry, mockFrenchEntry],
mockFrenchEntry, );
]);
}); });
it('should filter fields based on pattern', () => { it('should filter fields based on pattern', () => {
// Languages with an r in their name // Languages with an r in their name
expect(filterEntries(entries, { field: 'language', pattern: 'r' })).toEqual([ expect(filterEntries(entries, { field: 'language', pattern: 'r' }, undefined)).toEqual([
mockFrenchEntry, mockFrenchEntry,
mockIndexEntry, mockIndexEntry,
mockUnderscoreIndexEntry, mockUnderscoreIndexEntry,
@ -125,20 +126,22 @@ describe('filterEntries', () => {
}); });
it('should filter fields if field value is an array (must include value)', () => { it('should filter fields if field value is an array (must include value)', () => {
expect(filterEntries(entries, { field: 'tags', value: 'tag-4' })).toEqual([ expect(filterEntries(entries, { field: 'tags', value: 'tag-4' }, undefined)).toEqual([
mockFrenchEntry, mockFrenchEntry,
mockIndexEntry, mockIndexEntry,
mockTags1and4Entry, mockTags1and4Entry,
]); ]);
expect(filterEntries(entries, { field: 'numbers', value: 8 })).toEqual([ expect(filterEntries(entries, { field: 'numbers', value: 8 }, undefined)).toEqual([
mockRandomFileNameEntry, mockRandomFileNameEntry,
mockTags1and4Entry, mockTags1and4Entry,
]); ]);
}); });
it('should filter fields if field value is an array and multiple filter values are provided (must include only one)', () => { it('should filter fields if field value is an array and multiple filter values are provided (must include only one)', () => {
expect(filterEntries(entries, { field: 'tags', value: ['tag-3', 'tag-4'] })).toEqual([ expect(
filterEntries(entries, { field: 'tags', value: ['tag-3', 'tag-4'] }, undefined),
).toEqual([
mockFrenchEntry, mockFrenchEntry,
mockIndexEntry, mockIndexEntry,
mockUnderscoreIndexEntry, mockUnderscoreIndexEntry,
@ -146,7 +149,7 @@ describe('filterEntries', () => {
mockTags1and4Entry, mockTags1and4Entry,
]); ]);
expect(filterEntries(entries, { field: 'numbers', value: ['5', '6'] })).toEqual([ expect(filterEntries(entries, { field: 'numbers', value: ['5', '6'] }, undefined)).toEqual([
mockEnglishEntry, mockEnglishEntry,
mockFrenchEntry, mockFrenchEntry,
mockIndexEntry, mockIndexEntry,
@ -155,17 +158,21 @@ describe('filterEntries', () => {
it('should match all values if matchAll is one (value is an array, multiple filter values are provided)', () => { it('should match all values if matchAll is one (value is an array, multiple filter values are provided)', () => {
expect( expect(
filterEntries(entries, { field: 'tags', value: ['tag-1', 'tag-4'], matchAll: true }), filterEntries(
entries,
{ field: 'tags', value: ['tag-1', 'tag-4'], matchAll: true },
undefined,
),
).toEqual([mockFrenchEntry, mockIndexEntry, mockTags1and4Entry]); ).toEqual([mockFrenchEntry, mockIndexEntry, mockTags1and4Entry]);
expect( expect(
filterEntries(entries, { field: 'numbers', value: ['5', '6'], matchAll: true }), filterEntries(entries, { field: 'numbers', value: ['5', '6'], matchAll: true }, undefined),
).toEqual([mockFrenchEntry, mockIndexEntry]); ).toEqual([mockFrenchEntry, mockIndexEntry]);
}); });
it('should filter fields based on pattern when value is an array', () => { it('should filter fields based on pattern when value is an array', () => {
// Tags containing the word "fish" // Tags containing the word "fish"
expect(filterEntries(entries, { field: 'tags', pattern: 'fish' })).toEqual([ expect(filterEntries(entries, { field: 'tags', pattern: 'fish' }, undefined)).toEqual([
mockEnglishEntry, mockEnglishEntry,
mockTags1and4Entry, mockTags1and4Entry,
]); ]);
@ -173,10 +180,14 @@ describe('filterEntries', () => {
it('should filter based on multiple rules (must match all rules)', () => { it('should filter based on multiple rules (must match all rules)', () => {
expect( expect(
filterEntries(entries, [ filterEntries(
{ field: 'tags', value: ['tag-3', 'tag-4'] }, entries,
{ field: 'language', value: 'gr' }, [
]), { field: 'tags', value: ['tag-3', 'tag-4'] },
{ field: 'language', value: 'gr' },
],
undefined,
),
).toEqual([ ).toEqual([
mockIndexEntry, mockIndexEntry,
mockUnderscoreIndexEntry, mockUnderscoreIndexEntry,
@ -187,29 +198,35 @@ describe('filterEntries', () => {
it('should filter based on multiple rules (must match all rules) (matchAll on)', () => { it('should filter based on multiple rules (must match all rules) (matchAll on)', () => {
expect( expect(
filterEntries(entries, [ filterEntries(
{ field: 'tags', value: ['tag-1', 'tag-4'], matchAll: true }, entries,
{ field: 'language', value: 'gr' }, [
]), { field: 'tags', value: ['tag-1', 'tag-4'], matchAll: true },
{ field: 'language', value: 'gr' },
],
undefined,
),
).toEqual([mockIndexEntry, mockTags1and4Entry]); ).toEqual([mockIndexEntry, mockTags1and4Entry]);
}); });
}); });
describe('file rule', () => { describe('file rule', () => {
it('should filter based on file name', () => { it('should filter based on file name', () => {
expect(filterEntries(entries, { pattern: '^index.md$' })).toEqual([mockIndexEntry]); expect(filterEntries(entries, { pattern: '^index.md$' }, undefined)).toEqual([
mockIndexEntry,
]);
expect(filterEntries(entries, { pattern: '^_index.md$' })).toEqual([ expect(filterEntries(entries, { pattern: '^_index.md$' }, undefined)).toEqual([
mockUnderscoreIndexEntry, mockUnderscoreIndexEntry,
]); ]);
expect(filterEntries(entries, { pattern: 'index.md$' })).toEqual([ expect(filterEntries(entries, { pattern: 'index.md$' }, undefined)).toEqual([
mockIndexEntry, mockIndexEntry,
mockUnderscoreIndexEntry, mockUnderscoreIndexEntry,
]); ]);
// File names containing the word file (case insensitive) // File names containing the word file (case insensitive)
expect(filterEntries(entries, { pattern: '[fF][iI][lL][eE]' })).toEqual([ expect(filterEntries(entries, { pattern: '[fF][iI][lL][eE]' }, undefined)).toEqual([
mockEnglishEntry, mockEnglishEntry,
mockFrenchEntry, mockFrenchEntry,
mockRandomFileNameEntry, mockRandomFileNameEntry,
@ -219,7 +236,7 @@ describe('filterEntries', () => {
it('should filter based on multiple rules (must match all rules)', () => { it('should filter based on multiple rules (must match all rules)', () => {
// File names containing the word file (case insensitive) // File names containing the word file (case insensitive)
expect( expect(
filterEntries(entries, [{ pattern: '[fF][iI][lL][eE]' }, { pattern: 'some' }]), filterEntries(entries, [{ pattern: '[fF][iI][lL][eE]' }, { pattern: 'some' }], undefined),
).toEqual([mockRandomFileNameEntry]); ).toEqual([mockRandomFileNameEntry]);
}); });
}); });
@ -227,16 +244,20 @@ describe('filterEntries', () => {
describe('combined field and file rule', () => { describe('combined field and file rule', () => {
it('should filter based on multiple rules (must match all rules)', () => { it('should filter based on multiple rules (must match all rules)', () => {
expect( expect(
filterEntries(entries, [{ pattern: 'index.md$' }, { field: 'tags', value: 'tag-3' }]), filterEntries(
entries,
[{ pattern: 'index.md$' }, { field: 'tags', value: 'tag-3' }],
undefined,
),
).toEqual([mockUnderscoreIndexEntry]); ).toEqual([mockUnderscoreIndexEntry]);
}); });
}); });
describe('nested fields', () => { describe('nested fields', () => {
it('should filter based on multiple rules (must match all rules)', () => { it('should filter based on multiple rules (must match all rules)', () => {
expect(filterEntries(entries, [{ field: 'nested.object.field', value: 'yes' }])).toEqual([ expect(
mockNestedEntry, filterEntries(entries, [{ field: 'nested.object.field', value: 'yes' }], undefined),
]); ).toEqual([mockNestedEntry]);
}); });
}); });
}); });

View File

@ -91,22 +91,30 @@ export function getFieldValue(
return entry.data?.[field.name]; return entry.data?.[field.name];
} }
export function isHidden(field: Field, entry: Entry | undefined): boolean { export function isHidden(
field: Field,
entry: Entry | undefined,
listItemPath: string | undefined,
): boolean {
if (field.condition) { if (field.condition) {
if (!entry) { if (!entry) {
return false; return false;
} }
if (Array.isArray(field.condition)) { if (Array.isArray(field.condition)) {
return !field.condition.find(r => entryMatchesFieldRule(entry, r)); return !field.condition.find(r => entryMatchesFieldRule(entry, r, listItemPath));
} }
return !entryMatchesFieldRule(entry, field.condition); return !entryMatchesFieldRule(entry, field.condition, listItemPath);
} }
return false; return false;
} }
export function useHidden(field: Field, entry: Entry | undefined): boolean { export function useHidden(
return useMemo(() => isHidden(field, entry), [entry, field]); field: Field,
entry: Entry | undefined,
listItemPath: string | undefined,
): boolean {
return useMemo(() => isHidden(field, entry, listItemPath), [entry, field, listItemPath]);
} }

View File

@ -3,8 +3,15 @@ import get from 'lodash/get';
import type { Entry, FieldFilterRule, FilterRule } from '@staticcms/core/interface'; import type { Entry, FieldFilterRule, FilterRule } from '@staticcms/core/interface';
export function entryMatchesFieldRule(entry: Entry, filterRule: FieldFilterRule): boolean { export function entryMatchesFieldRule(
const fieldValue = get(entry.data, filterRule.field); entry: Entry,
filterRule: FieldFilterRule,
listItemPath: string | undefined,
): boolean {
const fieldValue = get(
entry.data,
listItemPath ? `${listItemPath}.${filterRule.field}` : filterRule.field,
);
if ('pattern' in filterRule) { if ('pattern' in filterRule) {
if (Array.isArray(fieldValue)) { if (Array.isArray(fieldValue)) {
return Boolean(fieldValue.find(v => new RegExp(filterRule.pattern).test(String(v)))); return Boolean(fieldValue.find(v => new RegExp(filterRule.pattern).test(String(v))));
@ -48,20 +55,24 @@ export function entryMatchesFieldRule(entry: Entry, filterRule: FieldFilterRule)
return String(fieldValue) === String(filterRule.value); return String(fieldValue) === String(filterRule.value);
} }
function entryMatchesRule(entry: Entry, filterRule: FilterRule) { function entryMatchesRule(entry: Entry, filterRule: FilterRule, listItemPath: string | undefined) {
if ('field' in filterRule) { if ('field' in filterRule) {
return entryMatchesFieldRule(entry, filterRule); return entryMatchesFieldRule(entry, filterRule, listItemPath);
} }
return new RegExp(filterRule.pattern).test(parse(entry.path).base); return new RegExp(filterRule.pattern).test(parse(entry.path).base);
} }
export default function filterEntries(entries: Entry[], filterRule: FilterRule | FilterRule[]) { export default function filterEntries(
entries: Entry[],
filterRule: FilterRule | FilterRule[],
listItemPath: string | undefined,
) {
return entries.filter(entry => { return entries.filter(entry => {
if (Array.isArray(filterRule)) { if (Array.isArray(filterRule)) {
return filterRule.every(r => entryMatchesRule(entry, r)); return filterRule.every(r => entryMatchesRule(entry, r, listItemPath));
} }
return entryMatchesRule(entry, filterRule); return entryMatchesRule(entry, filterRule, listItemPath);
}); });
} }

View File

@ -207,6 +207,7 @@ const ListItem: FC<ListItemProps> = ({
locale={locale} locale={locale}
i18n={i18n} i18n={i18n}
forList={true} forList={true}
listItemPath={`${path}.${objectField.name}`}
forSingleList={isSingleList} forSingleList={isSingleList}
/> />
</ListItemWrapper> </ListItemWrapper>

View File

@ -69,8 +69,6 @@ const InsertLinkToolbarButton: FC<InsertLinkToolbarButtonProps> = ({
return; return;
} }
console.log('editor.selection', editor.selection);
deleteText(editor, { deleteText(editor, {
at: editor.selection as unknown as Location, at: editor.selection as unknown as Location,
}); });

View File

@ -22,6 +22,7 @@ const ObjectControl: FC<WidgetControlProps<ObjectValue, ObjectField>> = ({
errors, errors,
disabled, disabled,
value = {}, value = {},
listItemPath,
}) => { }) => {
const objectLabel = useMemo(() => { const objectLabel = useMemo(() => {
const summary = field.summary; const summary = field.summary;
@ -59,6 +60,7 @@ const ObjectControl: FC<WidgetControlProps<ObjectValue, ObjectField>> = ({
locale={locale} locale={locale}
i18n={i18n} i18n={i18n}
forSingleList={forSingleList} forSingleList={forSingleList}
listItemPath={listItemPath}
/> />
); );
}) ?? null }) ?? null
@ -75,6 +77,7 @@ const ObjectControl: FC<WidgetControlProps<ObjectValue, ObjectField>> = ({
locale, locale,
i18n, i18n,
forSingleList, forSingleList,
listItemPath,
]); ]);
if (fields.length) { if (fields.length) {

View File

@ -65,6 +65,7 @@ export const createMockWidgetControlProps = <
hasErrors, hasErrors,
submitted: false, submitted: false,
forList: false, forList: false,
listItemPath: undefined,
forSingleList: false, forSingleList: false,
disabled: false, disabled: false,
locale: undefined, locale: undefined,

View File

@ -81,7 +81,7 @@ The `condition` option can take a single filter rule or a list of filter rules.
### Example ### Example
The example below creates a collection based on a nested field's value. The example below conditionally shows fields based on the values of other fields.
<CodeTabs> <CodeTabs>
```yaml ```yaml
@ -163,7 +163,7 @@ collections: [
### Nested Field Example ### Nested Field Example
The example below creates a collection based on a nested field's value. The example below conditionally shows fields based on the values of other nested fields.
<CodeTabs> <CodeTabs>
```yaml ```yaml
@ -241,3 +241,96 @@ collections: [
``` ```
</CodeTabs> </CodeTabs>
### List Field Example
The example below conditionally shows fields inside a list based on the values of other fields in the same list item. This works with both `fields` or `types`.
<CodeTabs>
```yaml
collections:
- name: list-field-filtered-collection
label: List Field Filtered Collection
folder: _list_field_condition
create: true
fields:
- name: list
label: List Field
widget: list
fields:
- name: value
label: Value 1
widget: string
condition:
field: nested.object.field
value: yes
- name: nested
label: Nested
widget: object
fields:
- name: object
label: Object
widget: object
fields:
- name: field
label: Field
widget: select
options:
- yes
- no
```
```js
collections: [
{
name: "list-field-filtered-collection",
label: "List Field Filtered Collection",
folder: "_list_field_condition",
create: true,
fields: [
{
name: "list",
label: "List Field",
widget: "list",
fields: [
{
name: "value",
label: "Value 1",
widget: "string",
condition: {
field: "nested.object.field",
value: "yes"
}
},
{
name: "nested",
label: "Nested",
widget: "object",
fields: [
{
name: "object",
label: "Object",
widget: "object",
fields: [
{
name: "field",
label: "Field",
widget: "select",
options: [
"yes",
"no"
]
}
]
}
]
}
]
}
]
}
],
```
</CodeTabs>