Unverified Commit d8ef38ef authored by Lyza Gardner's avatar Lyza Gardner Committed by GitHub

Merge pull request #1780 from hypothesis/annotation-omega-replies

AnnotationOmega: Add very basic expand/collapse for reply threads
parents a4d44f01 086d317b
......@@ -3,7 +3,12 @@ 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 {
isHighlight,
isNew,
isReply,
quote,
} from '../util/annotation-metadata';
import { isShared } from '../util/permissions';
import { withServices } from '../util/service-context';
......@@ -13,6 +18,7 @@ import AnnotationHeader from './annotation-header';
import AnnotationLicense from './annotation-license';
import AnnotationPublishControl from './annotation-publish-control';
import AnnotationQuote from './annotation-quote';
import Button from './button';
import TagEditor from './tag-editor';
import TagList from './tag-list';
......@@ -26,8 +32,10 @@ function AnnotationOmega({
onReplyCountClick,
replyCount,
showDocumentInfo,
threadIsCollapsed,
}) {
const createDraft = useStore(store => store.createDraft);
const setCollapsed = useStore(store => store.setCollapsed);
// An annotation will have a draft if it is being edited
const draft = useStore(store => store.getDraft(annotation));
......@@ -43,6 +51,9 @@ function AnnotationOmega({
const [isSaving, setIsSaving] = useState(false);
const isEditing = !!draft && !isSaving;
const toggleAction = threadIsCollapsed ? 'Show replies' : 'Hide replies';
const toggleText = `${toggleAction} (${replyCount})`;
useEffect(() => {
// TEMPORARY. Create a new draft for new (non-highlight) annotations
// to put the component in "edit mode."
......@@ -57,6 +68,7 @@ function AnnotationOmega({
const shouldShowActions = !isEditing && !isNew(annotation);
const shouldShowLicense = isEditing && !isPrivate && group.type !== 'private';
const shouldShowReplyToggle = replyCount > 0 && !isReply(annotation);
const onEditTags = ({ tags }) => {
createDraft(annotation, { ...draft, tags });
......@@ -77,6 +89,8 @@ function AnnotationOmega({
}
};
const onToggleReplies = () => setCollapsed(annotation.id, !threadIsCollapsed);
// TODO
const fakeOnReply = () => alert('Reply: TBD');
......@@ -109,14 +123,23 @@ function AnnotationOmega({
)}
</div>
{shouldShowLicense && <AnnotationLicense />}
{shouldShowActions && (
<div className="annotation-actions">
<AnnotationActionBar
annotation={annotation}
onReply={fakeOnReply}
<div className="annotation-omega__controls">
{shouldShowReplyToggle && (
<Button
className="annotation-omega__reply-toggle"
onClick={onToggleReplies}
buttonText={toggleText}
/>
</div>
)}
)}
{shouldShowActions && (
<div className="annotation-omega__actions">
<AnnotationActionBar
annotation={annotation}
onReply={fakeOnReply}
/>
</div>
)}
</div>
</footer>
</div>
);
......@@ -131,6 +154,8 @@ AnnotationOmega.propTypes = {
replyCount: propTypes.number.isRequired,
/** Should extended document info be rendered (e.g. in non-sidebar contexts)? */
showDocumentInfo: propTypes.bool.isRequired,
/** Is the thread to which this annotation belongs currently collapsed? */
threadIsCollapsed: propTypes.bool.isRequired,
/* Injected services */
annotationsService: propTypes.object.isRequired,
......
......@@ -42,6 +42,7 @@ describe('AnnotationOmega', () => {
onReplyCountClick={fakeOnReplyCountClick}
replyCount={0}
showDocumentInfo={false}
threadIsCollapsed={true}
{...props}
/>
);
......@@ -60,6 +61,7 @@ describe('AnnotationOmega', () => {
fakeMetadata = {
isNew: sinon.stub(),
isReply: sinon.stub(),
quote: sinon.stub(),
};
......@@ -73,6 +75,7 @@ describe('AnnotationOmega', () => {
getGroup: sinon.stub().returns({
type: 'private',
}),
setCollapsed: sinon.stub(),
};
$imports.$mock(mockImportedComponents());
......@@ -268,6 +271,66 @@ describe('AnnotationOmega', () => {
});
});
describe('reply thread toggle button', () => {
const findRepliesButton = wrapper =>
wrapper.find('Button').filter('.annotation-omega__reply-toggle');
it('should render a toggle button if the annotation has replies', () => {
fakeMetadata.isReply.returns(false);
const wrapper = createComponent({
replyCount: 5,
threadIsCollapsed: true,
});
assert.isTrue(findRepliesButton(wrapper).exists());
assert.equal(
findRepliesButton(wrapper).props().buttonText,
'Show replies (5)'
);
});
it('should not render a toggle button if the annotation has no replies', () => {
fakeMetadata.isReply.returns(false);
const wrapper = createComponent({
replyCount: 0,
threadIsCollapsed: true,
});
assert.isFalse(findRepliesButton(wrapper).exists());
});
it('should not render a toggle button if the annotation itself is a reply', () => {
fakeMetadata.isReply.returns(true);
const wrapper = createComponent({
replyCount: 5,
threadIsCollapsed: true,
});
assert.isFalse(findRepliesButton(wrapper).exists());
});
it('should toggle the collapsed state of the thread on click', () => {
fakeMetadata.isReply.returns(false);
const wrapper = createComponent({
replyCount: 5,
threadIsCollapsed: true,
});
act(() => {
findRepliesButton(wrapper)
.props()
.onClick();
});
wrapper.setProps({ threadIsCollapsed: false });
assert.calledOnce(fakeStore.setCollapsed);
assert.equal(
findRepliesButton(wrapper).props().buttonText,
'Hide replies (5)'
);
});
});
describe('annotation actions', () => {
it('should show annotation actions', () => {
const wrapper = createComponent();
......
......@@ -15,11 +15,12 @@
ng-if="vm.thread.annotation">
</moderation-banner>
<div ng-if="vm.shouldShowAnnotationOmega()"><em>Viewing AnnotationOmega</em></div>
<annotation-omega ng-if="vm.shouldShowAnnotationOmega()"
<annotation-omega ng-if="vm.shouldShowAnnotationOmega() && vm.thread.annotation"
annotation="vm.thread.annotation"
reply-count="vm.thread.replyCount"
on-reply-count-click="vm.toggleCollapsed()"
show-document-info="vm.showDocumentInfo">
show-document-info="vm.showDocumentInfo"
thread-is-collapsed="vm.thread.collapsed">
</annotation-omega>
<annotation ng-class="vm.annotationClasses()"
annotation="vm.thread.annotation"
......
.annotation-omega {
&__reply-toggle.button {
background-color: transparent;
padding: 0;
font-weight: 400;
&:hover {
background-color: transparent;
text-decoration: underline;
}
}
&__controls {
display: flex;
}
&__actions {
margin-left: auto;
}
}
......@@ -30,6 +30,7 @@
@use './components/annotation-document-info';
@use './components/annotation-header';
@use './components/annotation-license';
@use './components/annotation-omega';
@use './components/annotation-publish-control';
@use './components/annotation-quote';
@use './components/annotation-share-control';
......
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