Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
d77ca41f
Unverified
Commit
d77ca41f
authored
Jan 23, 2020
by
Lyza Gardner
Committed by
GitHub
Jan 23, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1708 from hypothesis/annotation-omega-footer
Add some initial footer components to `AnnotationOmega`
parents
052d6e86
f319e86f
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
269 additions
and
19 deletions
+269
-19
annotation-omega.js
src/sidebar/components/annotation-omega.js
+49
-1
annotation-omega-test.js
src/sidebar/components/test/annotation-omega-test.js
+208
-16
frame-sync-test.js
src/sidebar/services/test/frame-sync-test.js
+4
-1
annotation-fixtures.js
src/sidebar/test/annotation-fixtures.js
+5
-0
annotation-metadata-test.js
src/sidebar/util/test/annotation-metadata-test.js
+3
-1
No files found.
src/sidebar/components/annotation-omega.js
View file @
d77ca41f
...
@@ -2,10 +2,14 @@ import { createElement } from 'preact';
...
@@ -2,10 +2,14 @@ import { createElement } from 'preact';
import
propTypes
from
'prop-types'
;
import
propTypes
from
'prop-types'
;
import
useStore
from
'../store/use-store'
;
import
useStore
from
'../store/use-store'
;
import
{
quote
}
from
'../util/annotation-metadata'
;
import
{
isNew
,
isReply
,
quote
}
from
'../util/annotation-metadata'
;
import
{
isShared
}
from
'../util/permissions'
;
import
AnnotationActionBar
from
'./annotation-action-bar'
;
import
AnnotationBody
from
'./annotation-body'
;
import
AnnotationBody
from
'./annotation-body'
;
import
AnnotationHeader
from
'./annotation-header'
;
import
AnnotationHeader
from
'./annotation-header'
;
import
AnnotationLicense
from
'./annotation-license'
;
import
AnnotationPublishControl
from
'./annotation-publish-control'
;
import
AnnotationQuote
from
'./annotation-quote'
;
import
AnnotationQuote
from
'./annotation-quote'
;
import
TagEditor
from
'./tag-editor'
;
import
TagEditor
from
'./tag-editor'
;
import
TagList
from
'./tag-list'
;
import
TagList
from
'./tag-list'
;
...
@@ -20,16 +24,24 @@ function AnnotationOmega({
...
@@ -20,16 +24,24 @@ function AnnotationOmega({
showDocumentInfo
,
showDocumentInfo
,
})
{
})
{
const
createDraft
=
useStore
(
store
=>
store
.
createDraft
);
const
createDraft
=
useStore
(
store
=>
store
.
createDraft
);
const
setDefault
=
useStore
(
store
=>
store
.
setDefault
);
// An annotation will have a draft if it is being edited
// An annotation will have a draft if it is being edited
const
draft
=
useStore
(
store
=>
store
.
getDraft
(
annotation
));
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
tags
=
draft
?
draft
.
tags
:
annotation
.
tags
;
const
text
=
draft
?
draft
.
text
:
annotation
.
text
;
const
text
=
draft
?
draft
.
text
:
annotation
.
text
;
const
hasQuote
=
!!
quote
(
annotation
);
const
hasQuote
=
!!
quote
(
annotation
);
const
isEmpty
=
!
text
&&
!
tags
.
length
;
const
isSaving
=
false
;
const
isSaving
=
false
;
const
isEditing
=
!!
draft
&&
!
isSaving
;
const
isEditing
=
!!
draft
&&
!
isSaving
;
const
shouldShowActions
=
!
isEditing
&&
!
isNew
(
annotation
);
const
shouldShowLicense
=
isEditing
&&
!
isPrivate
&&
group
.
type
!==
'private'
;
const
onEditTags
=
({
tags
})
=>
{
const
onEditTags
=
({
tags
})
=>
{
createDraft
(
annotation
,
{
...
draft
,
tags
});
createDraft
(
annotation
,
{
...
draft
,
tags
});
};
};
...
@@ -38,6 +50,20 @@ function AnnotationOmega({
...
@@ -38,6 +50,20 @@ function AnnotationOmega({
createDraft
(
annotation
,
{
...
draft
,
text
});
createDraft
(
annotation
,
{
...
draft
,
text
});
};
};
const
onSetPrivacy
=
({
level
})
=>
{
createDraft
(
annotation
,
{
...
draft
,
isPrivate
:
level
===
'private'
});
// Persist this as privacy default for future annotations unless this is a reply
if
(
!
isReply
(
annotation
))
{
setDefault
(
'annotationPrivacy'
,
level
);
}
};
// TODO
const
fakeOnEdit
=
()
=>
alert
(
'Enter edit mode: TBD'
);
const
fakeOnReply
=
()
=>
alert
(
'Reply: TBD'
);
const
fakeOnRevert
=
()
=>
alert
(
'Revert changes: TBD'
);
const
fakeOnSave
=
()
=>
alert
(
'Save changes: TBD'
);
return
(
return
(
<
div
className
=
"annotation-omega"
>
<
div
className
=
"annotation-omega"
>
<
AnnotationHeader
<
AnnotationHeader
...
@@ -56,6 +82,28 @@ function AnnotationOmega({
...
@@ -56,6 +82,28 @@ function AnnotationOmega({
/
>
/
>
{
isEditing
&&
<
TagEditor
onEditTags
=
{
onEditTags
}
tagList
=
{
tags
}
/>
}
{
isEditing
&&
<
TagEditor
onEditTags
=
{
onEditTags
}
tagList
=
{
tags
}
/>
}
{
!
isEditing
&&
<
TagList
annotation
=
{
annotation
}
tags
=
{
tags
}
/>
}
{
!
isEditing
&&
<
TagList
annotation
=
{
annotation
}
tags
=
{
tags
}
/>
}
<
footer
className
=
"annotation-footer"
>
{
isEditing
&&
(
<
AnnotationPublishControl
group
=
{
group
}
isDisabled
=
{
isEmpty
}
isShared
=
{
!
isPrivate
}
onCancel
=
{
fakeOnRevert
}
onSave
=
{
fakeOnSave
}
onSetPrivacy
=
{
onSetPrivacy
}
/
>
)}
{
shouldShowLicense
&&
<
AnnotationLicense
/>
}
{
shouldShowActions
&&
(
<
div
className
=
"annotation-actions"
>
<
AnnotationActionBar
annotation
=
{
annotation
}
onEdit
=
{
fakeOnEdit
}
onReply
=
{
fakeOnReply
}
/
>
<
/div
>
)}
<
/footer
>
<
/div
>
<
/div
>
);
);
}
}
...
...
src/sidebar/components/test/annotation-omega-test.js
View file @
d77ca41f
...
@@ -15,9 +15,19 @@ describe('AnnotationOmega', () => {
...
@@ -15,9 +15,19 @@ describe('AnnotationOmega', () => {
let
fakeOnReplyCountClick
;
let
fakeOnReplyCountClick
;
// Dependency Mocks
// Dependency Mocks
let
fakeQuote
;
let
fakeMetadata
;
let
fakePermissions
;
let
fakeStore
;
let
fakeStore
;
const
setEditingMode
=
(
isEditing
=
true
)
=>
{
// The presence of a draft will make `isEditing` `true`
if
(
isEditing
)
{
fakeStore
.
getDraft
.
returns
(
fixtures
.
defaultDraft
());
}
else
{
fakeStore
.
getDraft
.
returns
(
null
);
}
};
const
createComponent
=
props
=>
{
const
createComponent
=
props
=>
{
return
mount
(
return
mount
(
<
Annotation
<
Annotation
...
@@ -33,17 +43,29 @@ describe('AnnotationOmega', () => {
...
@@ -33,17 +43,29 @@ describe('AnnotationOmega', () => {
beforeEach
(()
=>
{
beforeEach
(()
=>
{
fakeOnReplyCountClick
=
sinon
.
stub
();
fakeOnReplyCountClick
=
sinon
.
stub
();
fakeQuote
=
sinon
.
stub
();
fakeMetadata
=
{
isNew
:
sinon
.
stub
(),
isReply
:
sinon
.
stub
().
returns
(
false
),
quote
:
sinon
.
stub
(),
};
fakePermissions
=
{
isShared
:
sinon
.
stub
().
returns
(
true
),
};
fakeStore
=
{
fakeStore
=
{
createDraft
:
sinon
.
stub
(),
createDraft
:
sinon
.
stub
(),
getDraft
:
sinon
.
stub
().
returns
(
null
),
getDraft
:
sinon
.
stub
().
returns
(
null
),
getGroup
:
sinon
.
stub
().
returns
({
type
:
'private'
,
}),
setDefault
:
sinon
.
stub
(),
};
};
$imports
.
$mock
(
mockImportedComponents
());
$imports
.
$mock
(
mockImportedComponents
());
$imports
.
$mock
({
$imports
.
$mock
({
'../util/annotation-metadata'
:
{
'../util/annotation-metadata'
:
fakeMetadata
,
quote
:
fakeQuote
,
'../util/permissions'
:
fakePermissions
,
},
'../store/use-store'
:
callback
=>
callback
(
fakeStore
),
'../store/use-store'
:
callback
=>
callback
(
fakeStore
),
});
});
});
});
...
@@ -54,7 +76,7 @@ describe('AnnotationOmega', () => {
...
@@ -54,7 +76,7 @@ describe('AnnotationOmega', () => {
describe
(
'annotation quote'
,
()
=>
{
describe
(
'annotation quote'
,
()
=>
{
it
(
'renders quote if annotation has a quote'
,
()
=>
{
it
(
'renders quote if annotation has a quote'
,
()
=>
{
fake
Q
uote
.
returns
(
'quote'
);
fake
Metadata
.
q
uote
.
returns
(
'quote'
);
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
const
quote
=
wrapper
.
find
(
'AnnotationQuote'
);
const
quote
=
wrapper
.
find
(
'AnnotationQuote'
);
...
@@ -62,7 +84,7 @@ describe('AnnotationOmega', () => {
...
@@ -62,7 +84,7 @@ describe('AnnotationOmega', () => {
});
});
it
(
'does not render quote if annotation does not have a quote'
,
()
=>
{
it
(
'does not render quote if annotation does not have a quote'
,
()
=>
{
fake
Q
uote
.
returns
(
null
);
fake
Metadata
.
q
uote
.
returns
(
null
);
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
...
@@ -72,7 +94,7 @@ describe('AnnotationOmega', () => {
...
@@ -72,7 +94,7 @@ describe('AnnotationOmega', () => {
});
});
describe
(
'annotation body and excerpt'
,
()
=>
{
describe
(
'annotation body and excerpt'
,
()
=>
{
it
(
'updates annotation
state
when text edited'
,
()
=>
{
it
(
'updates annotation
draft
when text edited'
,
()
=>
{
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
const
body
=
wrapper
.
find
(
'AnnotationBody'
);
const
body
=
wrapper
.
find
(
'AnnotationBody'
);
...
@@ -80,23 +102,24 @@ describe('AnnotationOmega', () => {
...
@@ -80,23 +102,24 @@ describe('AnnotationOmega', () => {
body
.
props
().
onEditText
({
text
:
'updated text'
});
body
.
props
().
onEditText
({
text
:
'updated text'
});
});
});
const
call
=
fakeStore
.
createDraft
.
getCall
(
0
);
assert
.
calledOnce
(
fakeStore
.
createDraft
);
assert
.
calledOnce
(
fakeStore
.
createDraft
);
assert
.
equal
(
call
.
args
[
1
].
text
,
'updated text'
);
});
});
});
});
describe
(
'tags'
,
()
=>
{
describe
(
'tags'
,
()
=>
{
it
(
'renders tag editor if `isEditing'
,
()
=>
{
it
(
'renders tag editor if `isEditing'
,
()
=>
{
// The presence of a draft will make `isEditing` `true`
setEditingMode
(
true
);
fakeStore
.
getDraft
.
returns
(
fixtures
.
defaultDraft
());
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
assert
.
is
Ok
(
wrapper
.
find
(
'TagEditor'
).
exists
());
assert
.
is
True
(
wrapper
.
find
(
'TagEditor'
).
exists
());
assert
.
notOk
(
wrapper
.
find
(
'TagList'
).
exists
());
assert
.
isFalse
(
wrapper
.
find
(
'TagList'
).
exists
());
});
});
it
(
'updates annotation
state
if tags changed'
,
()
=>
{
it
(
'updates annotation
draft
if tags changed'
,
()
=>
{
fakeStore
.
getDraft
.
returns
(
fixtures
.
defaultDraft
()
);
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
wrapper
wrapper
...
@@ -104,14 +127,183 @@ describe('AnnotationOmega', () => {
...
@@ -104,14 +127,183 @@ describe('AnnotationOmega', () => {
.
props
()
.
props
()
.
onEditTags
({
tags
:
[
'uno'
,
'dos'
]
});
.
onEditTags
({
tags
:
[
'uno'
,
'dos'
]
});
const
call
=
fakeStore
.
createDraft
.
getCall
(
0
);
assert
.
calledOnce
(
fakeStore
.
createDraft
);
assert
.
calledOnce
(
fakeStore
.
createDraft
);
assert
.
sameMembers
(
call
.
args
[
1
].
tags
,
[
'uno'
,
'dos'
]);
});
});
it
(
'renders tag list if not `isEditing'
,
()
=>
{
it
(
'renders tag list if not `isEditing'
,
()
=>
{
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
assert
.
isOk
(
wrapper
.
find
(
'TagList'
).
exists
());
assert
.
isTrue
(
wrapper
.
find
(
'TagList'
).
exists
());
assert
.
notOk
(
wrapper
.
find
(
'TagEditor'
).
exists
());
assert
.
isFalse
(
wrapper
.
find
(
'TagEditor'
).
exists
());
});
});
describe
(
'publish control'
,
()
=>
{
it
(
'should show the publish control if in edit mode'
,
()
=>
{
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
assert
.
isTrue
(
wrapper
.
find
(
'AnnotationPublishControl'
).
exists
());
});
it
(
'should not show the publish control if not in edit mode'
,
()
=>
{
setEditingMode
(
false
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationPublishControl'
).
exists
());
});
it
(
'should set the publish control to disabled if annotation is empty'
,
()
=>
{
const
draft
=
fixtures
.
defaultDraft
();
draft
.
tags
=
[];
draft
.
text
=
''
;
fakeStore
.
getDraft
.
returns
(
draft
);
const
wrapper
=
createComponent
();
assert
.
isTrue
(
wrapper
.
find
(
'AnnotationPublishControl'
).
props
().
isDisabled
);
});
it
(
'should set `isShared` to `false` if annotation is private'
,
()
=>
{
const
draft
=
fixtures
.
defaultDraft
();
draft
.
isPrivate
=
true
;
fakeStore
.
getDraft
.
returns
(
draft
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationPublishControl'
).
props
().
isShared
);
});
it
(
'should set `isShared` to `true` if annotation is shared'
,
()
=>
{
const
draft
=
fixtures
.
defaultDraft
();
draft
.
isPrivate
=
false
;
fakeStore
.
getDraft
.
returns
(
draft
);
const
wrapper
=
createComponent
();
assert
.
isTrue
(
wrapper
.
find
(
'AnnotationPublishControl'
).
props
().
isShared
);
});
it
(
'should update annotation privacy when changed by publish control'
,
()
=>
{
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
act
(()
=>
{
wrapper
.
find
(
'AnnotationPublishControl'
)
.
props
()
.
onSetPrivacy
({
level
:
'private'
});
});
const
call
=
fakeStore
.
createDraft
.
getCall
(
0
);
assert
.
calledOnce
(
fakeStore
.
createDraft
);
assert
.
isTrue
(
call
.
args
[
1
].
isPrivate
);
});
it
(
'should update annotation privacy default on change'
,
()
=>
{
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
act
(()
=>
{
wrapper
.
find
(
'AnnotationPublishControl'
)
.
props
()
.
onSetPrivacy
({
level
:
'private'
});
});
assert
.
calledOnce
(
fakeStore
.
setDefault
);
assert
.
calledWith
(
fakeStore
.
setDefault
,
'annotationPrivacy'
,
'private'
);
});
it
(
'should not update annotation privacy default on change if annotation is reply'
,
()
=>
{
fakeMetadata
.
isReply
.
returns
(
true
);
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
act
(()
=>
{
wrapper
.
find
(
'AnnotationPublishControl'
)
.
props
()
.
onSetPrivacy
({
level
:
'private'
});
});
assert
.
equal
(
fakeStore
.
setDefault
.
callCount
,
0
);
});
});
describe
(
'license information'
,
()
=>
{
it
(
'should show license information when editing shared annotations in public groups'
,
()
=>
{
fakeStore
.
getGroup
.
returns
({
type
:
'open'
});
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
assert
.
isTrue
(
wrapper
.
find
(
'AnnotationLicense'
).
exists
());
});
it
(
'should not show license information when not editing'
,
()
=>
{
fakeStore
.
getGroup
.
returns
({
type
:
'open'
});
setEditingMode
(
false
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationLicense'
).
exists
());
});
it
(
'should not show license information for annotations in private groups'
,
()
=>
{
fakeStore
.
getGroup
.
returns
({
type
:
'private'
});
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationLicense'
).
exists
());
});
it
(
'should not show license information for private annotations'
,
()
=>
{
const
draft
=
fixtures
.
defaultDraft
();
draft
.
isPrivate
=
true
;
fakeStore
.
getGroup
.
returns
({
type
:
'open'
});
fakeStore
.
getDraft
.
returns
(
draft
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationLicense'
).
exists
());
});
});
describe
(
'annotation actions'
,
()
=>
{
it
(
'should show annotation actions'
,
()
=>
{
const
wrapper
=
createComponent
();
assert
.
isTrue
(
wrapper
.
find
(
'AnnotationActionBar'
).
exists
());
});
it
(
'should not show annotation actions when editing'
,
()
=>
{
setEditingMode
(
true
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationActionBar'
).
exists
());
});
it
(
'should not show annotation actions for new annotation'
,
()
=>
{
fakeMetadata
.
isNew
.
returns
(
true
);
const
wrapper
=
createComponent
();
assert
.
isFalse
(
wrapper
.
find
(
'AnnotationActionBar'
).
exists
());
});
});
});
});
});
});
src/sidebar/services/test/frame-sync-test.js
View file @
d77ca41f
...
@@ -156,8 +156,11 @@ describe('sidebar.frame-sync', function() {
...
@@ -156,8 +156,11 @@ describe('sidebar.frame-sync', function() {
});
});
it
(
'sends a "publicAnnotationCountChanged" message to the frame when there are only private annotations'
,
function
()
{
it
(
'sends a "publicAnnotationCountChanged" message to the frame when there are only private annotations'
,
function
()
{
const
annot
=
annotationFixtures
.
defaultAnnotation
();
delete
annot
.
permissions
;
fakeStore
.
setState
({
fakeStore
.
setState
({
annotations
:
{
annotations
:
[
annot
ationFixtures
.
defaultAnnotation
()
]
},
annotations
:
{
annotations
:
[
annot
]
},
});
});
assert
.
calledWithMatch
(
assert
.
calledWithMatch
(
fakeBridge
.
call
,
fakeBridge
.
call
,
...
...
src/sidebar/test/annotation-fixtures.js
View file @
d77ca41f
...
@@ -8,7 +8,12 @@ export function defaultAnnotation() {
...
@@ -8,7 +8,12 @@ export function defaultAnnotation() {
document
:
{
document
:
{
title
:
'A special document'
,
title
:
'A special document'
,
},
},
permissions
:
{
read
:
[
'group:__world__'
],
},
tags
:
[],
target
:
[{
source
:
'source'
,
selector
:
[]
}],
target
:
[{
source
:
'source'
,
selector
:
[]
}],
text
:
''
,
uri
:
'http://example.com'
,
uri
:
'http://example.com'
,
user
:
'acct:bill@localhost'
,
user
:
'acct:bill@localhost'
,
updated
:
'2015-05-10T20:18:56.613388+00:00'
,
updated
:
'2015-05-10T20:18:56.613388+00:00'
,
...
...
src/sidebar/util/test/annotation-metadata-test.js
View file @
d77ca41f
...
@@ -371,7 +371,9 @@ describe('annotation-metadata', function() {
...
@@ -371,7 +371,9 @@ describe('annotation-metadata', function() {
});
});
it
(
'returns false if an annotation is missing permissions'
,
function
()
{
it
(
'returns false if an annotation is missing permissions'
,
function
()
{
assert
.
isFalse
(
annotationMetadata
.
isPublic
(
fixtures
.
defaultAnnotation
()));
const
annot
=
fixtures
.
defaultAnnotation
();
delete
annot
.
permissions
;
assert
.
isFalse
(
annotationMetadata
.
isPublic
(
annot
));
});
});
});
});
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment