Skip to content
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

Show comparisons between versions in history viewer #33

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
0fc7660
NEW Add diff view form transform for comparisons
Jul 9, 2018
e025e91
Add redux support for comparison view
Jul 18, 2018
2a45f25
Add compare button on active version view
Jul 18, 2018
11ea083
NEW Adding a compare mode active notice to the history viewer
ScopeyNZ Jul 18, 2018
359a260
NEW Dropdown atop history viewer for holding actions
raissanorth Jul 18, 2018
db72f6d
NEW Adjust history viewer to allow for compare mode
Jul 18, 2018
fb28afa
Refactor history viewer components to be more maintainable
robbieaverill Jul 19, 2018
f365395
Add tests to HistoryViewerHeading and slightly refactor it by derefer…
raissanorth Jul 19, 2018
75c2c85
NEW Add schema endpoint and shell method for returning compareForm
robbieaverill Jul 19, 2018
c48f5a1
Refactor compare warning into own component
ScopeyNZ Jul 19, 2018
e029b73
NEW Alter components to allow for compare mode
Jul 19, 2018
a47bdd0
Add jest unit tests for the historyViewerReducer
robbieaverill Jul 23, 2018
f3f6a56
MINOR Refactor HistoryViewerController to centralise input validation…
robbieaverill Jul 22, 2018
a8e0d63
NEW styles and fixes for styling and code cleanliness
robbieaverill Jul 23, 2018
92eab59
FIX selecting two rows in list view UI issues
ScopeyNZ Jul 23, 2018
9d26ba0
Fix margins when choosing versions to compare (#11)
raissanorth Jul 23, 2018
09de65b
Refactoring compare properties to a consistent shoaped object (#15)
ScopeyNZ Jul 23, 2018
72938ef
Add colours to dels and ins (#16)
raissanorth Jul 23, 2018
55dc009
FIX SET_COMPARE_MODE reducer no longer clears previously set compareF…
robbieaverill Jul 23, 2018
aeaaa86
Logic for Diff rendering moved from Transformation to new Field
Jul 24, 2018
0cdd00c
Refactor tests to use Enzyme and add tests for HistoryViewer and Hist…
raissanorth Jul 24, 2018
c8e9047
Removing the addition of old render methods that were previously extr…
ScopeyNZ Jul 24, 2018
9ac05c7
FIX Set min-width for compare enabled table to 100% to prevent margin…
robbieaverill Jul 23, 2018
35afd7f
FIX Padding and scrolling around detail views is now consistent
robbieaverill Jul 23, 2018
064bb9a
NEW Add behat tests for using compare mode
robbieaverill Jul 24, 2018
4900fed
Simplifying actions definitions that dispatch a single action
ScopeyNZ Jul 24, 2018
6c421b5
FIX Badge margin is moved to the text to allow it to break lines nice…
robbieaverill Jul 24, 2018
6a8b7ea
Removing option to fix compare notice & adding padding under it
ScopeyNZ Jul 24, 2018
3dabca1
Use classnames helper on object defined classes
ScopeyNZ Jul 24, 2018
3e044dc
FIX History viewer list is now rendered as an unordered list instead …
robbieaverill Jul 24, 2018
dee3fc2
NEW History Viewer now uses ViewModeToggle to control the preview panel
robbieaverill Jul 24, 2018
786990c
Add tests for HistoryViewerVersion
raissanorth Jul 24, 2018
6b5b3d4
Update feature context since the versions are list items now instead …
raissanorth Jul 25, 2018
2758348
fix rebase
Jul 30, 2018
47e3ae2
API Versions from and to are now stored as objects in the store (#29)
NightJar Jul 31, 2018
763138f
FIX remove double-margins from version comparison
Aug 1, 2018
8e611ac
FIX Convert compareType to use version objects, restructure tests and…
robbieaverill Aug 1, 2018
58d128e
FIX Update case on viewModeActions import and recompile
robbieaverill Aug 1, 2018
855e1f2
FIX Margins are correct when both in and out of a GridField, and colo…
robbieaverill Aug 6, 2018
2614804
FIX getLatestVersion now looks at the LatestDraftVersion version prop…
robbieaverill Aug 6, 2018
aed0726
FIX Add roles to list and list items, make list items keyboard access…
robbieaverill Aug 8, 2018
eed1ca9
FIX Change list item anchor for span with role="button" to justify us…
robbieaverill Aug 8, 2018
8611e47
FIX Allow more width for version numbers in list, so alignment of sta…
raissanorth Aug 10, 2018
b5a8286
Update Behat tests to remove merge conflict and old references to anc…
robbieaverill Aug 12, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/dist/js/bundle.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/dist/styles/bundle.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions client/src/boot/registerComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import HistoryViewerVersion from 'components/HistoryViewer/HistoryViewerVersion'
import HistoryViewerVersionDetail from 'components/HistoryViewer/HistoryViewerVersionDetail';
import HistoryViewerVersionList from 'components/HistoryViewer/HistoryViewerVersionList';
import HistoryViewerVersionState from 'components/HistoryViewer/HistoryViewerVersionState';
import HistoryViewerCompareWarning from 'components/HistoryViewer/HistoryViewerCompareWarning';

export default () => {
Injector.component.registerMany({
Expand All @@ -16,6 +17,7 @@ export default () => {
HistoryViewerVersionDetail,
HistoryViewerVersionList,
HistoryViewerVersionState,
HistoryViewerCompareWarning,
});
};

200 changes: 166 additions & 34 deletions client/src/components/HistoryViewer/HistoryViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ import historyViewerConfig from 'containers/HistoryViewer/HistoryViewerConfig';
import i18n from 'i18n';
import { inject } from 'lib/Injector';
import Loading from 'components/Loading/Loading';
import { setCurrentPage, showVersion } from 'state/historyviewer/HistoryViewerActions';
import {
setCurrentPage,
showVersion,
clearMessages,
} from 'state/historyviewer/HistoryViewerActions';
import { versionType } from 'types/versionType';
import { compareType } from 'types/compareType';
import classNames from 'classnames';
import ResizeAware from 'react-resize-aware';
import * as viewModeActions from 'state/viewMode/ViewModeActions';

/**
* The HistoryViewer component is abstract, and requires an Injector component
Expand Down Expand Up @@ -65,26 +73,69 @@ class HistoryViewer extends Component {
}

/**
* Get the latest (highest) version number from the list available. If we are not on page
* zero then it's always false, because there are higher versions that we aren't aware of
* in this context.
* Returns a string to be used as the "class" attribute on the history viewer container
*
* @returns {object}
* @returns {string}
*/
getContainerClasses() {
const { compare, isInGridField } = this.props;

// GridFieldDetailForm provides its own padding, so apply a class to counteract this.
return classNames('history-viewer', 'fill-height', {
'history-viewer__compare-mode': compare,
'history-viewer--no-margins': isInGridField && !this.isListView(),
});
}

/**
* Get the latest version from the list available (if there is one)
*
* @returns {object|null}
*/
getLatestVersion() {
const { page } = this.props;
const { currentVersion } = this.props;

// Check whether the "current version" (in the store) is the latest draft
if (currentVersion && currentVersion.LatestDraftVersion === true) {
return currentVersion;
}

// Look for one in the list of available versions
const latestDraftVersion = this.getVersions()
.filter(version => version.LatestDraftVersion === true);

if (latestDraftVersion.length) {
return latestDraftVersion[0];
}

return null;
}

// Page numbers are not zero based as they come from GriddlePage numbers
if (page > 1) {
/**
* List view is when either no current version is set, or only one of the two versions is
* set for compare mode
*
* @returns {boolean}
*/
isListView() {
const { compare, currentVersion } = this.props;

// Nothing is set: initial list view
if (!currentVersion) {
return true;
}

// No compare mode data set: it's detail view
if (!compare) {
return false;
}
return this.getVersions()
.reduce((prev, current) => {
if (prev.Version > current.Version) {
return prev;
}
return current;
});

// Only part of the compare mode data is set: it's list view
if (compare.versionFrom && !compare.versionTo) {
return true;
}

return false;
}

/**
Expand Down Expand Up @@ -136,31 +187,47 @@ class HistoryViewer extends Component {
recordClass,
schemaUrl,
VersionDetailComponent,
compare,
compare: { versionFrom = false, versionTo = false },
previewState,
} = this.props;

// Insert variables into the schema URL via regex replacements
const schemaReplacements = {
const schemaVersionReplacements = {
':id': recordId,
':class': recordClass,
':version': currentVersion.Version,
};
const schemaCompareReplacements = {
':id': recordId,
':class': recordClass,
':version': currentVersion,
':from': versionFrom.Version || 0,
':to': versionTo.Version || 0,
};
const schemaSearch = compare ? /:id|:class|:from|:to/g : /:id|:class|:version/g;
const schemaReplacements = compare ? schemaCompareReplacements : schemaVersionReplacements;

// eslint-disable-next-line no-shadow
const version = this.getVersions().find(version => version.Version === currentVersion);
const version = compare ? compare.versionFrom : currentVersion;
const latestVersion = this.getLatestVersion();

const props = {
isLatestVersion: latestVersion && latestVersion.Version === version.Version,
// comparison shows two versions as one, so by nature cannot be a single 'latest' version.
isLatestVersion: !compare && latestVersion && latestVersion.Version === version.Version,
isPreviewable,
recordId,
schemaUrl: schemaUrl.replace(/:id|:class|:version/g, (match) => schemaReplacements[match]),
schemaUrl: schemaUrl.replace(schemaSearch, (match) => schemaReplacements[match]),
version,
compare,
previewState,
};

return (
<div className="history-viewer fill-height">
<ResizeAware
className={this.getContainerClasses()}
onResize={({ width }) => this.props.onResize(width)}
>
<VersionDetailComponent {...props} />
</div>
</ResizeAware>
);
}

Expand Down Expand Up @@ -207,20 +274,51 @@ class HistoryViewer extends Component {
);
}

/**
* Render the list containing versions selected for comparison.
* It is not the ListComponent's place to know the context in which it is being rendered
* so it is the directive of this contextual component to tell it what stylistic adaptations
* it should present based on the context (the type of list it contains).
*
* @returns {HistoryViewerVersionList|null}
*/
renderComparisonSelectionList() {
const { compare: { versionFrom }, ListComponent } = this.props;
if (!versionFrom) {
return null;
}
return (
<ListComponent
versions={[versionFrom]}
extraClass="history-viewer__table history-viewer__table--comparison-selected"
/>
);
}

/**
* Renders a list of versions
*
* @returns {HistoryViewerVersionList}
*/
renderVersionList() {
const { isPreviewable, ListComponent, onSelect } = this.props;
const {
isPreviewable,
ListComponent,
CompareWarningComponent,
compare,
compare: { versionFrom: hasVersionFrom },
} = this.props;


return (
<div className="history-viewer fill-height">
<div className={this.getContainerClasses()}>
<CompareWarningComponent />

<div className={isPreviewable ? 'panel panel--padded panel--scrollable' : ''}>
{this.renderComparisonSelectionList()}
<ListComponent
onSelect={onSelect}
versions={this.getVersions()}
showHeader={!compare || (compare && !hasVersionFrom)}
/>

<div className="history-viewer__pagination">
Expand All @@ -231,13 +329,26 @@ class HistoryViewer extends Component {
);
}

renderCompareMode() {
const { compare } = this.props;

if (compare && compare.versionFrom && compare.versionTo) {
return this.renderVersionDetail();
}
return this.renderVersionList();
}

render() {
const { loading, currentVersion } = this.props;
const { loading, compare, currentVersion } = this.props;

if (loading) {
return <Loading />;
}

if (compare) {
return this.renderCompareMode();
}

if (currentVersion) {
return this.renderVersionDetail();
}
Expand All @@ -252,9 +363,12 @@ HistoryViewer.propTypes = {
ListComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
offset: PropTypes.number,
recordId: PropTypes.number.isRequired,
currentVersion: PropTypes.number,
currentVersion: PropTypes.oneOfType([PropTypes.bool, versionType]),
compare: compareType,
isInGridField: PropTypes.bool,
isPreviewable: PropTypes.bool,
VersionDetailComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
CompareWarningComponent: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
versions: PropTypes.shape({
Versions: PropTypes.shape({
pageInfo: PropTypes.shape({
Expand All @@ -267,14 +381,19 @@ HistoryViewer.propTypes = {
}),
page: PropTypes.number,
schemaUrl: PropTypes.string,
// @todo replace this with import { VIEW_MODE_STATES } from 'state/viewMode/ViewModeStates'
// when webpack-config has this export available via silverstripe/admin
previewState: PropTypes.oneOf(['edit', 'preview', 'split']),
actions: PropTypes.object,
onSelect: PropTypes.func,
onSetPage: PropTypes.func,
onResize: PropTypes.func,
};

HistoryViewer.defaultProps = {
contextKey: '',
currentVersion: 0,
currentVersion: false,
isInGridField: false,
isPreviewable: false,
schemaUrl: '',
versions: {
Expand All @@ -289,22 +408,34 @@ HistoryViewer.defaultProps = {


function mapStateToProps(state) {
const { currentPage, currentVersion } = state.versionedAdmin.historyViewer;
const {
currentPage,
currentVersion,
compare,
} = state.versionedAdmin.historyViewer;

const { activeState } = state.viewMode;

return {
page: currentPage,
currentVersion,
compare,
previewState: activeState,
};
}

function mapDispatchToProps(dispatch) {
return {
onSelect(id) {
dispatch(showVersion(id));
dispatch(clearMessages());
},
onSetPage(page) {
dispatch(setCurrentPage(page));
},
onResize(panelWidth) {
dispatch(viewModeActions.enableOrDisableSplitMode(panelWidth));
}
};
}

Expand All @@ -314,10 +445,11 @@ export default compose(
connect(mapStateToProps, mapDispatchToProps),
historyViewerConfig,
inject(
['HistoryViewerVersionList', 'HistoryViewerVersionDetail'],
(HistoryViewerVersionList, HistoryViewerVersionDetail) => ({
ListComponent: HistoryViewerVersionList,
VersionDetailComponent: HistoryViewerVersionDetail,
['HistoryViewerVersionList', 'HistoryViewerVersionDetail', 'HistoryViewerCompareWarning'],
(ListComponent, VersionDetailComponent, CompareWarningComponent) => ({
ListComponent,
VersionDetailComponent,
CompareWarningComponent,
}),
({ contextKey }) => `VersionedAdmin.HistoryViewer.${contextKey}`
)
Expand Down
Loading