Commit 33ff0af2 authored by Lyza Danger Gardner's avatar Lyza Danger Gardner

Add save-annotation capability to `AnnotationOmega`

parent fd162d2e
import { createElement } from 'preact';
import { useEffect } from 'preact/hooks';
import { useEffect, useState } from 'preact/hooks';
import propTypes from 'prop-types';
import useStore from '../store/use-store';
import { isHighlight, isNew, quote } from '../util/annotation-metadata';
import { isShared } from '../util/permissions';
import { withServices } from '../util/service-context';
import AnnotationActionBar from './annotation-action-bar';
import AnnotationBody from './annotation-body';
......@@ -20,6 +21,8 @@ import TagList from './tag-list';
*/
function AnnotationOmega({
annotation,
annotationsService,
flash,
onReplyCountClick,
replyCount,
showDocumentInfo,
......@@ -30,26 +33,27 @@ function AnnotationOmega({
const draft = useStore(store => store.getDraft(annotation));
const group = useStore(store => store.getGroup(annotation.group));
const isPrivate = draft ? draft.isPrivate : !isShared(annotation.permissions);
const tags = draft ? draft.tags : annotation.tags;
const text = draft ? draft.text : annotation.text;
const hasQuote = !!quote(annotation);
const isEmpty = !text && !tags.length;
const [isSaving, setIsSaving] = useState(false);
const isEditing = !!draft && !isSaving;
useEffect(() => {
// TEMPORARY. Create a new draft for new (non-highlight) annotations
// to put the component in "edit mode."
if (!draft && isNew(annotation) && !isHighlight(annotation)) {
if (!isSaving && !draft && isNew(annotation) && !isHighlight(annotation)) {
createDraft(annotation, {
tags: annotation.tags,
text: annotation.text,
isPrivate: !isShared(annotation.permissions),
});
}
}, [annotation, draft, createDraft]);
const isPrivate = draft ? draft.isPrivate : !isShared(annotation.permissions);
const tags = draft ? draft.tags : annotation.tags;
const text = draft ? draft.text : annotation.text;
const hasQuote = !!quote(annotation);
const isEmpty = !text && !tags.length;
const isSaving = false;
const isEditing = !!draft && !isSaving;
}, [annotation, draft, createDraft, isSaving]);
const shouldShowActions = !isEditing && !isNew(annotation);
const shouldShowLicense = isEditing && !isPrivate && group.type !== 'private';
......@@ -62,9 +66,19 @@ function AnnotationOmega({
createDraft(annotation, { ...draft, text });
};
const onSave = async () => {
setIsSaving(true);
try {
await annotationsService.save(annotation);
} catch (err) {
flash.error(err.message, 'Saving annotation failed');
} finally {
setIsSaving(false);
}
};
// TODO
const fakeOnReply = () => alert('Reply: TBD');
const fakeOnSave = () => alert('Save changes: TBD');
return (
<div className="annotation-omega">
......@@ -90,7 +104,7 @@ function AnnotationOmega({
<AnnotationPublishControl
annotation={annotation}
isDisabled={isEmpty}
onSave={fakeOnSave}
onSave={onSave}
/>
)}
</div>
......@@ -117,6 +131,12 @@ AnnotationOmega.propTypes = {
replyCount: propTypes.number.isRequired,
/** Should extended document info be rendered (e.g. in non-sidebar contexts)? */
showDocumentInfo: propTypes.bool.isRequired,
/* Injected services */
annotationsService: propTypes.object.isRequired,
flash: propTypes.object.isRequired,
};
export default AnnotationOmega;
AnnotationOmega.injectedProps = ['annotationsService', 'flash'];
export default withServices(AnnotationOmega);
......@@ -5,6 +5,7 @@ import { act } from 'preact/test-utils';
import * as fixtures from '../../test/annotation-fixtures';
import mockImportedComponents from '../../../test-util/mock-imported-components';
import { waitFor } from '../../../test-util/wait';
// @TODO Note this import as `Annotation` for easier updating later
......@@ -17,6 +18,10 @@ describe('AnnotationOmega', () => {
// Dependency Mocks
let fakeMetadata;
let fakePermissions;
// Injected dependency mocks
let fakeAnnotationsService;
let fakeFlash;
let fakeStore;
const setEditingMode = (isEditing = true) => {
......@@ -32,6 +37,8 @@ describe('AnnotationOmega', () => {
return mount(
<Annotation
annotation={fixtures.defaultAnnotation()}
annotationsService={fakeAnnotationsService}
flash={fakeFlash}
onReplyCountClick={fakeOnReplyCountClick}
replyCount={0}
showDocumentInfo={false}
......@@ -43,6 +50,14 @@ describe('AnnotationOmega', () => {
beforeEach(() => {
fakeOnReplyCountClick = sinon.stub();
fakeAnnotationsService = {
save: sinon.stub().resolves(),
};
fakeFlash = {
error: sinon.stub(),
};
fakeMetadata = {
isNew: sinon.stub(),
quote: sinon.stub(),
......@@ -72,6 +87,8 @@ describe('AnnotationOmega', () => {
$imports.$restore();
});
it('should test `isSaving`');
describe('annotation quote', () => {
it('renders quote if annotation has a quote', () => {
fakeMetadata.quote.returns('quote');
......@@ -179,6 +196,36 @@ describe('AnnotationOmega', () => {
wrapper.find('AnnotationPublishControl').props().isDisabled
);
});
context('saving an annotation', () => {
it('should save the annotation when the publish control invokes the `onSave` callback', () => {
setEditingMode(true);
const wrapper = createComponent();
wrapper
.find('AnnotationPublishControl')
.props()
.onSave();
assert.calledWith(
fakeAnnotationsService.save,
wrapper.props().annotation
);
});
it('should flash an error message on failure', async () => {
setEditingMode(true);
fakeAnnotationsService.save.rejects();
const wrapper = createComponent();
wrapper
.find('AnnotationPublishControl')
.props()
.onSave();
await waitFor(() => fakeFlash.error.called);
});
});
});
describe('license information', () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment