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
e45b5e0f
Commit
e45b5e0f
authored
Mar 27, 2023
by
Alejandro Celaya
Committed by
Alejandro Celaya
Mar 27, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make host's ToastMessages subscribe to events via emitter instead of sidebar RPC
parent
340e5850
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
59 additions
and
30 deletions
+59
-30
ToastMessages.tsx
src/annotator/components/ToastMessages.tsx
+12
-11
ToastMessages-test.js
src/annotator/components/test/ToastMessages-test.js
+12
-14
sidebar.tsx
src/annotator/sidebar.tsx
+13
-5
sidebar-test.js
src/annotator/test/sidebar-test.js
+22
-0
No files found.
src/annotator/components/ToastMessages.tsx
View file @
e45b5e0f
...
...
@@ -2,22 +2,18 @@ import { useCallback, useEffect, useState } from 'preact/hooks';
import
BaseToastMessages
from
'../../shared/components/BaseToastMessages'
;
import
type
{
ToastMessage
}
from
'../../shared/components/BaseToastMessages'
;
import
type
{
PortRPC
}
from
'../../shared/messaging'
;
import
type
{
HostToSidebarEvent
,
SidebarToHostEvent
,
}
from
'../../types/port-rpc-events'
;
import
type
{
Emitter
}
from
'../util/emitter'
;
export
type
ToastMessagesProps
=
{
sidebarRPC
:
PortRPC
<
SidebarToHostEvent
,
HostToSidebarEvent
>
;
emitter
:
Emitter
;
};
/**
* A component that renders toast messages
coming
from the sidebar, in a way
* A component that renders toast messages
published
from the sidebar, in a way
* that they "appear" in the viewport even when the sidebar is collapsed.
* This is useful to make sure screen readers announce hidden messages.
*/
export
default
function
ToastMessages
({
sidebarRPC
}:
ToastMessagesProps
)
{
export
default
function
ToastMessages
({
emitter
}:
ToastMessagesProps
)
{
const
[
messages
,
setMessages
]
=
useState
<
ToastMessage
[]
>
([]);
const
addMessage
=
useCallback
(
(
newMessage
:
ToastMessage
)
=>
setMessages
(
prev
=>
[...
prev
,
newMessage
]),
...
...
@@ -30,9 +26,14 @@ export default function ToastMessages({ sidebarRPC }: ToastMessagesProps) {
);
useEffect
(()
=>
{
sidebarRPC
.
on
(
'toastMessageAdded'
,
addMessage
);
sidebarRPC
.
on
(
'toastMessageDismissed'
,
dismissMessage
);
},
[
sidebarRPC
,
dismissMessage
,
addMessage
]);
emitter
.
subscribe
(
'toastMessageAdded'
,
addMessage
);
emitter
.
subscribe
(
'toastMessageDismissed'
,
dismissMessage
);
return
()
=>
{
emitter
.
unsubscribe
(
'toastMessageAdded'
,
addMessage
);
emitter
.
unsubscribe
(
'toastMessageDismissed'
,
dismissMessage
);
};
},
[
emitter
,
dismissMessage
,
addMessage
]);
return
(
<
BaseToastMessages
messages=
{
messages
}
onMessageDismiss=
{
dismissMessage
}
/>
...
...
src/annotator/components/test/ToastMessages-test.js
View file @
e45b5e0f
import
{
mount
}
from
'enzyme'
;
import
EventEmitter
from
'tiny-emitter'
;
import
{
Emitter
}
from
'../../util/emitter'
;
import
ToastMessages
from
'../ToastMessages'
;
describe
(
'ToastMessages'
,
()
=>
{
let
emitter
;
let
fakeSidebarRPC
;
const
fakeMessage
=
(
id
=
'someId'
)
=>
({
id
,
...
...
@@ -15,23 +15,21 @@ describe('ToastMessages', () => {
moreInfoURL
:
'http://www.example.com'
,
});
const
createComponent
=
()
=>
mount
(
<
ToastMessages
sidebarRPC
=
{
fakeSidebarRPC
}
/>
)
;
const
createComponent
=
()
=>
mount
(
<
ToastMessages
emitter
=
{
emitter
}
/>
)
;
beforeEach
(()
=>
{
emitter
=
new
EventEmitter
();
fakeSidebarRPC
=
{
on
:
(...
args
)
=>
emitter
.
on
(...
args
)
};
emitter
=
new
Emitter
(
new
EventEmitter
());
});
it
(
'
pushe
s new toast messages on toastMessageAdded'
,
()
=>
{
it
(
'
add
s new toast messages on toastMessageAdded'
,
()
=>
{
const
wrapper
=
createComponent
();
// Initially messages is empty
assert
.
lengthOf
(
wrapper
.
find
(
'BaseToastMessages'
).
prop
(
'messages'
),
0
);
emitter
.
emit
(
'toastMessageAdded'
,
fakeMessage
(
'someId1'
));
emitter
.
emit
(
'toastMessageAdded'
,
fakeMessage
(
'someId2'
));
emitter
.
emit
(
'toastMessageAdded'
,
fakeMessage
(
'someId3'
));
emitter
.
publish
(
'toastMessageAdded'
,
fakeMessage
(
'someId1'
));
emitter
.
publish
(
'toastMessageAdded'
,
fakeMessage
(
'someId2'
));
emitter
.
publish
(
'toastMessageAdded'
,
fakeMessage
(
'someId3'
));
wrapper
.
update
();
assert
.
lengthOf
(
wrapper
.
find
(
'BaseToastMessages'
).
prop
(
'messages'
),
3
);
...
...
@@ -41,14 +39,14 @@ describe('ToastMessages', () => {
const
wrapper
=
createComponent
();
// We push some messages first
emitter
.
emit
(
'toastMessageAdded'
,
fakeMessage
(
'someId1'
));
emitter
.
emit
(
'toastMessageAdded'
,
fakeMessage
(
'someId2'
));
emitter
.
emit
(
'toastMessageAdded'
,
fakeMessage
(
'someId3'
));
emitter
.
publish
(
'toastMessageAdded'
,
fakeMessage
(
'someId1'
));
emitter
.
publish
(
'toastMessageAdded'
,
fakeMessage
(
'someId2'
));
emitter
.
publish
(
'toastMessageAdded'
,
fakeMessage
(
'someId3'
));
wrapper
.
update
();
emitter
.
emit
(
'toastMessageDismissed'
,
'someId1'
);
emitter
.
publish
(
'toastMessageDismissed'
,
'someId1'
);
// We can also "dismiss" unknown messages. Those will be ignored
emitter
.
emit
(
'toastMessageDismissed'
,
'someId4'
);
emitter
.
publish
(
'toastMessageDismissed'
,
'someId4'
);
wrapper
.
update
();
assert
.
lengthOf
(
wrapper
.
find
(
'BaseToastMessages'
).
prop
(
'messages'
),
2
);
...
...
src/annotator/sidebar.tsx
View file @
e45b5e0f
import
*
as
Hammer
from
'hammerjs'
;
import
{
render
}
from
'preact'
;
import
type
{
ToastMessage
}
from
'../shared/components/BaseToastMessages'
;
import
{
addConfigFragment
}
from
'../shared/config-fragment'
;
import
{
sendErrorsTo
}
from
'../shared/frame-error-capture'
;
import
{
ListenerCollection
}
from
'../shared/listener-collection'
;
...
...
@@ -195,10 +196,7 @@ export class Sidebar implements Destroyable {
// will forward messages to render here while it is collapsed.
this
.
_messagesElement
=
document
.
createElement
(
'div'
);
shadowRoot
.
appendChild
(
this
.
_messagesElement
);
render
(
<
ToastMessages
sidebarRPC=
{
this
.
_sidebarRPC
}
/>,
this
.
_messagesElement
);
render
(<
ToastMessages
emitter=
{
this
.
_emitter
}
/>,
this
.
_messagesElement
);
}
// Register the sidebar as a handler for Hypothesis errors in this frame.
...
...
@@ -391,7 +389,7 @@ export class Sidebar implements Destroyable {
this
.
_sidebarRPC
.
on
(
'closeSidebar'
,
()
=>
this
.
close
());
// Sidebar listens to the `openNotebook` and `openProfile` events coming
// from the sidebar's iframe and re-publishes
it
via the emitter to the
// from the sidebar's iframe and re-publishes
them
via the emitter to the
// Notebook/Profile
this
.
_sidebarRPC
.
on
(
'openNotebook'
,
(
groupId
:
string
)
=>
{
this
.
hide
();
...
...
@@ -409,6 +407,16 @@ export class Sidebar implements Destroyable {
this
.
show
();
});
// Sidebar listens to the `toastMessageAdded` and `toastMessageDismissed`
// events coming from the sidebar's iframe and re-publishes them via the
// emitter
this
.
_sidebarRPC
.
on
(
'toastMessageAdded'
,
(
newMessage
:
ToastMessage
)
=>
{
this
.
_emitter
.
publish
(
'toastMessageAdded'
,
newMessage
);
});
this
.
_sidebarRPC
.
on
(
'toastMessageDismissed'
,
(
messageId
:
string
)
=>
{
this
.
_emitter
.
publish
(
'toastMessageDismissed'
,
messageId
);
});
// Suppressing ban-types here because the functions are originally defined
// as `Function` somewhere else. To be fixed when that is migrated to TS
// eslint-disable-next-line @typescript-eslint/ban-types
...
...
src/annotator/test/sidebar-test.js
View file @
e45b5e0f
...
...
@@ -402,6 +402,28 @@ describe('Sidebar', () => {
});
});
describe
(
'on "toastMessageAdded" event'
,
()
=>
{
it
(
're-publishes event via emitter'
,
()
=>
{
const
sidebar
=
createSidebar
();
sinon
.
stub
(
sidebar
.
_emitter
,
'publish'
);
emitSidebarEvent
(
'toastMessageAdded'
,
{});
assert
.
calledWith
(
sidebar
.
_emitter
.
publish
,
'toastMessageAdded'
,
{});
});
});
describe
(
'on "toastMessageDismissed" event'
,
()
=>
{
it
(
're-publishes event via emitter'
,
()
=>
{
const
sidebar
=
createSidebar
();
sinon
.
stub
(
sidebar
.
_emitter
,
'publish'
);
emitSidebarEvent
(
'toastMessageDismissed'
,
'someId'
);
assert
.
calledWith
(
sidebar
.
_emitter
.
publish
,
'toastMessageDismissed'
,
'someId'
);
});
});
describe
(
'on "loginRequested" event'
,
()
=>
{
it
(
'calls the onLoginRequest callback function if one was provided'
,
()
=>
{
const
onLoginRequest
=
sandbox
.
stub
();
...
...
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