fix: allow any default list as default value for list widgets (#5030)
This commit is contained in:
parent
9d049ca604
commit
83c235423e
@ -146,6 +146,28 @@ describe('entries', () => {
|
||||
expect(createEmptyDraftData(fields)).toEqual({ images: fromJS([]) });
|
||||
});
|
||||
|
||||
it('should allow a complex array as list default for a single field list', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
default: [
|
||||
{
|
||||
url: 'https://image.png',
|
||||
},
|
||||
],
|
||||
field: { name: 'url', widget: 'text' },
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({
|
||||
images: fromJS([
|
||||
{
|
||||
url: 'https://image.png',
|
||||
},
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow an empty array as list default for a fields list', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
@ -161,78 +183,49 @@ describe('entries', () => {
|
||||
expect(createEmptyDraftData(fields)).toEqual({ images: fromJS([]) });
|
||||
});
|
||||
|
||||
it('should not allow setting a non empty array as a default value for a single field list', () => {
|
||||
it('should allow a complex array as list default for a fields list', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
default: [{ name: 'url' }, { other: 'field' }],
|
||||
field: { name: 'url', widget: 'text' },
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({});
|
||||
});
|
||||
|
||||
it('should not allow setting a non empty array as a default value for a fields list', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
default: [{ name: 'url' }, { other: 'field' }],
|
||||
default: [
|
||||
{
|
||||
title: 'default image',
|
||||
url: 'https://image.png',
|
||||
},
|
||||
],
|
||||
fields: [
|
||||
{ name: 'title', widget: 'text' },
|
||||
{ name: 'url', widget: 'text' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({});
|
||||
});
|
||||
|
||||
it('should set default value for list field widget', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
field: { name: 'url', widget: 'text', default: 'https://image.png' },
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({ images: ['https://image.png'] });
|
||||
});
|
||||
|
||||
it('should override list default with field default', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
default: [],
|
||||
field: { name: 'url', widget: 'text', default: 'https://image.png' },
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({ images: ['https://image.png'] });
|
||||
});
|
||||
|
||||
it('should set default values for list fields widget', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
fields: [
|
||||
{ name: 'title', widget: 'text', default: 'default image' },
|
||||
{ name: 'url', widget: 'text', default: 'https://image.png' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({
|
||||
images: [{ title: 'default image', url: 'https://image.png' }],
|
||||
images: fromJS([
|
||||
{
|
||||
title: 'default image',
|
||||
url: 'https://image.png',
|
||||
},
|
||||
]),
|
||||
});
|
||||
});
|
||||
|
||||
it('should override list default with fields default', () => {
|
||||
it('should use field default when no list default is provided', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
field: { name: 'url', widget: 'text', default: 'https://image.png' },
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({ images: [{ url: 'https://image.png' }] });
|
||||
});
|
||||
|
||||
it('should use fields default when no list default is provided', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'images',
|
||||
widget: 'list',
|
||||
default: [],
|
||||
fields: [
|
||||
{ name: 'title', widget: 'text', default: 'default image' },
|
||||
{ name: 'url', widget: 'text', default: 'https://image.png' },
|
||||
@ -298,6 +291,26 @@ describe('entries', () => {
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({});
|
||||
});
|
||||
|
||||
it('should populate nested fields', () => {
|
||||
const fields = fromJS([
|
||||
{
|
||||
name: 'names',
|
||||
widget: 'list',
|
||||
field: {
|
||||
name: 'object',
|
||||
widget: 'object',
|
||||
fields: [
|
||||
{ name: 'first', widget: 'string', default: 'first' },
|
||||
{ name: 'second', widget: 'string', default: 'second' },
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(createEmptyDraftData(fields)).toEqual({
|
||||
names: [{ object: { first: 'first', second: 'second' } }],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('persistLocalBackup', () => {
|
||||
|
@ -771,7 +771,6 @@ interface DraftEntryData {
|
||||
|
||||
export function createEmptyDraftData(
|
||||
fields: EntryFields,
|
||||
withNameKey = true,
|
||||
skipField: (field: EntryField) => boolean = () => false,
|
||||
) {
|
||||
return fields.reduce(
|
||||
@ -795,36 +794,27 @@ export function createEmptyDraftData(
|
||||
return [[{}], {}].some(e => isEqual(val, e));
|
||||
}
|
||||
|
||||
if (List.isList(subfields)) {
|
||||
const subDefaultValue = list
|
||||
? [createEmptyDraftData(subfields as EntryFields, withNameKey, skipField)]
|
||||
: createEmptyDraftData(subfields as EntryFields, withNameKey, skipField);
|
||||
if (!isEmptyDefaultValue(subDefaultValue)) {
|
||||
acc[name] = subDefaultValue;
|
||||
} else if (list && List.isList(defaultValue) && (defaultValue as List<unknown>).isEmpty()) {
|
||||
// allow setting an empty list as a default
|
||||
const hasSubfields = List.isList(subfields) || Map.isMap(subfields);
|
||||
if (hasSubfields) {
|
||||
if (list && List.isList(defaultValue)) {
|
||||
acc[name] = defaultValue;
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
} else {
|
||||
const asList = List.isList(subfields)
|
||||
? (subfields as EntryFields)
|
||||
: List([subfields as EntryField]);
|
||||
|
||||
if (Map.isMap(subfields)) {
|
||||
const subDefaultValue = list
|
||||
? [createEmptyDraftData(List([subfields as EntryField]), false, skipField)]
|
||||
: createEmptyDraftData(List([subfields as EntryField]), withNameKey, skipField);
|
||||
if (!isEmptyDefaultValue(subDefaultValue)) {
|
||||
acc[name] = subDefaultValue;
|
||||
} else if (list && List.isList(defaultValue) && (defaultValue as List<unknown>).isEmpty()) {
|
||||
// allow setting an empty list as a default
|
||||
acc[name] = defaultValue;
|
||||
const subDefaultValue = list
|
||||
? [createEmptyDraftData(asList, skipField)]
|
||||
: createEmptyDraftData(asList, skipField);
|
||||
|
||||
if (!isEmptyDefaultValue(subDefaultValue)) {
|
||||
acc[name] = subDefaultValue;
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (defaultValue !== null) {
|
||||
if (!withNameKey) {
|
||||
return defaultValue;
|
||||
}
|
||||
acc[name] = defaultValue;
|
||||
}
|
||||
|
||||
@ -843,7 +833,7 @@ function createEmptyDraftI18nData(collection: Collection, dataFields: EntryField
|
||||
return field.get(I18N) !== I18N_FIELD.DUPLICATE && field.get(I18N) !== I18N_FIELD.TRANSLATE;
|
||||
}
|
||||
|
||||
const i18nData = createEmptyDraftData(dataFields, true, skipField);
|
||||
const i18nData = createEmptyDraftData(dataFields, skipField);
|
||||
return duplicateDefaultI18nFields(collection, i18nData);
|
||||
}
|
||||
|
||||
|
@ -76,7 +76,7 @@ export class PreviewPane extends React.Component {
|
||||
// We retrieve the field by name so that this function can also be used in
|
||||
// custom preview templates, where the field object can't be passed in.
|
||||
let field = fields && fields.find(f => f.get('name') === name);
|
||||
let value = values && values.get(field.get('name'));
|
||||
let value = Map.isMap(values) && values.get(field.get('name'));
|
||||
if (field.get('meta')) {
|
||||
value = this.props.entry.getIn(['meta', field.get('name')]);
|
||||
}
|
||||
|
@ -80,6 +80,17 @@ function handleSummary(summary, entry, label, item) {
|
||||
return stringTemplate.compileStringTemplate(summary, null, '', data);
|
||||
}
|
||||
|
||||
function validateItem(field, item) {
|
||||
if (!Map.isMap(item)) {
|
||||
console.warn(
|
||||
`'${field.get('name')}' field item value value should be a map but is a '${typeof item}'`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export default class ListControl extends React.Component {
|
||||
validations = [];
|
||||
|
||||
@ -386,6 +397,9 @@ export default class ListControl extends React.Component {
|
||||
const valueType = this.getValueType();
|
||||
switch (valueType) {
|
||||
case valueTypes.MIXED: {
|
||||
if (!validateItem(field, item)) {
|
||||
return;
|
||||
}
|
||||
const itemType = getTypedFieldForValue(field, item);
|
||||
const label = itemType.get('label', itemType.get('name'));
|
||||
// each type can have its own summary, but default to the list summary if exists
|
||||
@ -402,6 +416,9 @@ export default class ListControl extends React.Component {
|
||||
return labelReturn;
|
||||
}
|
||||
case valueTypes.MULTIPLE: {
|
||||
if (!validateItem(field, item)) {
|
||||
return;
|
||||
}
|
||||
const multiFields = field.get('fields');
|
||||
const labelField = multiFields && multiFields.first();
|
||||
const value = item.get(labelField.get('name'));
|
||||
|
@ -9,7 +9,10 @@ The list widget allows you to create a repeatable item in the UI which saves as
|
||||
* **Data type:** list of widget values
|
||||
* **Options:**
|
||||
|
||||
* `default`: you may specify a list of strings to populate the basic text field, but declare defaults on the child widgets if you are specifying `fields`;
|
||||
* `default`: you may specify a list of strings to populate the basic text
|
||||
field, or an array of list items for lists using the `fields` option. If no
|
||||
default is declared when using `field` or `fields`, will default to a single
|
||||
list item using the defaults on the child widgets
|
||||
* `allow_add`: `false` hides the button to add additional items
|
||||
* `collapsed`: when `true`, the entries collapse by default
|
||||
* `summary`: specify the label displayed on collapsed entries
|
||||
@ -20,84 +23,107 @@ The list widget allows you to create a repeatable item in the UI which saves as
|
||||
* `max`: maximum number of items in the list
|
||||
* `min`: minimum number of items in the list
|
||||
* `add_to_top`: when `true`, new entries will be added to the top of the list
|
||||
|
||||
* **Example** (`field`/`fields` not specified):
|
||||
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
default: ["news"]
|
||||
```
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
default: ["news"]
|
||||
```
|
||||
|
||||
* **Example** (`allow_add` marked `false`):
|
||||
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
allow_add: false
|
||||
default: ["news"]
|
||||
```
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
allow_add: false
|
||||
default: ["news"]
|
||||
```
|
||||
|
||||
* **Example** (with `field`):
|
||||
|
||||
```yaml
|
||||
- label: "Gallery"
|
||||
name: "galleryImages"
|
||||
widget: "list"
|
||||
summary: '{{fields.image}}'
|
||||
field: {label: Image, name: image, widget: image}
|
||||
```
|
||||
```yaml
|
||||
- label: "Gallery"
|
||||
name: "galleryImages"
|
||||
widget: "list"
|
||||
summary: '{{fields.image}}'
|
||||
field: {label: Image, name: image, widget: image}
|
||||
```
|
||||
|
||||
* **Example** (with `fields`):
|
||||
|
||||
```yaml
|
||||
- label: "Testimonials"
|
||||
name: "testimonials"
|
||||
widget: "list"
|
||||
summary: '{{fields.quote}} - {{fields.author.name}}'
|
||||
fields:
|
||||
- {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
|
||||
- label: Author
|
||||
name: author
|
||||
widget: object
|
||||
fields:
|
||||
- {label: Name, name: name, widget: string, default: "Emmet"}
|
||||
- {label: Avatar, name: avatar, widget: image, default: "/img/emmet.jpg"}
|
||||
```
|
||||
```yaml
|
||||
- label: "Testimonials"
|
||||
name: "testimonials"
|
||||
widget: "list"
|
||||
summary: '{{fields.quote}} - {{fields.author.name}}'
|
||||
fields:
|
||||
- {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
|
||||
- label: Author
|
||||
name: author
|
||||
widget: object
|
||||
fields:
|
||||
- {label: Name, name: name, widget: string, default: "Emmet"}
|
||||
- {label: Avatar, name: avatar, widget: image, default: "/img/emmet.jpg"}
|
||||
```
|
||||
|
||||
* **Example** (with `default`):
|
||||
|
||||
```yaml
|
||||
- label: "Gallery"
|
||||
name: "galleryImages"
|
||||
widget: "list"
|
||||
fields:
|
||||
- { label: "Source", name: "src", widget: "string" }
|
||||
- { label: "Alt Text", name: "alt", widget: "string" }
|
||||
default:
|
||||
- { src: "/img/tenis.jpg", alt: "Tenis" }
|
||||
- { src: "/img/footbar.jpg", alt: "Football" }
|
||||
```
|
||||
|
||||
* **Example** (`collapsed` marked `false`):
|
||||
|
||||
```yaml
|
||||
- label: "Testimonials"
|
||||
name: "testimonials"
|
||||
collapsed: false
|
||||
widget: "list"
|
||||
fields:
|
||||
- {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
|
||||
- {label: Author, name: author, widget: string }
|
||||
```
|
||||
```yaml
|
||||
- label: "Testimonials"
|
||||
name: "testimonials"
|
||||
collapsed: false
|
||||
widget: "list"
|
||||
fields:
|
||||
- {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
|
||||
- {label: Author, name: author, widget: string }
|
||||
```
|
||||
|
||||
* **Example** (`minimize_collapsed` marked `true`):
|
||||
|
||||
```yaml
|
||||
- label: "Testimonials"
|
||||
name: "testimonials"
|
||||
minimize_collapsed: true
|
||||
widget: "list"
|
||||
fields:
|
||||
- {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
|
||||
- {label: Author, name: author, widget: string }
|
||||
```
|
||||
```yaml
|
||||
- label: "Testimonials"
|
||||
name: "testimonials"
|
||||
minimize_collapsed: true
|
||||
widget: "list"
|
||||
fields:
|
||||
- {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
|
||||
- {label: Author, name: author, widget: string }
|
||||
```
|
||||
|
||||
* **Example** (with `max` & `min`):
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
max: 3
|
||||
min: 1
|
||||
default: ["news"]
|
||||
```
|
||||
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
max: 3
|
||||
min: 1
|
||||
default: ["news"]
|
||||
```
|
||||
|
||||
* **Example** (`add_to_top` marked `true`):
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
add_to_top: true
|
||||
```
|
||||
|
||||
```yaml
|
||||
- label: "Tags"
|
||||
name: "tags"
|
||||
widget: "list"
|
||||
add_to_top: true
|
||||
```
|
||||
|
Loading…
x
Reference in New Issue
Block a user