Skip to content

Commit

Permalink
UI toc generation components, WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
daneryl committed Feb 12, 2021
1 parent bc0d5a4 commit cb1146f
Show file tree
Hide file tree
Showing 15 changed files with 267 additions and 14 deletions.
5 changes: 2 additions & 3 deletions app/api/files/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ export const files = {
return toDeleteFiles;
},

async tocReviewed(_id: string) {
async tocReviewed(_id: string, language) {
const savedFile = await files.save({ _id, generatedToc: false });
const sameEntityFiles = await files.get({ entity: savedFile.entity }, { generatedToc: 1 });
const [entity] = await entities.get({
sharedId: savedFile.entity,
language: savedFile.language,
});

await entities.save(
Expand All @@ -50,7 +49,7 @@ export const files = {
false
),
},
{ user: {}, language: savedFile.language }
{ user: {}, language }
);

return savedFile;
Expand Down
2 changes: 1 addition & 1 deletion app/api/files/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export default (app: Application) => {
}),
async (req, res, next) => {
try {
res.json(await files.tocReviewed(req.body.fileId));
res.json(await files.tocReviewed(req.body.fileId, req.language));
} catch (e) {
next(e);
}
Expand Down
2 changes: 0 additions & 2 deletions app/api/files/specs/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const fixtures: DBFixture = {
{
_id: uploadId,
entity: 'sharedId1',
language: 'es',
generatedToc: true,
originalname: 'upload1',
filename: fileName1,
Expand All @@ -22,7 +21,6 @@ const fixtures: DBFixture = {
{
_id: uploadId2,
generatedToc: true,
language: 'es',
entity: 'sharedId1',
filename: 'fileNotInDisk',
},
Expand Down
7 changes: 6 additions & 1 deletion app/react/App/scss/modules/_toc.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
.toc {
padding: 15px;
padding: 0px 15px 15px 15px;
}

div.tocHeader {
padding-left: 15px;
border-bottom: 1px solid #f4f4f4;
}

.toc-view{
Expand Down
11 changes: 9 additions & 2 deletions app/react/Attachments/components/File.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { APIURL } from 'app/config.js';
import { LocalForm, Control } from 'react-redux-form';
import { updateFile, deleteFile } from 'app/Attachments/actions/actions';
import { wrapDispatch } from 'app/Multireducer';
import { TocGeneratedLabel } from 'app/ToggledFeatures/tocGeneration/TocGeneratedLabel';
import { NeedAuthorization } from 'app/Auth';
import { EntitySchema } from 'shared/types/entityType';
import { ViewDocumentLink } from './ViewDocumentLink';
Expand Down Expand Up @@ -102,7 +103,10 @@ export class File extends Component<FileProps, FileState> {
<div>
<div className="file-language">
<Translate>{language ? transformLanguage(language) || '' : ''}</Translate>
</div>{' '}
</div>
<TocGeneratedLabel file={this.props.file}>
<Translate>ML TOC</Translate>
</TocGeneratedLabel>
<a
href={`${APIURL}files/${filename}`}
target="_blank"
Expand Down Expand Up @@ -206,4 +210,7 @@ export class File extends Component<FileProps, FileState> {
const mapDispatchToProps = (dispatch: Dispatch<{}>, props: FileProps) =>
bindActionCreators({ updateFile, deleteFile }, wrapDispatch(dispatch, props.storeKey));

export const ConnectedFile = connect(null, mapDispatchToProps)(File);
export const ConnectedFile = connect(
null,
mapDispatchToProps
)(File);
14 changes: 13 additions & 1 deletion app/react/Attachments/components/specs/File.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { LocalForm } from 'react-redux-form';
import { FileType } from 'shared/types/fileType';
import { File, FileProps } from '../File';
import { Translate } from 'app/I18N';
import { File, FileProps } from '../File';

describe('file', () => {
let component: ShallowWrapper<File>;
Expand Down Expand Up @@ -32,6 +32,7 @@ describe('file', () => {
});

const render = () => {
//eslint-disable-next-line react/jsx-props-no-spreading
component = shallow(<File {...props} />, { context });
};

Expand All @@ -47,6 +48,17 @@ describe('file', () => {
expect(language).toBe('english');
});

it('should render ML TOC label when file has generatedToc = true', () => {
render();
let label = component.find('.file-label-generatedToc');
expect(label.length).toBe(0);

props.file.generatedToc = true;
render();
label = component.find('.file-label-generatedToc');
expect(label.length).toBe(1);
});

describe('editing the file', () => {
it('should render a form with the file', () => {
render();
Expand Down
16 changes: 15 additions & 1 deletion app/react/Documents/components/DocumentSidePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import ShowIf from 'app/App/ShowIf';
import SidePanel from 'app/Layout/SidePanel';
import DocumentSemanticSearchResults from 'app/SemanticSearch/components/DocumentResults';
import { CopyFromEntity } from 'app/Metadata/components/CopyFromEntity';
import { FeatureToggle } from 'app/components/Elements/FeatureToggle';
import { ReviewTocButton } from 'app/ToggledFeatures/tocGeneration/ReviewTocButton';
import { TocGeneratedLabel } from 'app/ToggledFeatures/tocGeneration/TocGeneratedLabel';
import { Icon } from 'UI';

import * as viewerModule from 'app/Viewer';
Expand Down Expand Up @@ -321,11 +324,14 @@ export class DocumentSidePanel extends Component {
<div className="sidepanel-footer">
<button
onClick={() => this.props.editToc(this.props.file.toc || [])}
className="edit-toc btn btn-success"
className="edit-toc btn btn-info"
>
<Icon icon="pencil-alt" />
<span className="btn-label">Edit</span>
</button>
<ReviewTocButton file={this.props.file}>
<Translate>Mark as Reviewed</Translate>
</ReviewTocButton>
</div>
</ShowIf>
</NeedAuthorization>
Expand All @@ -340,6 +346,14 @@ export class DocumentSidePanel extends Component {
/>
</TabContent>
<TabContent for="toc">
<div className="tocHeader">
<h1>
<Translate>Table of contents</Translate>
</h1>
<TocGeneratedLabel file={this.props.file}>
<Translate>auto-created ⓘ </Translate>
</TocGeneratedLabel>
</div>
<ShowIf if={!this.props.tocBeingEdited}>
<ShowToc
toc={defaultDocumentToC}
Expand Down
7 changes: 5 additions & 2 deletions app/react/Documents/components/ShowToc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { connect } from 'react-redux';
import { scrollTo } from 'app/Viewer/actions/uiActions';
import Immutable from 'immutable';
import ShowIf from 'app/App/ShowIf';
import { t } from 'app/I18N';
import { t, Translate} from 'app/I18N';
import { Icon } from 'UI';

export class ShowToc extends Component {
Expand Down Expand Up @@ -66,4 +66,7 @@ function mapDispatchToProps() {
return { scrollTo };
}

export default connect(null, mapDispatchToProps)(ShowToc);
export default connect(
null,
mapDispatchToProps
)(ShowToc);
34 changes: 34 additions & 0 deletions app/react/ToggledFeatures/tocGeneration/ReviewTocButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import { Icon } from 'UI';
import { FeatureToggle } from 'app/components/Elements/FeatureToggle';
import { connect, ConnectedProps } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ClientFile } from 'app/istore';
import { tocGenerationActions } from './actions';

export interface ReviewTocButtonProps {
file: ClientFile;
children: JSX.Element | string;
}

const mapDispatchToProps = (dispatch: Dispatch<{}>) =>
bindActionCreators({ onClick: tocGenerationActions.reviewToc }, dispatch);

const connector = connect(null, mapDispatchToProps);

type MappedProps = ConnectedProps<typeof connector>;
type ComponentProps = ReviewTocButtonProps & MappedProps;

const ReviewTocButton = ({ file, onClick, children }: ComponentProps) => (
<FeatureToggle feature="tocGeneration">
{file.generatedToc && (
<button type="button" onClick={() => onClick(file._id)} className="edit-toc btn btn-success">
<Icon icon="pencil-alt" />
<span className="btn-label">{children}</span>
</button>
)}
</FeatureToggle>
);

const container = connector(ReviewTocButton);
export { container as ReviewTocButton };
14 changes: 14 additions & 0 deletions app/react/ToggledFeatures/tocGeneration/TocGeneratedLabel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { FeatureToggle } from 'app/components/Elements/FeatureToggle';
import { FileType } from 'shared/types/fileType';

export interface TocGeneratedLabelProps {
file: FileType;
children: JSX.Element | string;
}

export const TocGeneratedLabel = ({ file, children }: TocGeneratedLabelProps) => (
<FeatureToggle feature="tocGeneration">
{file.generatedToc && <span className="label-generatedToc">{children}</span>}
</FeatureToggle>
);
34 changes: 34 additions & 0 deletions app/react/ToggledFeatures/tocGeneration/actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { actions } from 'app/BasicReducer/reducer';
import { actions as formActions } from 'react-redux-form';
import { RequestParams } from 'app/utils/RequestParams';
import api from 'app/utils/api';
import { notificationActions } from 'app/Notifications';

const tocGenerationActions = {
reviewToc(fileId: string) {
return async (dispatch, getState) => {
const currentDoc = getState().documentViewer.doc.toJS();
dispatch(formActions.reset('documentViewer.sidepanel.metadata'));

const updatedFile = (await api.post('files/tocReviewed', new RequestParams({ fileId }))).json;
//WTF
updatedFile.pdfInfo = {}
const doc = {
...currentDoc,
defaultDoc: updatedFile,
documents: currentDoc.documents.map(d => {
if (d._id === updatedFile._id) {
return updatedFile;
}
return d;
}),
};

dispatch(notificationActions.notify('Document updated', 'success'));
dispatch(formActions.reset('documentViewer.sidepanel.metadata'));
dispatch(actions.set('viewer/doc', doc));
};
},
};

export { tocGenerationActions };
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import configureStore, { MockStore, MockStoreCreator } from 'redux-mock-store';
import { Provider } from 'react-redux';
import { FileType } from 'shared/types/fileType';
import { ReviewTocButton } from '../ReviewTocButton';

describe('ReviewTocButton', () => {
let component: ShallowWrapper<typeof ReviewTocButton>;

const mockStoreCreator: MockStoreCreator<object> = configureStore<object>([]);
const render = (file: FileType) => {
const store: MockStore<object> = mockStoreCreator({});
component = shallow(
<Provider store={store}>
<ReviewTocButton file={file}>
<span>test</span>
</ReviewTocButton>
</Provider>
)
.dive()
.dive();
};

it('should render nothing if file generatedToc is false', () => {
render({ generatedToc: false });
expect(component.find('button').length).toEqual(0);

render({});
expect(component.find('button').length).toEqual(0);
});

it('should render when generatedToc is true', () => {
render({ generatedToc: true });
expect(component.find('button').length).toEqual(1);
});
});
91 changes: 91 additions & 0 deletions app/react/ToggledFeatures/tocGeneration/specs/actions.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import api from 'app/utils/api';
import backend from 'fetch-mock';
import * as notificationsTypes from 'app/Notifications/actions/actionTypes';
import { actions as relationshipActions } from 'app/Relationships';
import { APIURL } from 'app/config';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import Immutable from 'immutable';
import { mockID } from 'shared/uniqueID.js';
import { tocGenerationActions } from '../actions';

const middlewares = [thunk];
const mockStore = configureMockStore(middlewares);

describe('reviewToc', () => {
it('should store the document with the response of reviewToc', done => {
mockID();
const fileId = 'fileId';

backend.post(`${APIURL}files/tocReviewed`, {
body: JSON.stringify({ _id: fileId, generatedToc: false }),
});

spyOn(relationshipActions, 'reloadRelationships').and.returnValue({
type: 'reloadRelationships',
});

const doc = {
name: 'doc',
_id: 'id',
sharedId: 'sharedId',
defaultDoc: {
_id: fileId,
generatedToc: true,
},
documents: [
{
_id: fileId,
generatedToc: true,
},
],
};

const updatedEntity = {
name: 'doc',
_id: 'id',
sharedId: 'sharedId',
defaultDoc: {
_id: fileId,
generatedToc: false,
},
documents: [
{
_id: fileId,
generatedToc: false,
},
],
};

const expectedActions = [
{ type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata' },
{
type: notificationsTypes.NOTIFY,
notification: { message: 'Document updated', type: 'success', id: 'unique_id' },
},
{ type: 'rrf/reset', model: 'documentViewer.sidepanel.metadata' },
{ type: 'viewer/doc/SET', value: updatedEntity },
];

const store = mockStore({
documentViewer: {
doc: Immutable.fromJS(doc),
},
});

spyOn(api, 'post').and.callThrough();
store
.dispatch(tocGenerationActions.reviewToc(fileId))
.then(() => {
expect(api.post).toHaveBeenCalledWith('files/tocReviewed', {
data: {
fileId: 'fileId',
},
headers: {},
});
expect(store.getActions()).toEqual(expectedActions);
})
.then(done)
.catch(done.fail);
});
});
Loading

0 comments on commit cb1146f

Please sign in to comment.