Skip to content

Commit

Permalink
SPIKE Inline validation
Browse files Browse the repository at this point in the history
  • Loading branch information
emteknetnz committed Feb 28, 2024
1 parent 136f1ea commit 78b7cc6
Show file tree
Hide file tree
Showing 10 changed files with 4,816 additions and 297 deletions.
3,845 changes: 3,835 additions & 10 deletions client/dist/js/bundle.js

Large diffs are not rendered by default.

484 changes: 483 additions & 1 deletion client/dist/styles/bundle.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/src/boot/registerTransforms.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import readBlocksForAreaQuery from 'state/editor/readBlocksForAreaQuery';
import addElementToArea from 'state/editor/addElementMutation';
import ArchiveAction from 'components/ElementActions/ArchiveAction';
import DuplicateAction from 'components/ElementActions/DuplicateAction';
import PublishAction from 'components/ElementActions/PublishAction';
import SaveAction from 'components/ElementActions/SaveAction';
import PublishAction from 'components/ElementActions/PublishAction';
import UnpublishAction from 'components/ElementActions/UnpublishAction';

export default () => {
Expand Down
208 changes: 99 additions & 109 deletions client/src/components/ElementActions/PublishAction.js
Original file line number Diff line number Diff line change
@@ -1,122 +1,97 @@
/* global window */
import React from 'react';
import React, { useContext, useEffect } from 'react';
import { compose } from 'redux';
import AbstractAction from 'components/ElementActions/AbstractAction';
import publishBlockMutation from 'state/editor/publishBlockMutation';
import i18n from 'i18n';
import backend from 'lib/Backend';
import { connect } from 'react-redux';
import { loadElementSchemaValue } from 'state/editor/loadElementSchemaValue';
import { loadElementFormStateName } from 'state/editor/loadElementFormStateName';
import { initialize } from 'redux-form';

/**
* Show a toast message reporting whether publication of Element was successful
*
* @param {string} type E.g. "Content" - human friendly element type (not PHP FQCN)
* @param {string} title Title of the element, or a false value if unset (e.g. undefined)
* @param {boolean} success Show a success message (true), or an error message (false)
*/
const reportPublicationStatus = (type, title, success) => {
const noTitle = i18n.inject(
i18n._t('ElementHeader.NOTITLE', 'Untitled {type} block'),
{ type }
);
const successMessage = i18n.inject(
i18n._t('ElementPublishAction.SUCCESS_NOTIFICATION', 'Published \'{title}\' successfully'),
{ title: title || noTitle }
);
const errorMessage = i18n.inject(
i18n._t('ElementPublishAction.ERROR_NOTIFICATION', 'Error publishing \'{title}\''),
{ title: title || noTitle }
);
window.jQuery.noticeAdd({
text: success ? successMessage : errorMessage,
stay: false,
type: success ? 'success' : 'error',
});
};

/**
* Post updated Element data to save it
*
* @param {number} id Element ID
* @param {object} formData Information to be saved
* @param {string} securityId Security ID for form submission
*/
const performSaveForElementWithFormData = (id, formData, securityId) => {
const saveEndpoint = backend.createEndpointFetcher({
url: loadElementSchemaValue('saveUrl', id),
method: loadElementSchemaValue('saveMethod'),
payloadFormat: loadElementSchemaValue('payloadFormat'),
defaultData: {
SecurityID: securityId
},
});

// Perform save & get new version number to publish
return saveEndpoint(formData)
.then(() => window.ss.apolloClient.queryManager.reFetchObservableQueries())
.then((input) => {
const preview = window.jQuery('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
return input;
})
.then((newPageData) => {
const newElementData = newPageData[0] && newPageData[0]
.data
.readOneElementalArea
.elements
.find((elementData) => elementData.id === id);
return newElementData && newElementData.version;
});
};
import { ElementContext } from 'components/ElementEditor/Element';

// /**
// * Post updated Element data to save it
// *
// * @param {number} id Element ID
// * @param {object} formData Information to be saved
// * @param {string} securityId Security ID for form submission
// */
// const performSaveForElementWithFormData = (id, formData, securityId) => {
// // Perform save & get new version number to publish
// return saveEndpoint(formData)
// .then(() => window.ss.apolloClient.queryManager.reFetchObservableQueries())
// .then((input) => {
// const preview = window.jQuery('.cms-preview');
// preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));
// return input;
// })
// .then((newPageData) => {
// const newElementData = newPageData[0] && newPageData[0]
// .data
// .readOneElementalArea
// .elements
// .find((elementData) => elementData.id === id);
// return newElementData && newElementData.version;
// });
// };

/**
* Adds the elemental menu action to publish a draft/modified block
*/
const PublishAction = (MenuComponent) => (props) => {
if (props.type.broken) {
// Don't allow this action for a broken element.
return (
<MenuComponent {...props} />
const { saveElement, onPublishButtonClick, doPublishElement, onAfterPublish } = useContext(ElementContext);
const {
element,
type,
securityId,
formData,
actions: { handlePublishBlock },
reinitialiseForm,
formDirty,
} = props;

/**
* Show a toast message reporting whether publication of Element was successful
*
* @param {string} type E.g. "Content" - human friendly element type (not PHP FQCN)
* @param {string} title Title of the element, or a false value if unset (e.g. undefined)
* @param {boolean} success Show a success message (true), or an error message (false)
*/
const reportPublicationStatus = (type, title, success) => {
const noTitle = i18n.inject(
i18n._t('ElementHeader.NOTITLE', 'Untitled {type} block'),
{ type }
);
}
const successMessage = i18n.inject(
i18n._t('ElementPublishAction.SUCCESS_NOTIFICATION', 'Published \'{title}\' successfully'),
{ title: title || noTitle }
);
const errorMessage = i18n.inject(
i18n._t('ElementPublishAction.ERROR_NOTIFICATION', 'Error publishing \'{title}\''),
{ title: title || noTitle }
);
window.jQuery.noticeAdd({
text: success ? successMessage : errorMessage,
stay: false,
type: success ? 'success' : 'error',
});
};

const { element, formDirty } = props;
const publishElement = () => {
console.log('publishElement');
// handlePublishBlock is a graphql mutation defined in publishBlockMutation.js
handlePublishBlock(element.id)
.then(() => reportPublicationStatus(type.title, element.title, true))
.catch(() => reportPublicationStatus(type.title, element.title, false));
onAfterPublish();
}

const handleClick = (event) => {
event.stopPropagation();

const {
element: {
id,
title,
},
type,
securityId,
formData,
actions: { handlePublishBlock },
reinitialiseForm,
} = props;

let actionFlow = new Promise((resolve) => resolve());

// Edits have been made to the form. Peform a "Save & Publish"
onPublishButtonClick(formDirty);
if (formDirty) {
actionFlow = performSaveForElementWithFormData(id, formData, securityId)
.then((passthrough) => {
reinitialiseForm(formData);
return passthrough;
});
saveElement();
}

// Perform publish. Data is assumed to be up to date
actionFlow
.then(() => handlePublishBlock(id))
.then(() => reportPublicationStatus(type.title, title, true))
.catch(() => reportPublicationStatus(type.title, title, false));
};
}

const disabled = props.element.canPublish !== undefined && !props.element.canPublish;
const label = i18n._t('ElementArchiveAction.PUBLISH', 'Publish');
Expand All @@ -132,6 +107,20 @@ const PublishAction = (MenuComponent) => (props) => {
toggle: props.toggle,
};

console.log('doPublishElement is', doPublishElement);
useEffect(() => {
if (doPublishElement) {
publishElement();
}
}, [doPublishElement]);

if (props.type.broken) {
// Don't allow this action for a broken element.
return (
<MenuComponent {...props} />
);
}

return (
<MenuComponent {...props}>
{props.children}
Expand All @@ -156,20 +145,21 @@ function mapStateToProps(state, ownProps) {
};
}

function mapDispatchToProps(dispatch, ownProps) {
const formName = loadElementFormStateName(ownProps.element.id);
// function mapDispatchToProps(dispatch, ownProps) {
// const formName = loadElementFormStateName(ownProps.element.id);

return {
reinitialiseForm(savedData) {
dispatch(initialize(`element.${formName}`, savedData));
}
};
}
// return {
// reinitialiseForm(savedData) {
// dispatch(initialize(`element.${formName}`, savedData));
// }
// };
// }

export { PublishAction as Component };

export default compose(
publishBlockMutation,
connect(mapStateToProps, mapDispatchToProps),
// connect(mapStateToProps, mapDispatchToProps),
connect(mapStateToProps),
PublishAction
);
83 changes: 6 additions & 77 deletions client/src/components/ElementActions/SaveAction.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import React from 'react';
import React, { useContext } from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import AbstractAction from 'components/ElementActions/AbstractAction';
import backend from 'lib/Backend';
import i18n from 'i18n';
import { loadElementSchemaValue } from 'state/editor/loadElementSchemaValue';
import { loadElementFormStateName } from 'state/editor/loadElementFormStateName';
import { initialize } from 'redux-form';
import { ElementContext } from 'components/ElementEditor/Element';

/**
* Using a REST backend, serialize the current form data and post it to the backend endpoint to save
* the inline edit form's data for the current block.
*/
const SaveAction = (MenuComponent) => (props) => {
const { saveElement } = useContext(ElementContext);

if (!props.expandable || props.type.broken) {
// Some elemental blocks can not be edited inline (e.g. User form blocks)
// We don't want to add a "Save" action for those blocks.
Expand All @@ -23,64 +19,7 @@ const SaveAction = (MenuComponent) => (props) => {

const handleClick = (event) => {
event.stopPropagation();

const { element, type, securityId, formData, reinitialiseForm } = props;
const { jQuery: $ } = window;
const noTitle = i18n.inject(
i18n._t(
'ElementHeader.NOTITLE',
'Untitled {type} block'
),
{ type: type.title }
);

const endpointSpec = {
url: loadElementSchemaValue('saveUrl', element.id),
method: loadElementSchemaValue('saveMethod'),
payloadFormat: loadElementSchemaValue('payloadFormat'),
defaultData: {
SecurityID: securityId
},
};

const endpoint = backend.createEndpointFetcher(endpointSpec);
endpoint(formData)
.then(() => {
// Update the Apollo query cache with the new form data
const { apolloClient } = window.ss;

apolloClient.queryManager.reFetchObservableQueries();
reinitialiseForm(formData);

const preview = $('.cms-preview');
preview.entwine('ss.preview')._loadUrl(preview.find('iframe').attr('src'));

const newTitle = formData ? formData[`PageElements_${element.id}_Title`] : null;
$.noticeAdd({
text: i18n.inject(
i18n._t(
'ElementSaveAction.SUCCESS_NOTIFICATION',
'Saved \'{title}\' successfully'
),
{ title: newTitle || noTitle }
),
stay: false,
type: 'success'
});
})
.catch(() => {
$.noticeAdd({
text: i18n.inject(
i18n._t(
'ElementSaveAction.ERROR_NOTIFICATION',
'Error saving \'{title}\''
),
{ title: element.Title || noTitle }
),
stay: false,
type: 'error'
});
});
saveElement();
};

const newProps = {
Expand Down Expand Up @@ -113,16 +52,6 @@ function mapStateToProps(state, ownProps) {
};
}

function mapDispatchToProps(dispatch, ownProps) {
const formName = loadElementFormStateName(ownProps.element.id);

return {
reinitialiseForm(savedData) {
dispatch(initialize(`element.${formName}`, savedData));
}
};
}

export { SaveAction as Component };

export default compose(connect(mapStateToProps, mapDispatchToProps), SaveAction);
export default compose(connect(mapStateToProps), SaveAction);
3 changes: 3 additions & 0 deletions client/src/components/ElementEditor/Content.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Content extends PureComponent {
handleLoadingError,
formDirty,
broken,
onFormSchemaResponse,
} = this.props;

return (
Expand All @@ -44,6 +45,7 @@ class Content extends PureComponent {
activeTab={activeTab}
onFormInit={onFormInit}
handleLoadingError={handleLoadingError}
onFormSchemaResponse={onFormSchemaResponse}
/>
}
{formDirty &&
Expand All @@ -69,6 +71,7 @@ Content.propTypes = {
InlineEditFormComponent: PropTypes.elementType,
handleLoadingError: PropTypes.func,
broken: PropTypes.bool,
onFormSchemaResponse: PropTypes.func,
};

Content.defaultProps = {};
Expand Down
Loading

0 comments on commit 78b7cc6

Please sign in to comment.