Skip to content

Commit

Permalink
Fields: Refactor fields abstraction to allow nested fields
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed Apr 14, 2018
1 parent 2a836da commit 7959e87
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 417 deletions.
44 changes: 16 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ GCF uses the WordPress data module to store the list of available field types. F
const fieldTypes = wp.data.select("gcf/fields").all();
```

You can use the data module to register new custom field types as well. Defining a field type means defining the `edit` function of the block used to edit this field type for example:
You can use the data module to register new custom field types as well. Defining a field type means defining the `editForm` function of the field. A higher order component used to edit the field's value.

```js
const myCustomFieldType = {
Expand All @@ -33,37 +33,25 @@ const myCustomFieldType = {
// Label of your field type
label: "My Custom Field Type",

// Function returning the block settings based on the field configuration
// The field configuration contains information like the name of the field, the title etc...
getBlockSettings(fieldConfig) {
return {
edit({ attributes, setAttributes }) {
// By default the content of the field is saved in a block attribute name "content"
// This attribute is saved as post meta (using the name of the field)
// The attributes definition can be overridden
const { content } = attributes;

return (
<label>
{fieldConfig.title || fieldConfig.name}
<input
type="text"
value={attributes.content || ""}
onChange={event => {
setAttributes({ content: event.target.value });
}}
placeholder="Write"
/>
</label>
);
}
};
// Function returning a Component used to edit the field value.
editForm: fieldConfig => ({ value, onChange }) => {
return (
<label>
{fieldConfig.title || fieldConfig.name}
<input
type="text"
value={value || ""}
onChange={event => {
onChange(event.target.value);
}}
placeholder="Write"
/>
</label>
);
}
};

wp.data.dispatch("gcf/fields").register(myCustomFieldType);
```

In the example above `getBlockSettings` returns only the mandatory block settings (the `edit` function) and will use the default settings used by GCF. All these default settings can be overridden.

Last thing, make sure your script registering your custom fields is loaded in Gutenberg before the editor initialization and in the GCF Admin page before the `gcf-config-app` script.
160 changes: 16 additions & 144 deletions scripts/config-app/components/template-form/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { connect } from "react-redux";
import { cloneDeep, map, without, head, values, filter } from "lodash";
import uuid from "uuid/v4";
import { cloneDeep, map, head, filter } from "lodash";
import { __, sprintf } from "@wordpress/i18n";
import { withInstanceId, Button, IconButton } from "@wordpress/components";
import { withInstanceId, Button } from "@wordpress/components";
import { Component } from "@wordpress/element";
import { withSelect } from "@wordpress/data";

import { FieldListForm } from "@gcf/fields";

import "./style.scss";
import QueryModelList from "../query/model-list";
Expand Down Expand Up @@ -37,7 +37,7 @@ class TemplateForm extends Component {
this.onChangeTitle = this.onChangeProperty("title");
this.onChangePostType = this.onChangeProperty("post_type");
this.onChangeLock = this.onChangeProperty("lock");
this.onAddField = this.onAddField.bind(this);
this.onChangeFields = this.onChangeFields.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}

Expand All @@ -61,74 +61,15 @@ class TemplateForm extends Component {
};
}

onAddField() {
const newField = {
id: uuid(),
type: "text"
};

this.setState(state => ({
editedTemplate: {
...state.editedTemplate,
fields: (state.editedTemplate.fields || []).concat([newField])
}
}));
}

onBlurFieldName(field) {
return event => {
const value = event.target.value;
this.setState(state => {
const index = state.editedTemplate.fields.indexOf(field);
if (!!field.title) {
return;
}
const newField = {
...field,
title: value
};
const newFields = [...state.editedTemplate.fields];
newFields[index] = newField;
return {
editedTemplate: {
...state.editedTemplate,
fields: newFields
}
};
});
};
}

onChangeField(field, property) {
return event => {
const value = event.target.value;
this.setState(state => {
const index = state.editedTemplate.fields.indexOf(field);
const newField = {
...field,
[property]: value
};
const newFields = [...state.editedTemplate.fields];
newFields[index] = newField;
return {
editedTemplate: {
...state.editedTemplate,
fields: newFields
}
};
});
};
}

onRemoveField(field) {
return () => {
this.setState(state => ({
onChangeFields(fields) {
this.setState(state => {
return {
editedTemplate: {
...state.editedTemplate,
fields: without(state.editedTemplate.fields, field)
fields
}
}));
};
};
});
}

onSubmit(event) {
Expand Down Expand Up @@ -223,75 +164,10 @@ class TemplateForm extends Component {
</div>

<div className="gcf-template-form__group">
<label>{__("Fields", "gutenberg-custom-fields")}</label>

<div className="gcf-template-form__fields">
{map(editedTemplate.fields, field => (
<div key={field.id} className="gcf-template-form__field">
<IconButton
className="gcf-template-form__remove-field"
icon="no-alt"
onClick={this.onRemoveField(field)}
/>

<div>
<label
htmlFor={`template-fields-name-${field.id}-${instanceId}`}
>
{__("Name", "gutenberg-custom-fields")}
</label>
<input
type="text"
id={`template-fields-name-${field.id}-${instanceId}`}
value={field.name || ""}
onChange={this.onChangeField(field, "name")}
onBlur={this.onBlurFieldName(field)}
/>
</div>

<div>
<label
htmlFor={`template-fields-title-${field.id}-${instanceId}`}
>
{__("Title", "gutenberg-custom-fields")}
</label>
<input
type="text"
id={`template-fields-title-${field.id}-${instanceId}`}
value={field.title || ""}
onChange={this.onChangeField(field, "title")}
/>
</div>

<div>
<label
htmlFor={`template-fields-type-${field.id}-${instanceId}`}
>
{__("Type", "gutenberg-custom-fields")}
</label>
<select
id={`template-fields-type-${field.id}-${instanceId}`}
value={field.type}
onChange={this.onChangeField(field, "type")}
>
{map(availableFieldTypes, fieldType => (
<option key={fieldType.name} value={fieldType.name}>
{fieldType.label}
</option>
))}
</select>
</div>
</div>
))}

<IconButton
className="gcf-template-form__add-field"
icon="insert"
onClick={this.onAddField}
>
{__("Add Field", "gutenberg-custom-fields")}
</IconButton>
</div>
<FieldListForm
fields={editedTemplate.fields}
onChange={this.onChangeFields}
/>
</div>

<div className="gcf-template-form__footer">
Expand All @@ -309,8 +185,4 @@ class TemplateForm extends Component {

export default connect(state => ({
postTypes: getRecords(state, "postTypes")
}))(
withSelect(select => ({
availableFieldTypes: select("gcf/fields").all()
}))(TemplateForm)
);
}))(withInstanceId(TemplateForm));
96 changes: 96 additions & 0 deletions scripts/fields/components/config/field-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { map } from "lodash";

import { compose } from "@wordpress/element";
import { withSelect } from "@wordpress/data";
import { withInstanceId } from "@wordpress/components";
import { __ } from "@wordpress/i18n";

function FieldForm({
field,
instanceId,
onChange,
fieldType,
availableFieldTypes
}) {
const id = `gcf-field-${instanceId}`;

const onChangeProperty = property => {
return event => {
const value = event.target.value;
const newField = {
...field,
[property]: value
};
onChange(newField);
};
};

const onBlurFieldName = event => {
const value = event.target.value;
if (!!field.title) {
return;
}
const newField = {
...field,
title: value
};
onChange(newField);
};

return (
<div key={field.id} className="gcf-fields__field-form">
<div>
<label htmlFor={`template-fields-name-${field.id}-${instanceId}`}>
{__("Name", "gutenberg-custom-fields")}
</label>
<input
type="text"
id={`template-fields-name-${field.id}-${instanceId}`}
value={field.name || ""}
onChange={onChangeProperty("name")}
onBlur={onBlurFieldName}
/>
</div>

<div>
<label htmlFor={`template-fields-title-${field.id}-${instanceId}`}>
{__("Title", "gutenberg-custom-fields")}
</label>
<input
type="text"
id={`template-fields-title-${field.id}-${instanceId}`}
value={field.title || ""}
onChange={onChangeProperty("title")}
/>
</div>

<div>
<label htmlFor={`template-fields-type-${field.id}-${instanceId}`}>
{__("Type", "gutenberg-custom-fields")}
</label>
<select
id={`template-fields-type-${field.id}-${instanceId}`}
value={field.type}
onChange={onChangeProperty("type")}
>
{map(availableFieldTypes, fieldType => (
<option key={fieldType.name} value={fieldType.name}>
{fieldType.label}
</option>
))}
</select>
</div>

{fieldType.configForm &&
fieldType.configForm({ field, instanceId, onChange })}
</div>
);
}

export default compose([
withSelect((select, { field }) => ({
availableFieldTypes: select("gcf/fields").all(),
fieldType: select("gcf/fields").get(field.type)
})),
withInstanceId
])(FieldForm);
Loading

0 comments on commit 7959e87

Please sign in to comment.