-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
custom_fields: compose fields from given list #229
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,122 @@ | ||||||
import _isEmpty from "lodash/isEmpty"; | ||||||
import React, { Component } from "react"; | ||||||
import PropTypes from "prop-types"; | ||||||
import { Divider } from "semantic-ui-react"; | ||||||
import { AccordionField } from "../../AccordionField"; | ||||||
import { FieldLabel } from "../../FieldLabel"; | ||||||
import { Extensions } from "./Extensions"; | ||||||
|
||||||
export class ComposeFields extends Component { | ||||||
constructor(props) { | ||||||
super(props); | ||||||
const { composeSections, record } = props; | ||||||
const filled = Object.keys(record.custom_fields).map( | ||||||
(key) => `custom_fields.${key}` | ||||||
); | ||||||
this.state = { sections: composeSections, tempFields: [], recordFields: filled }; | ||||||
this.fieldsCfg = this.getFieldsConfig(composeSections); | ||||||
this.sectionsList = composeSections.map((section) => section.section); | ||||||
} | ||||||
|
||||||
getFieldsConfig = (sectionCfg) => { | ||||||
const cfg = {}; | ||||||
for (const section of sectionCfg) { | ||||||
for (const fieldCfg of section.fieldsConfig) { | ||||||
const { field, props, ui_widget, ...otherCfg } = fieldCfg; | ||||||
cfg[field] = { ui_widget: ui_widget, section: section, ...props, ...otherCfg }; | ||||||
} | ||||||
} | ||||||
|
||||||
return cfg; | ||||||
}; | ||||||
|
||||||
getFieldsWithValues = (sectionFields) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe here a better name would be |
||||||
const { record } = this.props; | ||||||
const { tempFields, recordFields } = this.state; | ||||||
const filledFields = []; | ||||||
if (!record.custom_fields) { | ||||||
return []; | ||||||
} | ||||||
for (const field of sectionFields) { | ||||||
if (recordFields.includes(field.key) || tempFields.includes(field)) { | ||||||
filledFields.push(field); | ||||||
} | ||||||
} | ||||||
return filledFields; | ||||||
}; | ||||||
|
||||||
getSectionOfField = (field) => { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
const { sections } = this.state; | ||||||
for (const section of sections) { | ||||||
if (section.fields.map((field) => field.key).includes(field.key)) { | ||||||
return section.section; | ||||||
} | ||||||
} | ||||||
}; | ||||||
|
||||||
addFieldCallback = (fields) => { | ||||||
const { sections: prevSections, tempFields: prevTempFields } = this.state; | ||||||
|
||||||
const sections = [...prevSections]; | ||||||
for (const field of fields) { | ||||||
const sectionToUpdate = this.getSectionOfField(field); | ||||||
for (const section of sections) { | ||||||
if (section.section === sectionToUpdate) { | ||||||
section["fields"] = [...section.fields, field].sort((a, b) => | ||||||
a.key.localeCompare(b.key) | ||||||
); | ||||||
} | ||||||
} | ||||||
} | ||||||
this.setState({ | ||||||
sections: [...sections], | ||||||
tempFields: [...prevTempFields, ...fields], | ||||||
}); | ||||||
}; | ||||||
|
||||||
render() { | ||||||
const { templateLoaders, record } = this.props; | ||||||
const { sections, tempFields, recordFields } = this.state; | ||||||
const existingFields = [ | ||||||
...Object.entries(tempFields).map(([key, value]) => value.key), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||||||
...recordFields, | ||||||
]; | ||||||
|
||||||
return ( | ||||||
<AccordionField key="compose fields" label="Domain specific fields" active> | ||||||
{sections.map(({ fields, paths, ...sectionConfig }) => { | ||||||
const recordCustomFields = this.getFieldsWithValues(fields); | ||||||
if (_isEmpty(recordCustomFields)) { | ||||||
return undefined; | ||||||
} | ||||||
return ( | ||||||
<div key={sectionConfig.section} className="rel-mb-2"> | ||||||
<FieldLabel | ||||||
htmlFor={sectionConfig.section} | ||||||
icon={sectionConfig.icon} | ||||||
label={sectionConfig.section} | ||||||
/> | ||||||
<Divider fitted className="rel-mb-1" /> | ||||||
<div className="rel-ml-1">{recordCustomFields}</div> | ||||||
</div> | ||||||
); | ||||||
})} | ||||||
<Extensions | ||||||
fieldPath="custom_fields" | ||||||
{...this.fieldsCfg} | ||||||
templateLoaders={templateLoaders} | ||||||
addFieldCallback={this.addFieldCallback} | ||||||
sections={this.sectionsList} | ||||||
record={record} | ||||||
existingFields={existingFields} | ||||||
/> | ||||||
</AccordionField> | ||||||
); | ||||||
} | ||||||
} | ||||||
|
||||||
ComposeFields.propTypes = { | ||||||
templateLoaders: PropTypes.array.isRequired, | ||||||
composeSections: PropTypes.array.isRequired, | ||||||
record: PropTypes.object.isRequired, | ||||||
}; |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,54 +7,90 @@ | |||||
|
||||||
import React, { Component } from "react"; | ||||||
import PropTypes from "prop-types"; | ||||||
import { ComposeFields } from "./ComposeFields"; | ||||||
import { AccordionField } from "../../AccordionField"; | ||||||
import { loadWidgetsFromConfig } from "../loader"; | ||||||
|
||||||
export class CustomFields extends Component { | ||||||
state = { sections: [] }; | ||||||
constructor(props) { | ||||||
super(props); | ||||||
this.state = { sections: undefined, composeSections: undefined }; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we need to explain e.g docstring what is the difference between |
||||||
} | ||||||
|
||||||
componentDidMount() { | ||||||
this.populateConfig(); | ||||||
} | ||||||
|
||||||
populateConfig = async () => { | ||||||
const { includesPaths, fieldPathPrefix } = this.props; | ||||||
// use of `Promise.then()` as eslint is giving an error when calling setState() directly | ||||||
// in the componentDidMount() method | ||||||
this.loadCustomFieldsWidgets() | ||||||
.then((sections) => { | ||||||
sections = sections.map((sectionCfg) => { | ||||||
const paths = includesPaths(sectionCfg.fields, fieldPathPrefix); | ||||||
return { ...sectionCfg, paths }; | ||||||
}); | ||||||
this.setState({ sections }); | ||||||
}) | ||||||
.catch((error) => { | ||||||
console.error("Couldn't load custom fields widgets.", error); | ||||||
try { | ||||||
const { sectionsConfig, composeSectionConfig } = | ||||||
await this.loadCustomFieldsWidgets(); | ||||||
const sections = sectionsConfig.map((sectionCfg) => { | ||||||
const paths = includesPaths(sectionCfg.fields, fieldPathPrefix); | ||||||
return { ...sectionCfg, paths }; | ||||||
}); | ||||||
} | ||||||
|
||||||
const composeSections = composeSectionConfig.map((sectionCfg) => { | ||||||
const paths = includesPaths(sectionCfg.fields, fieldPathPrefix); | ||||||
return { ...sectionCfg, paths }; | ||||||
}); | ||||||
|
||||||
this.setState({ sections: sections, composeSections: composeSections }); | ||||||
} catch (error) { | ||||||
console.error("Couldn't load custom fields widgets.", error); | ||||||
} | ||||||
}; | ||||||
|
||||||
async loadCustomFieldsWidgets() { | ||||||
const { config, fieldPathPrefix, templateLoaders } = this.props; | ||||||
const { config, fieldPathPrefix, templateLoaders, record } = this.props; | ||||||
|
||||||
const sections = []; | ||||||
const composeFieldSections = []; | ||||||
for (const sectionCfg of config) { | ||||||
// Path to end user's folder defining custom fields ui widgets | ||||||
const fields = await loadWidgetsFromConfig({ | ||||||
templateLoaders: templateLoaders, | ||||||
fieldPathPrefix: fieldPathPrefix, | ||||||
fields: sectionCfg.fields, | ||||||
record: record, | ||||||
}); | ||||||
sections.push({ ...sectionCfg, fields }); | ||||||
if (sectionCfg.compose_fields) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should align the name with the comment here |
||||||
composeFieldSections.push({ | ||||||
...sectionCfg, | ||||||
fields: fields, | ||||||
fieldsConfig: sectionCfg.fields, | ||||||
}); | ||||||
} else { | ||||||
sections.push({ ...sectionCfg, fields }); | ||||||
} | ||||||
} | ||||||
return sections; | ||||||
return { sectionsConfig: sections, composeSectionConfig: composeFieldSections }; | ||||||
} | ||||||
|
||||||
render() { | ||||||
const { sections } = this.state; | ||||||
const { sections, composeSections } = this.state; | ||||||
const { templateLoaders, record } = this.props; | ||||||
return ( | ||||||
<> | ||||||
{sections.map(({ section, fields, paths }) => ( | ||||||
<AccordionField key={section} includesPaths={paths} label={section} active> | ||||||
{fields} | ||||||
</AccordionField> | ||||||
))} | ||||||
{sections && | ||||||
sections.map(({ fields, paths, ...sectionConfig }) => ( | ||||||
<AccordionField | ||||||
key={sectionConfig.section} | ||||||
includesPaths={paths} | ||||||
label={sectionConfig.section} | ||||||
active | ||||||
> | ||||||
{fields} | ||||||
</AccordionField> | ||||||
))} | ||||||
{composeSections && composeSections && ( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
<ComposeFields | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't it more of |
||||||
templateLoaders={templateLoaders} | ||||||
composeSections={composeSections} | ||||||
record={record} | ||||||
/> | ||||||
)} | ||||||
</> | ||||||
); | ||||||
} | ||||||
|
@@ -76,6 +112,7 @@ CustomFields.propTypes = { | |||||
templateLoaders: PropTypes.array.isRequired, | ||||||
fieldPathPrefix: PropTypes.string.isRequired, | ||||||
includesPaths: PropTypes.func, | ||||||
record: PropTypes.object.isRequired, | ||||||
}; | ||||||
|
||||||
CustomFields.defaultProps = { | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm here are not only the "compose" fields but all record save fields right?