Skip to content

Commit

Permalink
NEW Fixing change tracking and adding an icon to show unsaved changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ScopeyNZ committed Nov 26, 2018
1 parent 553e5ab commit b91056b
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 1,343 deletions.
37 changes: 31 additions & 6 deletions client/src/components/ElementEditor/Content.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import React, { PureComponent, PropTypes } from 'react';
import { inject } from 'lib/Injector';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { loadElementFormStateName } from 'state/editor/loadElementFormStateName';
import { isDirty } from 'redux-form';
import getFormState from 'lib/getFormState';

class Content extends PureComponent {
render() {
Expand All @@ -14,6 +19,7 @@ class Content extends PureComponent {
activeTab,
onFormInit,
handleLoadingError,
formDirty,
} = this.props;

return (
Expand All @@ -37,6 +43,14 @@ class Content extends PureComponent {
handleLoadingError={handleLoadingError}
/>
}
{formDirty &&
<input
type="hidden"
name="change-tracker"
className="element-form-dirty-state"
value="1"
/>
}
</div>
);
}
Expand All @@ -55,12 +69,23 @@ Content.propTypes = {

Content.defaultProps = {};

function mapStateToProps(state, ownProps) {
const formName = loadElementFormStateName(ownProps.id);

return {
formDirty: isDirty(`element.${formName}`, getFormState)(state),
};
}

export { Content as Component };

export default inject(
['ElementSummary', 'ElementInlineEditForm'],
(SummaryComponent, InlineEditFormComponent) => ({
SummaryComponent, InlineEditFormComponent,
}),
() => 'ElementEditor.ElementList.Element'
export default compose(
inject(
['ElementSummary', 'ElementInlineEditForm'],
(SummaryComponent, InlineEditFormComponent) => ({
SummaryComponent, InlineEditFormComponent,
}),
() => 'ElementEditor.ElementList.Element'
),
connect(mapStateToProps)
)(Content);
7 changes: 6 additions & 1 deletion client/src/components/ElementEditor/ElementEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,12 @@ class ElementEditor extends PureComponent {
dragTargetElementId={dragTargetElementId}
/>
<ElementDragPreview />
<input name={fieldName} type="hidden" value={JSON.stringify(formState) || ''} />
<input
name={fieldName}
type="hidden"
value={JSON.stringify(formState) || ''}
className="no-change-track"
/>
</div>
);
}
Expand Down
34 changes: 26 additions & 8 deletions client/src/components/ElementEditor/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import { Tooltip } from 'reactstrap';
import { elementType } from 'types/elementType';
import { elementTypeType } from 'types/elementTypeType';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { inject } from 'lib/Injector';
import i18n from 'i18n';
import classNames from 'classnames';
import { loadElementFormStateName } from 'state/editor/loadElementFormStateName';
import { isDirty } from 'redux-form';
import getFormState from 'lib/getFormState';

class Header extends Component {
constructor(props) {
Expand Down Expand Up @@ -46,22 +50,26 @@ class Header extends Component {
* @returns {DOMElement|null}
*/
renderVersionedStateMessage() {
const { element: { IsLiveVersion: isLiveVersion, IsPublished: isPublished } } = this.props;
const {
element: { IsLiveVersion: isLiveVersion, IsPublished: isPublished },
formDirty,
} = this.props;

// No indication required for published elements
if (isPublished && isLiveVersion) {
if (!formDirty && isPublished && isLiveVersion) {
return null;
}

let versionStateButtonTitle = '';
const stateClassNames = ['element-editor-header__version-state'];

if (!isPublished) {
if (formDirty) {
versionStateButtonTitle = i18n._t('ElementHeader.STATE_UNSAVED', 'Item has unsaved changes');
stateClassNames.push('element-editor-header__version-state--unsaved');
} else if (!isPublished) {
versionStateButtonTitle = i18n._t('ElementHeader.STATE_DRAFT', 'Item has not been published yet');
stateClassNames.push('element-editor-header__version-state--draft');
}

if (isPublished && !isLiveVersion) {
} else if (!isLiveVersion) {
versionStateButtonTitle = i18n._t('ElementHeader.STATE_MODIFIED', 'Item has unpublished changes');
stateClassNames.push('element-editor-header__version-state--modified');
}
Expand Down Expand Up @@ -97,7 +105,7 @@ class Header extends Component {
const expandTitle = i18n._t('ElementHeader.EXPAND', 'Show editable fields');
const containerClasses = classNames(
'element-editor-header', {
'element-editor-header--simple': simple
'element-editor-header--simple': simple,
}
);
const expandCaretClasses = classNames(
Expand Down Expand Up @@ -158,12 +166,21 @@ Header.propTypes = {
ElementActionsComponent: React.PropTypes.oneOfType([React.PropTypes.node, React.PropTypes.func]),
previewExpanded: PropTypes.bool,
disableTooltip: PropTypes.bool,
formDirty: PropTypes.bool,
};

Header.defaultProps = {
expandable: true,
};

function mapStateToProps(state, ownProps) {
const formName = loadElementFormStateName(ownProps.element.ID);

return {
formDirty: isDirty(`element.${formName}`, getFormState)(state),
};
}

export { Header as Component };

export default compose(
Expand All @@ -173,5 +190,6 @@ export default compose(
ElementActionsComponent,
}),
() => 'ElementEditor.ElementList.Element'
)
),
connect(mapStateToProps)
)(Header);
5 changes: 5 additions & 0 deletions client/src/components/ElementEditor/Header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@
width: 8px;
z-index: 1;

&--unsaved {
background-color: $link-color;
border: 1px solid $link-hover-color;
}

&--draft {
background-color: $state-draft-bg;
}
Expand Down
38 changes: 38 additions & 0 deletions client/src/legacy/ElementEditor/entwine.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,42 @@ jQuery.entwine('ss', ($) => {
}
},
});

$('.js-injector-boot .element-editor__container .element-form-dirty-state').entwine({
onmatch() {
$('.cms-edit-form').trigger('change');
},
onunmatch() {
$('.cms-edit-form').trigger('change');
}
});

// Prevent dirty form detection on any field loaded in with FormBuilderLoader.
// This looks pretty hacky, and it is. This is mostly copied from some code in subsites that does
// a similar thing (with some history):
$('.cms-edit-form').entwine({
getChangeTrackerOptions() {
// Figure out if we're still returning the default value
const isDefault = (this.entwineData('ChangeTrackerOptions') === undefined);
// Get the current options
let opts = this._super();

if (isDefault) {
// If it is the default then...
// clone the object (so we don't modify the original),
opts = $.extend({}, opts);
// modify it,
opts.ignoreFieldSelector += ', .elementalarea :input:not(.element-form-dirty-state)';
if (opts.fieldSelector === undefined) {
opts.fieldSelector = ':input:not(:button,[type="submit"],[type="search"],.gridstate)';
}
opts.fieldSelector += ', .element-editor-header';
// then set the clone as the value on this element
// (so next call to this method gets this same clone)
this.setChangeTrackerOptions(opts);
}

return opts;
}
});
});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"react-dnd-html5-backend": "^2.2.3",
"react-redux": "^4.4.1",
"reactstrap": "^5.0.0-beta",
"redux": "^3.3.1"
"redux": "^3.3.1",
"redux-form": "^6.8.0"
},
"devDependencies": {
"@silverstripe/eslint-config": "^0.0.2",
Expand Down
2 changes: 1 addition & 1 deletion src/Forms/ElementalAreaField.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public function __construct($name, ElementalArea $area, array $blockTypes)
parent::__construct($name, '', $area->Elements(), $config);
$this->area = $area;

$this->addExtraClass('element-editor__container');
$this->addExtraClass('element-editor__container no-change-track');
}

/**
Expand Down
Loading

0 comments on commit b91056b

Please sign in to comment.