Skip to content

Commit

Permalink
custom_fields: add remove discovered field button
Browse files Browse the repository at this point in the history
* chore: renaming to improve readability
  • Loading branch information
kpsherva authored and slint committed Feb 1, 2024
1 parent 4830790 commit ee0a38a
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 51 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

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
Expand Up @@ -6,14 +6,15 @@
// under the terms of the MIT License; see LICENSE file for more details.

import React, { Component } from "react";
import { RemoveField } from "./RemoveField";
import { ListAndFilterCustomFields } from "./ListAndFilterCustomFields";
import { importWidget } from "../loader";

import { Button, Icon, Modal, Divider } from "semantic-ui-react";

Check warning on line 13 in src/lib/forms/widgets/custom_fields/AddDiscoverableFieldsModal.js

View workflow job for this annotation

GitHub Actions / Tests (18.x)

'Divider' is defined but never used

Check warning on line 13 in src/lib/forms/widgets/custom_fields/AddDiscoverableFieldsModal.js

View workflow job for this annotation

GitHub Actions / Tests (16.x)

'Divider' is defined but never used

import PropTypes from "prop-types";

export class Extensions extends Component {
export class AddDiscoverableFieldsModal extends Component {
constructor(props) {
super(props);
const { existingFields } = props;
Expand Down Expand Up @@ -61,20 +62,26 @@ export class Extensions extends Component {
} = this.state;
const { fieldPath, templateLoaders, addFieldCallback } = this.props;
this.setState({ loading: true });
selectedField["props"]["label"] = (
<RemoveField
fieldPath={`${fieldPath}.${selectedField.field}`}
removeFieldCallback={this.handleRemoveField}
field={{ key: `${fieldPath}.${selectedField.field}` }}
label={selectedField.props.label}
/>
);

const field = await importWidget(templateLoaders, {
...selectedField,
fieldPath: `${fieldPath}.${selectedField.field}`,
});

const performCallback = (selectedFieldTarget) => {
const performCallback = () => {
const { addFields } = this.state;

if (withClose) {
addFieldCallback(addFields);
this.setState({ addFields: [], existingFields: [] });
this.handleModalClosed();
}
addFieldCallback(addFields);
this.setState({ addFields: [] });
this.handleModalClosed();
};

selectedFieldTarget.classList.toggle("selected-background");
this.setState(
{
Expand All @@ -84,7 +91,7 @@ export class Extensions extends Component {
selectedFieldTarget: undefined,
loading: false,
},
() => performCallback(selectedFieldTarget)
() => (withClose ? performCallback() : null)
);
};

Expand All @@ -96,6 +103,13 @@ export class Extensions extends Component {
this.handleModalClosed();
};

handleRemoveField = (field) => {
const { existingFields: prevExisting } = this.state;
const { removeFieldCallback } = this.props;
const updatedFields = prevExisting.filter((n) => field.key !== n);
this.setState({ existingFields: [...updatedFields] });
removeFieldCallback(field);
};
render() {
const {
fieldPath, // injected by the custom field loader via the `field` config property
Expand All @@ -104,14 +118,14 @@ export class Extensions extends Component {
record,
templateLoaders,
addFieldCallback,
removeFieldCallback,
sections,
existingFields: selected,
existingFields: _,
...fieldsList
} = this.props;
const { modalOpen, existingFields, loading } = this.state;
const { modalOpen, existingFields, loading, selectedField } = this.state;
return (
<>
<Divider />
<Button icon labelPosition="left" onClick={this.handleModalOpen}>
<Icon name="plus" />
Add field
Expand All @@ -137,7 +151,7 @@ export class Extensions extends Component {
icon
labelPosition="left"
onClick={() => this.handleAddField(false)}
disabled={loading}
disabled={loading || !selectedField}
loading={loading}
>
<Icon name="plus" />
Expand All @@ -147,7 +161,7 @@ export class Extensions extends Component {
icon
labelPosition="left"
onClick={() => this.handleAddField(true)}
disabled={loading}
disabled={loading || !selectedField}
loading={loading}
>
<Icon name="plus" />
Expand All @@ -160,18 +174,19 @@ export class Extensions extends Component {
}
}

Extensions.propTypes = {
AddDiscoverableFieldsModal.propTypes = {
fieldPath: PropTypes.string.isRequired,
record: PropTypes.object.isRequired,
icon: PropTypes.string,
label: PropTypes.string,
templateLoaders: PropTypes.array.isRequired,
addFieldCallback: PropTypes.func.isRequired,
removeFieldCallback: PropTypes.func.isRequired,
sections: PropTypes.array,
existingFields: PropTypes.array.isRequired,
};

Extensions.defaultProps = {
AddDiscoverableFieldsModal.defaultProps = {
icon: undefined,
label: undefined,
sections: undefined,
Expand Down
29 changes: 16 additions & 13 deletions src/lib/forms/widgets/custom_fields/CustomFields.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

import React, { Component } from "react";
import PropTypes from "prop-types";
import { ComposeFields } from "./ComposeFields";
import { DiscoverFieldsSection } from "./DiscoverFieldsSection";
import { AccordionField } from "../../AccordionField";
import { loadWidgetsFromConfig } from "../loader";

export class CustomFields extends Component {
constructor(props) {
super(props);
this.state = { sections: undefined, composeSections: undefined };
this.state = { sections: undefined, discoverFieldsSections: undefined };
}

componentDidMount() {
Expand All @@ -24,19 +24,22 @@ export class CustomFields extends Component {
populateConfig = async () => {
const { includesPaths, fieldPathPrefix } = this.props;
try {
const { sectionsConfig, composeSectionConfig } =
const { sectionsConfig, discoverFieldsConfig } =
await this.loadCustomFieldsWidgets();
const sections = sectionsConfig.map((sectionCfg) => {
const paths = includesPaths(sectionCfg.fields, fieldPathPrefix);
return { ...sectionCfg, paths };
});

const composeSections = composeSectionConfig.map((sectionCfg) => {
const discoverFieldsSections = discoverFieldsConfig.map((sectionCfg) => {
const paths = includesPaths(sectionCfg.fields, fieldPathPrefix);
return { ...sectionCfg, paths };
});

this.setState({ sections: sections, composeSections: composeSections });
this.setState({
sections: sections,
discoverFieldsSections: discoverFieldsSections,
});
} catch (error) {
console.error("Couldn't load custom fields widgets.", error);
}
Expand All @@ -46,7 +49,7 @@ export class CustomFields extends Component {
const { config, fieldPathPrefix, templateLoaders, record } = this.props;

const sections = [];
const composeFieldSections = [];
const discoverFieldsSections = []; // finds sections with discoverable fields
for (const sectionCfg of config) {
// Path to end user's folder defining custom fields ui widgets
const fields = await loadWidgetsFromConfig({
Expand All @@ -55,8 +58,8 @@ export class CustomFields extends Component {
fields: sectionCfg.fields,
record: record,
});
if (sectionCfg.compose_fields) {
composeFieldSections.push({
if (sectionCfg.discoverable_fields) {
discoverFieldsSections.push({
...sectionCfg,
fields: fields,
fieldsConfig: sectionCfg.fields,
Expand All @@ -65,11 +68,11 @@ export class CustomFields extends Component {
sections.push({ ...sectionCfg, fields });
}
}
return { sectionsConfig: sections, composeSectionConfig: composeFieldSections };
return { sectionsConfig: sections, discoverFieldsConfig: discoverFieldsSections };
}

render() {
const { sections, composeSections } = this.state;
const { sections, discoverFieldsSections } = this.state;
const { templateLoaders, record } = this.props;
return (
<>
Expand All @@ -84,10 +87,10 @@ export class CustomFields extends Component {
{fields}
</AccordionField>
))}
{composeSections && composeSections && (
<ComposeFields
{discoverFieldsSections && (
<DiscoverFieldsSection
templateLoaders={templateLoaders}
composeSections={composeSections}
sections={discoverFieldsSections}
record={record}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import PropTypes from "prop-types";
import { Divider } from "semantic-ui-react";
import { AccordionField } from "../../AccordionField";
import { FieldLabel } from "../../FieldLabel";
import { Extensions } from "./Extensions";
import { AddDiscoverableFieldsModal } from "./AddDiscoverableFieldsModal";
import isEmpty from "lodash/isEmpty";

export class ComposeFields extends Component {
export class DiscoverFieldsSection extends Component {
constructor(props) {
super(props);
const { composeSections, record } = props;
const filled =
record.custom_fields && typeof record.custom_fields === "object"
? 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);
const { sections, record } = props; // sections = fields grouping, usually by domain
console.warn(props);
let filled = [];
if (record && !isEmpty(record.custom_fields)) {
filled = Object.keys(record.custom_fields).map((key) => `custom_fields.${key}`);
}

this.state = { sections: sections, tempFields: [], recordFields: filled };
this.fieldsCfg = this.getFieldsConfig(sections);
this.sectionsList = sections.map((section) => section.section);
}

getFieldsConfig = (sectionCfg) => {
Expand All @@ -32,12 +35,8 @@ export class ComposeFields extends Component {
};

getFieldsWithValues = (sectionFields) => {
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);
Expand Down Expand Up @@ -75,6 +74,23 @@ export class ComposeFields extends Component {
});
};

removeFieldCallback = (field) => {
const { sections: prevSections, tempFields: prevTempFields } = this.state;
const sections = [...prevSections];
let tempFields = [...prevTempFields];
const sectionToUpdate = this.getSectionOfField(field);
for (const section of sections) {
if (section.section === sectionToUpdate) {
section["fields"] = section.fields.filter((n) => field.key !== n.key);
tempFields = tempFields.filter((n) => field.key !== n.key);
}
}
this.setState({
sections: [...sections],
tempFields: [...tempFields],
});
};

render() {
const { templateLoaders, record } = this.props;
const { sections, tempFields, recordFields } = this.state;
Expand All @@ -84,7 +100,7 @@ export class ComposeFields extends Component {
];

return (
<AccordionField key="compose fields" label="Domain specific fields" active>
<AccordionField key="discover-fields" label="Domain specific fields" active>
{sections.map(({ fields, paths, ...sectionConfig }) => {
const recordCustomFields = this.getFieldsWithValues(fields);
if (_isEmpty(recordCustomFields)) {
Expand All @@ -102,11 +118,12 @@ export class ComposeFields extends Component {
</div>
);
})}
<Extensions
<AddDiscoverableFieldsModal
fieldPath="custom_fields"
{...this.fieldsCfg}
templateLoaders={templateLoaders}
addFieldCallback={this.addFieldCallback}
removeFieldCallback={this.removeFieldCallback}
sections={this.sectionsList}
record={record}
existingFields={existingFields}
Expand All @@ -116,8 +133,8 @@ export class ComposeFields extends Component {
}
}

ComposeFields.propTypes = {
DiscoverFieldsSection.propTypes = {
templateLoaders: PropTypes.array.isRequired,
composeSections: PropTypes.array.isRequired,
sections: PropTypes.array.isRequired,
record: PropTypes.object.isRequired,
};
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export class ListAndFilterCustomFields extends Component {
<Item.Group divided relaxed>
{Object.entries(filteredFieldsList).map(([key, value]) => {
const names = key.split(":");

const isDisabled = alreadyAddedFields.includes(`${fieldPath}.${key}`);

return (
Expand Down
30 changes: 30 additions & 0 deletions src/lib/forms/widgets/custom_fields/RemoveField.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Button, Icon, Popup } from "semantic-ui-react";
import { FieldLabel } from "../../FieldLabel";

export class RemoveField extends Component {
render() {
const { removeFieldCallback, fieldPath, field, label } = this.props;
return (
<>
<FieldLabel htmlFor={fieldPath} label={label} className="mr-10" />
<Popup
content="Remove empty fields from the form"
trigger={
<Button icon size="mini" onClick={() => removeFieldCallback(field)}>
<Icon name="trash alternate outline" />
</Button>
}
/>
</>
);
}
}

RemoveField.propTypes = {
field: PropTypes.object.isRequired,
fieldPath: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
removeFieldCallback: PropTypes.func.isRequired,
};
1 change: 0 additions & 1 deletion src/lib/forms/widgets/custom_fields/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { CustomFields } from "./CustomFields";
export { Extensions } from "./Extensions";

0 comments on commit ee0a38a

Please sign in to comment.