Skip to content

Commit

Permalink
Merge pull request #100 from kbase/add_object_reverter_revision
Browse files Browse the repository at this point in the history
Revisions to new object reverter tool and dropdown viewer
  • Loading branch information
charleshtrenholm authored Feb 15, 2022
2 parents 95807ba + 52d4568 commit b87e39c
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default class CopyItem extends Component<ControlMenuItemProps, State> {
const config = Runtime.getConfig();
const wsId = this.props.narrative.access_group;
const objId = this.props.narrative.obj_id;
const { version } = this.props.narrative;
const narrativeService = new DynamicServiceClient({
moduleName: 'NarrativeService',
wizardUrl: config.service_routes.service_wizard,
Expand All @@ -35,7 +36,7 @@ export default class CopyItem extends Component<ControlMenuItemProps, State> {
try {
await narrativeService.call('copy_narrative', [
{
workspaceRef: `${wsId}/${objId}`,
workspaceRef: `${wsId}/${objId}/${version}`,
workspaceId: wsId,
newName: this.state.newName,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export default class DeleteNarrative extends Component<

renderConfirmation() {
return (
<>
<div style={{ textAlign: 'center' }}>
<div className="pb2">
<p>
Deleting a Narrative will permanently remove it and all its data.
Expand All @@ -210,7 +210,7 @@ export default class DeleteNarrative extends Component<
Cancel
</DashboardButton>
</div>
</>
</div>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,28 +202,32 @@ class RevertNarrative extends Component<ControlMenuItemProps, ComponentState> {

renderConfirmation() {
return (
<>
<div style={{ textAlign: 'center' }}>
<div className="pb2">
<p>
Reverting a narrative to a previous version is permanent and may
cause data loss.
Reverting a narrative will create a new version identical to v
{this.props.narrative.version}.
</p>
<p>
This new narrative can be reverted to an earlier version at any
time.
</p>
<p style={{ fontWeight: 'bold' }}>This action cannot be undone!</p>
</div>
<div className="pb2">Continue?</div>
<div className="pb2" style={{ fontWeight: 'bold' }}>
Do you wish to continue?
</div>
<div>
<DashboardButton
onClick={() => this.doRevert()}
bgcolor={'red'}
textcolor={'white'}
bgcolor={'lightblue'}
>
Revert
</DashboardButton>
<DashboardButton onClick={this.props.cancelFn}>
Cancel
</DashboardButton>
</div>
</>
</div>
);
}

Expand Down
11 changes: 7 additions & 4 deletions src/client/components/dashboard/NarrativeList/ItemList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export class ItemList extends Component<Props, State> {
<div className={css.inner}>
<div className="ma0 mb2 pa0 f5">
{item.narrative_title || 'Untitled'}
{category === 'own' && this.renderDropdown(upa, item.version)}
{category === 'own' &&
this.renderDropdown(upa, item.version, idx)}
</div>
<p className="ma0 pa0 f7">
Updated {timeago.format(item.timestamp)} by {item.creator}
Expand All @@ -101,19 +102,21 @@ export class ItemList extends Component<Props, State> {
);
};

renderDropdown(upa: string, version: number) {
renderDropdown(upa: string, version: number, idx: number) {
const { selected } = this.props;
const selectedNarr = selected.substring(0, selected.lastIndexOf('/'));
const selectedVersion = +selected.split('/')[2];
const narr = upa.substring(0, upa.lastIndexOf('/'));
if (narr !== selectedNarr) {

// only give dropdown to selected version or default highlighted version on first load
if (narr !== selectedNarr && !(selected === '0/0/0' && idx === 0)) {
return <></>;
}
return (
<VersionDropdown
upa={upa}
version={version}
selectedVersion={selectedVersion}
selectedVersion={selected === '0/0/0' ? version : selectedVersion}
category={this.props.category}
history={this.props.history}
/>
Expand Down
24 changes: 20 additions & 4 deletions src/client/components/dashboard/NarrativeList/NarrativeDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import ControlMenu from './ControlMenu/ControlMenu';
import DataView from './DataView';
import Preview from './Preview';
import { LoadingSpinner } from '../../generic/LoadingSpinner';
import { History } from 'history';
import { VersionDropdown } from './VersionDropdown';

function detailsHeaderItem(key: string, value: string | JSX.Element[]) {
return (
Expand Down Expand Up @@ -190,6 +192,7 @@ interface Props {
// takes precedence over active item if it was fetched from narrative service
previousVersion: Doc | null;
loading: boolean;
history: History;
}

interface State {
Expand Down Expand Up @@ -231,6 +234,20 @@ export class NarrativeDetails extends React.Component<Props, State> {
});
}

renderVersionDropdown() {
const { activeItem, previousVersion, category, history } = this.props;
const { access_group, obj_id, version } = activeItem;
return (
<VersionDropdown
upa={`${access_group}/${obj_id}/${version}`}
version={activeItem.version}
selectedVersion={previousVersion?.version ?? activeItem.version}
category={category}
history={history}
/>
);
}

render() {
const { activeItem, previousVersion, cache, updateSearch, view, category } =
this.props;
Expand Down Expand Up @@ -296,10 +313,9 @@ export class NarrativeDetails extends React.Component<Props, State> {
</span>
</a>
<span className="b f4 gray i ml2">
v{displayItem.version}
{previousVersion && (
<span className="b f4 gray i"> of {activeItem.version}</span>
)}
{category === 'own'
? this.renderVersionDropdown()
: `v${displayItem.version}`}
</span>
</div>
<div className="ml-auto">
Expand Down
104 changes: 78 additions & 26 deletions src/client/components/dashboard/NarrativeList/VersionDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,86 @@ import React, { Component, MouseEvent } from 'react';
import { keepParamsLinkTo } from '../utils';
import { History } from 'history';

// wrapper for dismissing dropdown on outside click
class OutsideClickListener extends Component<{ dismissToggle: () => void }> {
wrapperRef: HTMLDivElement | null = null;

constructor(props: any) {
super(props);
this.setWrapperRef = this.setWrapperRef.bind(this);
this.handleOutsideClick = this.handleOutsideClick.bind(this);
}

componentDidMount() {
document.addEventListener('mousedown', this.handleOutsideClick);
}

componentWillUnmount() {
document.removeEventListener('mousedown', this.handleOutsideClick);
}

handleOutsideClick(event: any) {
if (this.wrapperRef && !this.wrapperRef?.contains(event.target)) {
this.props.dismissToggle();
}
}

setWrapperRef(node: HTMLDivElement) {
this.wrapperRef = node;
}

render() {
return (
<div style={{ display: 'inline-block' }} ref={this.setWrapperRef}>
{this.props.children}
</div>
);
}
}
interface Props {
upa: string;
version: number;
selectedVersion: number;
category: string;
history: History;
}

interface State {
versionToggle: boolean;
selectedVersion: number;
}

export class VersionDropdown extends Component<Props, State> {
state = {
versionToggle: false,
// selectedVersion: this.props.version
selectedVersion: this.props.selectedVersion,
};

componentDidUpdate() {
// state should match again whenever component updates
if (this.state.selectedVersion !== this.props.selectedVersion) {
this.setState({ selectedVersion: this.props.selectedVersion });
}
}

setVersionToggle(event: MouseEvent) {
event.preventDefault();
this.setState((prevState) => ({
versionToggle: !prevState.versionToggle,
}));
}

dismissToggle() {
this.setState({ versionToggle: false });
}

handleSelectVersion(e: MouseEvent, upa: string, version: number) {
const { history } = this.props;
const keepParams = (link: string) =>
keepParamsLinkTo(['limit', 'search', 'sort', 'view'], link);
history.push(keepParams(upa)(history.location));
this.setState({
versionToggle: false,
selectedVersion: version,
});
history.push(keepParams(upa)(history.location));
// prevent parent link from redirecting back to current version URL
e.preventDefault();
}
Expand All @@ -58,8 +102,8 @@ export class VersionDropdown extends Component<Props, State> {
return (
<span
key={version}
className="db pa2 pointer hover-bg-blue hover-white"
// onClick={(e) => this.handleSelectVersion(e, version)}
className="db pa2 pointer hover-bg-blue hover-white f5 fs-normal"
style={{ color: 'black', fontWeight: 'normal' }}
onClick={(e) =>
this.handleSelectVersion(e, `${prefix}${newUpa}`, version)
}
Expand All @@ -71,31 +115,39 @@ export class VersionDropdown extends Component<Props, State> {

render() {
const { version } = this.props;
// prevent scrollbar when all elements can fit in dropdown
const overflowStyle = version > 6 ? 'scroll' : 'hidden';
const versions = Array(version)
.fill(null)
.map((_, n) => n + 1)
.reverse();
return (
<div className="relative ml2" style={{ display: 'inline-block' }}>
<span onClick={(e) => this.setVersionToggle(e)}>
<span className="f5 gray i"> v{this.state.selectedVersion}</span>
<i className="fa fa-caret-down ml1 gray"></i>
</span>
{this.state.versionToggle && (
<div
className="ba b--black-30 bg-white db fr absolute"
style={{
boxShadow: '0 2px 3px #aaa',
zIndex: 1,
width: '8rem',
maxHeight: '200px',
overflowY: 'scroll',
}}
>
{versions.map((ver) => this.itemView(ver))}
</div>
)}
</div>
<OutsideClickListener dismissToggle={this.dismissToggle.bind(this)}>
<div className="relative ml2" style={{ display: 'inline-block' }}>
<span onClick={(e) => this.setVersionToggle(e)}>
<span className="gray i">
{' '}
v{this.state.selectedVersion}
{this.state.selectedVersion < version && ` of ${version}`}
</span>
<i className="fa fa-caret-down ml1 gray"></i>
</span>
{this.state.versionToggle && (
<div
className="ba b--black-30 bg-white db fr absolute"
style={{
boxShadow: '0 2px 3px #aaa',
zIndex: 1,
width: '8rem',
maxHeight: '200px',
overflowY: overflowStyle,
}}
>
{versions.map((ver) => this.itemView(ver))}
</div>
)}
</div>
</OutsideClickListener>
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import { enableFetchMocks } from 'jest-fetch-mock';
import { NarrativeDetails } from '../NarrativeDetails';
import { createBrowserHistory } from 'history';

enableFetchMocks();

Expand Down Expand Up @@ -62,6 +63,7 @@ describe('NarrativeDetails tests', () => {
previousVersion={null}
category={''}
loading={false}
history={createBrowserHistory()}
/>
);
expect(wrapper).toBeTruthy();
Expand Down
1 change: 1 addition & 0 deletions src/client/components/dashboard/NarrativeList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export class NarrativeList extends Component<Props, State> {
previousVersion={this.state.oldVersionDoc}
category={category}
loading={this.state.oldVersionLoading}
history={this.props.history}
/>
) : (
<></>
Expand Down

0 comments on commit b87e39c

Please sign in to comment.