Skip to content

Commit

Permalink
Added global form state. (#5721) (#5741)
Browse files Browse the repository at this point in the history
Co-authored-by: Rob Gietema <rob.gietema@gmail.com>
  • Loading branch information
sneridagh and robgietema authored Feb 7, 2024
1 parent 3929ece commit b3e8bed
Show file tree
Hide file tree
Showing 11 changed files with 158 additions and 53 deletions.
1 change: 1 addition & 0 deletions news/5721.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add global form state. @robgietema
1 change: 0 additions & 1 deletion packages/types/news/5718.bugfix

This file was deleted.

19 changes: 19 additions & 0 deletions src/actions/form/form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Form actions.
* @module actions/form/form
*/

import { SET_FORM_DATA } from '@plone/volto/constants/ActionTypes';

/**
* Set form data function.
* @function setFormData
* @param {Object} data New form data.
* @returns {Object} Set sidebar action.
*/
export function setFormData(data) {
return {
type: SET_FORM_DATA,
data,
};
}
14 changes: 14 additions & 0 deletions src/actions/form/form.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { setFormData } from './form';
import { SET_FORM_DATA } from '@plone/volto/constants/ActionTypes';

describe('Form action', () => {
describe('setFormData', () => {
it('should create an action to set the form data', () => {
const data = { foo: 'bar' };
const action = setFormData(data);

expect(action.type).toEqual(SET_FORM_DATA);
expect(action.data).toEqual(data);
});
});
});
1 change: 1 addition & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export {
export { getQuerystring } from '@plone/volto/actions/querystring/querystring';
export { getQueryStringResults } from '@plone/volto/actions/querystringsearch/querystringsearch';
export { setSidebarTab } from '@plone/volto/actions/sidebar/sidebar';
export { setFormData } from '@plone/volto/actions/form/form';
export {
deleteLinkTranslation,
getTranslationLocator,
Expand Down
1 change: 1 addition & 0 deletions src/components/manage/Add/Add.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ class Add extends Component {
onSelectForm={() => {
this.setState({ formSelected: 'addForm' });
}}
global
/>
{this.state.isClient && (
<Portal node={document.getElementById('toolbar')}>
Expand Down
1 change: 1 addition & 0 deletions src/components/manage/Edit/Edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ class Edit extends Component {
onSelectForm={() => {
this.setState({ formSelected: 'editForm' });
}}
global
/>
);

Expand Down
140 changes: 92 additions & 48 deletions src/components/manage/Form/Form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import clearSVG from '@plone/volto/icons/clear.svg';
import {
findIndex,
isEmpty,
isEqual,
keys,
map,
mapValues,
Expand All @@ -40,7 +41,7 @@ import {
import { v4 as uuid } from 'uuid';
import { toast } from 'react-toastify';
import { BlocksToolbar, UndoToolbar } from '@plone/volto/components';
import { setSidebarTab } from '@plone/volto/actions';
import { setSidebarTab, setFormData } from '@plone/volto/actions';
import { compose } from 'redux';
import config from '@plone/volto/registry';

Expand Down Expand Up @@ -69,6 +70,7 @@ class Form extends Component {
required: PropTypes.arrayOf(PropTypes.string),
}),
formData: PropTypes.objectOf(PropTypes.any),
globalData: PropTypes.objectOf(PropTypes.any),
pathname: PropTypes.string,
onSubmit: PropTypes.func,
onCancel: PropTypes.func,
Expand All @@ -93,6 +95,7 @@ class Form extends Component {
requestError: PropTypes.string,
allowedBlocks: PropTypes.arrayOf(PropTypes.string),
showRestricted: PropTypes.bool,
global: PropTypes.bool,
};

/**
Expand Down Expand Up @@ -123,6 +126,7 @@ class Form extends Component {
editable: true,
requestError: null,
allowedBlocks: null,
global: false,
};

/**
Expand Down Expand Up @@ -201,6 +205,12 @@ class Form extends Component {
}
}

// Sync state to global state
if (this.props.global) {
this.props.setFormData(formData);
}

// Set initial state
this.state = {
formData,
initialFormData,
Expand Down Expand Up @@ -246,14 +256,18 @@ class Form extends Component {
}

if (this.props.onChangeFormData) {
if (
// TODO: use fast-deep-equal
JSON.stringify(prevState?.formData) !==
JSON.stringify(this.state.formData)
) {
if (!isEqual(prevState?.formData, this.state.formData)) {
this.props.onChangeFormData(this.state.formData);
}
}
if (
this.props.global &&
!isEqual(this.props.globalData, this.state.formData)
) {
this.setState({
formData: this.props.globalData,
});
}
}

/**
Expand Down Expand Up @@ -327,15 +341,18 @@ class Form extends Component {
onChangeField(id, value) {
this.setState((prevState) => {
const { errors, formData } = prevState;
const newFormData = {
...formData,
// We need to catch also when the value equals false this fixes #888
[id]: value || (value !== undefined && isBoolean(value)) ? value : null,
};
delete errors[id];
if (this.props.global) {
this.props.setFormData(newFormData);
}
return {
errors,
formData: {
...formData,
// We need to catch also when the value equals false this fixes #888
[id]:
value || (value !== undefined && isBoolean(value)) ? value : null,
},
formData: newFormData,
// Changing the form data re-renders the select widget which causes the
// focus to get lost. To circumvent this, we set the focus back to
// the input.
Expand All @@ -357,14 +374,13 @@ class Form extends Component {
onSelectBlock(id, isMultipleSelection, event) {
let multiSelected = [];
let selected = id;
const formData = this.state.formData;

if (isMultipleSelection) {
selected = null;
const blocksLayoutFieldname = getBlocksLayoutFieldname(
this.state.formData,
);
const blocksLayoutFieldname = getBlocksLayoutFieldname(formData);

const blocks_layout = this.state.formData[blocksLayoutFieldname].items;
const blocks_layout = formData[blocksLayoutFieldname].items;

if (event.shiftKey) {
const anchor =
Expand Down Expand Up @@ -424,6 +440,9 @@ class Form extends Component {
this.setState({
formData: this.props.formData,
});
if (this.props.global) {
this.props.setFormData(this.props.formData);
}
}
this.props.onCancel(event);
}
Expand All @@ -435,14 +454,16 @@ class Form extends Component {
* @returns {undefined}
*/
onSubmit(event) {
const formData = this.state.formData;

if (event) {
event.preventDefault();
}

const errors = this.props.schema
? FormValidation.validateFieldsPerFieldset({
schema: this.props.schema,
formData: this.state.formData,
formData,
formatMessage: this.props.intl.formatMessage,
})
: {};
Expand Down Expand Up @@ -477,12 +498,15 @@ class Form extends Component {
if (this.props.isEditForm) {
this.props.onSubmit(this.getOnlyFormModifiedValues());
} else {
this.props.onSubmit(this.state.formData);
this.props.onSubmit(formData);
}
if (this.props.resetAfterSubmit) {
this.setState({
formData: this.props.formData,
});
if (this.props.global) {
this.props.setFormData(this.props.formData);
}
}
}
}
Expand All @@ -497,15 +521,15 @@ class Form extends Component {
* @returns {undefined}
*/
getOnlyFormModifiedValues = () => {
const formData = this.state.formData;

const fieldsModified = Object.keys(
difference(this.state.formData, this.state.initialFormData),
difference(formData, this.state.initialFormData),
);
return {
...pickBy(this.state.formData, (value, key) =>
fieldsModified.includes(key),
),
...(this.state.formData['@static_behaviors'] && {
'@static_behaviors': this.state.formData['@static_behaviors'],
...pickBy(formData, (value, key) => fieldsModified.includes(key)),
...(formData['@static_behaviors'] && {
'@static_behaviors': formData['@static_behaviors'],
}),
};
};
Expand Down Expand Up @@ -551,7 +575,7 @@ class Form extends Component {
navRoot,
type,
} = this.props;
const { formData } = this.state;
const formData = this.state.formData;
const schema = this.removeBlocksLayoutFields(originalSchema);
const Container =
config.getComponent({ name: 'Container' }).component || SemanticContainer;
Expand All @@ -562,40 +586,53 @@ class Form extends Component {
this.state.isClient && (
<Container>
<BlocksToolbar
formData={this.state.formData}
formData={formData}
selectedBlock={this.state.selected}
selectedBlocks={this.state.multiSelected}
onChangeBlocks={(newBlockData) =>
onChangeBlocks={(newBlockData) => {
const newFormData = {
...formData,
...newBlockData,
};
this.setState({
formData: {
...formData,
...newBlockData,
},
})
}
formData: newFormData,
});
if (this.props.global) {
this.props.setFormData(newFormData);
}
}}
onSetSelectedBlocks={(blockIds) =>
this.setState({ multiSelected: blockIds })
}
onSelectBlock={this.onSelectBlock}
/>
<UndoToolbar
state={{
formData: this.state.formData,
formData,
selected: this.state.selected,
multiSelected: this.state.multiSelected,
}}
enableHotKeys
onUndoRedo={({ state }) => this.setState(state)}
onUndoRedo={({ state }) => {
if (this.props.global) {
this.props.setFormData(state.formData);
}
return this.setState(state);
}}
/>
<BlocksForm
onChangeFormData={(newFormData) =>
onChangeFormData={(newData) => {
const newFormData = {
...formData,
...newData,
};
this.setState({
formData: {
...formData,
...newFormData,
},
})
}
formData: newFormData,
});
if (this.props.global) {
this.props.setFormData(newFormData);
}
}}
onChangeField={this.onChangeField}
onSelectBlock={this.onSelectBlock}
properties={formData}
Expand Down Expand Up @@ -635,9 +672,9 @@ class Form extends Component {
{...schema.properties[field]}
id={field}
fieldSet={item.title.toLowerCase()}
formData={this.state.formData}
formData={formData}
focus={this.state.inFocus[field]}
value={this.state.formData?.[field]}
value={formData?.[field]}
required={schema.required.indexOf(field) !== -1}
onChange={this.onChangeField}
onBlur={this.onBlurField}
Expand Down Expand Up @@ -700,10 +737,10 @@ class Form extends Component {
{...schema.properties[field]}
isDisabled={!this.props.editable}
id={field}
formData={this.state.formData}
formData={formData}
fieldSet={item.title.toLowerCase()}
focus={this.state.inFocus[field]}
value={this.state.formData?.[field]}
value={formData?.[field]}
required={schema.required.indexOf(field) !== -1}
onChange={this.onChangeField}
onBlur={this.onBlurField}
Expand Down Expand Up @@ -751,7 +788,7 @@ class Form extends Component {
<Field
{...schema.properties[field]}
id={field}
value={this.state.formData?.[field]}
value={formData?.[field]}
required={schema.required.indexOf(field) !== -1}
onChange={this.onChangeField}
onBlur={this.onBlurField}
Expand Down Expand Up @@ -812,5 +849,12 @@ class Form extends Component {
const FormIntl = injectIntl(Form, { forwardRef: true });

export default compose(
connect(null, { setSidebarTab }, null, { forwardRef: true }),
connect(
(state, props) => ({
globalData: state.form?.global,
}),
{ setSidebarTab, setFormData },
null,
{ forwardRef: true },
),
)(FormIntl);
1 change: 1 addition & 0 deletions src/constants/ActionTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,4 @@ export const POST_UPGRADE = 'POST_UPGRADE';
export const RESET_LOGIN_REQUEST = 'RESET_LOGIN_REQUEST';
export const GET_SITE = 'GET_SITE';
export const GET_NAVROOT = 'GET_NAVROOT';
export const SET_FORM_DATA = 'SET_FORM_DATA';
Loading

0 comments on commit b3e8bed

Please sign in to comment.