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
612a583e
Unverified
Commit
612a583e
authored
May 28, 2020
by
Lyza Gardner
Committed by
GitHub
May 28, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2208 from hypothesis/ie11-deprecation-toast
Add deprecation notice for IE11 browsers
parents
81e6c501
b3ddff67
Changes
8
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
177 additions
and
13 deletions
+177
-13
hypothesis-app.js
src/sidebar/components/hypothesis-app.js
+15
-0
hypothesis-app-test.js
src/sidebar/components/test/hypothesis-app-test.js
+25
-0
toast-messages-test.js
src/sidebar/components/test/toast-messages-test.js
+39
-1
toast-messages.js
src/sidebar/components/toast-messages.js
+16
-2
toast-messenger-test.js
src/sidebar/services/test/toast-messenger-test.js
+28
-0
toast-messenger.js
src/sidebar/services/toast-messenger.js
+23
-3
toast-messages.scss
src/styles/sidebar/components/toast-messages.scss
+29
-2
variables.scss
src/styles/variables.scss
+2
-5
No files found.
src/sidebar/components/hypothesis-app.js
View file @
612a583e
...
@@ -3,6 +3,7 @@ import { useEffect, useMemo } from 'preact/hooks';
...
@@ -3,6 +3,7 @@ import { useEffect, useMemo } from 'preact/hooks';
import
propTypes
from
'prop-types'
;
import
propTypes
from
'prop-types'
;
import
bridgeEvents
from
'../../shared/bridge-events'
;
import
bridgeEvents
from
'../../shared/bridge-events'
;
import
{
isIE11
}
from
'../../shared/user-agent'
;
import
serviceConfig
from
'../service-config'
;
import
serviceConfig
from
'../service-config'
;
import
useStore
from
'../store/use-store'
;
import
useStore
from
'../store/use-store'
;
import
uiConstants
from
'../ui-constants'
;
import
uiConstants
from
'../ui-constants'
;
...
@@ -88,6 +89,20 @@ function HypothesisApp({
...
@@ -88,6 +89,20 @@ function HypothesisApp({
}
}
},
[
isSidebar
,
profile
,
openSidebarPanel
,
settings
]);
},
[
isSidebar
,
profile
,
openSidebarPanel
,
settings
]);
// Show a deprecation warning if current browser is IE11
useEffect
(()
=>
{
if
(
isIE11
())
{
toastMessenger
.
notice
(
'Hypothesis is ending support for this browser (Internet Explorer 11) on July 1, 2020.'
,
{
autoDismiss
:
false
,
moreInfoURL
:
'https://web.hypothes.is/help/which-browsers-are-supported-by-hypothesis/'
,
}
);
}
},
[
toastMessenger
]);
const
login
=
async
()
=>
{
const
login
=
async
()
=>
{
if
(
serviceConfig
(
settings
))
{
if
(
serviceConfig
(
settings
))
{
// Let the host page handle the login request
// Let the host page handle the login request
...
...
src/sidebar/components/test/hypothesis-app-test.js
View file @
612a583e
...
@@ -7,6 +7,7 @@ import mockImportedComponents from '../../../test-util/mock-imported-components'
...
@@ -7,6 +7,7 @@ import mockImportedComponents from '../../../test-util/mock-imported-components'
import
HypothesisApp
,
{
$imports
}
from
'../hypothesis-app'
;
import
HypothesisApp
,
{
$imports
}
from
'../hypothesis-app'
;
describe
(
'HypothesisApp'
,
()
=>
{
describe
(
'HypothesisApp'
,
()
=>
{
let
fakeUserAgent
=
null
;
let
fakeStore
=
null
;
let
fakeStore
=
null
;
let
fakeAuth
=
null
;
let
fakeAuth
=
null
;
let
fakeBridge
=
null
;
let
fakeBridge
=
null
;
...
@@ -72,12 +73,18 @@ describe('HypothesisApp', () => {
...
@@ -72,12 +73,18 @@ describe('HypothesisApp', () => {
call
:
sinon
.
stub
(),
call
:
sinon
.
stub
(),
};
};
fakeUserAgent
=
{
isIE11
:
sinon
.
stub
().
returns
(
false
),
};
fakeToastMessenger
=
{
fakeToastMessenger
=
{
error
:
sinon
.
stub
(),
error
:
sinon
.
stub
(),
notice
:
sinon
.
stub
(),
};
};
$imports
.
$mock
(
mockImportedComponents
());
$imports
.
$mock
(
mockImportedComponents
());
$imports
.
$mock
({
$imports
.
$mock
({
'../../shared/user-agent'
:
fakeUserAgent
,
'../service-config'
:
fakeServiceConfig
,
'../service-config'
:
fakeServiceConfig
,
'../store/use-store'
:
callback
=>
callback
(
fakeStore
),
'../store/use-store'
:
callback
=>
callback
(
fakeStore
),
'../util/session'
:
{
'../util/session'
:
{
...
@@ -138,6 +145,24 @@ describe('HypothesisApp', () => {
...
@@ -138,6 +145,24 @@ describe('HypothesisApp', () => {
});
});
});
});
describe
(
'toast message warning of IE11 deprecation'
,
()
=>
{
it
(
'shows notice if user agent is IE11'
,
()
=>
{
fakeUserAgent
.
isIE11
.
returns
(
true
);
createComponent
();
assert
.
called
(
fakeToastMessenger
.
notice
);
});
it
(
'does not show notice if another user agent'
,
()
=>
{
fakeUserAgent
.
isIE11
.
returns
(
false
);
createComponent
();
assert
.
notCalled
(
fakeToastMessenger
.
notice
);
});
});
describe
(
'"status" field of "auth" prop passed to children'
,
()
=>
{
describe
(
'"status" field of "auth" prop passed to children'
,
()
=>
{
const
getStatus
=
wrapper
=>
wrapper
.
find
(
'TopBar'
).
prop
(
'auth'
).
status
;
const
getStatus
=
wrapper
=>
wrapper
.
find
(
'TopBar'
).
prop
(
'auth'
).
status
;
...
...
src/sidebar/components/test/toast-messages-test.js
View file @
612a583e
...
@@ -29,6 +29,16 @@ describe('ToastMessages', () => {
...
@@ -29,6 +29,16 @@ describe('ToastMessages', () => {
};
};
};
};
let
fakeNoticeMessage
=
()
=>
{
return
{
type
:
'notice'
,
message
:
'you should know...'
,
id
:
'someid3'
,
isDismissed
:
false
,
moreInfoURL
:
'http://www.example.com'
,
};
};
function
createComponent
(
props
)
{
function
createComponent
(
props
)
{
return
mount
(
return
mount
(
<
ToastMessages
toastMessenger
=
{
fakeToastMessenger
}
{...
props
}
/
>
<
ToastMessages
toastMessenger
=
{
fakeToastMessenger
}
{...
props
}
/
>
...
@@ -58,11 +68,12 @@ describe('ToastMessages', () => {
...
@@ -58,11 +68,12 @@ describe('ToastMessages', () => {
fakeStore
.
getToastMessages
.
returns
([
fakeStore
.
getToastMessages
.
returns
([
fakeSuccessMessage
(),
fakeSuccessMessage
(),
fakeErrorMessage
(),
fakeErrorMessage
(),
fakeNoticeMessage
(),
]);
]);
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
assert
.
lengthOf
(
wrapper
.
find
(
'ToastMessage'
),
2
);
assert
.
lengthOf
(
wrapper
.
find
(
'ToastMessage'
),
3
);
});
});
describe
(
'`ToastMessage` sub-component'
,
()
=>
{
describe
(
'`ToastMessage` sub-component'
,
()
=>
{
...
@@ -91,9 +102,24 @@ describe('ToastMessages', () => {
...
@@ -91,9 +102,24 @@ describe('ToastMessages', () => {
assert
.
calledOnce
(
fakeToastMessenger
.
dismiss
);
assert
.
calledOnce
(
fakeToastMessenger
.
dismiss
);
});
});
it
(
'should not dismiss the message if a "More info" link is clicked'
,
()
=>
{
fakeStore
.
getToastMessages
.
returns
([
fakeNoticeMessage
()]);
const
wrapper
=
createComponent
();
const
link
=
wrapper
.
find
(
'.toast-message__link a'
);
act
(()
=>
{
link
.
getDOMNode
().
dispatchEvent
(
new
Event
(
'click'
,
{
bubbles
:
true
}));
});
assert
.
notCalled
(
fakeToastMessenger
.
dismiss
);
});
[
[
{
message
:
fakeSuccessMessage
(),
className
:
'toast-message--success'
},
{
message
:
fakeSuccessMessage
(),
className
:
'toast-message--success'
},
{
message
:
fakeErrorMessage
(),
className
:
'toast-message--error'
},
{
message
:
fakeErrorMessage
(),
className
:
'toast-message--error'
},
{
message
:
fakeNoticeMessage
(),
className
:
'toast-message--notice'
},
].
forEach
(
testCase
=>
{
].
forEach
(
testCase
=>
{
it
(
'should assign a CSS class based on message type'
,
()
=>
{
it
(
'should assign a CSS class based on message type'
,
()
=>
{
fakeStore
.
getToastMessages
.
returns
([
testCase
.
message
]);
fakeStore
.
getToastMessages
.
returns
([
testCase
.
message
]);
...
@@ -129,6 +155,7 @@ describe('ToastMessages', () => {
...
@@ -129,6 +155,7 @@ describe('ToastMessages', () => {
[
[
{
messages
:
[
fakeSuccessMessage
()],
icons
:
[
'success'
]
},
{
messages
:
[
fakeSuccessMessage
()],
icons
:
[
'success'
]
},
{
messages
:
[
fakeErrorMessage
()],
icons
:
[
'error'
]
},
{
messages
:
[
fakeErrorMessage
()],
icons
:
[
'error'
]
},
{
messages
:
[
fakeNoticeMessage
()],
icons
:
[
'cancel'
]
},
{
{
messages
:
[
fakeSuccessMessage
(),
fakeErrorMessage
()],
messages
:
[
fakeSuccessMessage
(),
fakeErrorMessage
()],
icons
:
[
'success'
,
'error'
],
icons
:
[
'success'
,
'error'
],
...
@@ -148,11 +175,22 @@ describe('ToastMessages', () => {
...
@@ -148,11 +175,22 @@ describe('ToastMessages', () => {
});
});
});
});
it
(
'should render a "more info" link if URL is present in message object'
,
()
=>
{
fakeStore
.
getToastMessages
.
returns
([
fakeNoticeMessage
()]);
const
wrapper
=
createComponent
();
const
link
=
wrapper
.
find
(
'.toast-message__link a'
);
assert
.
equal
(
link
.
props
().
href
,
'http://www.example.com'
);
assert
.
equal
(
link
.
text
(),
'More info'
);
});
describe
(
'a11y'
,
()
=>
{
describe
(
'a11y'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
fakeStore
.
getToastMessages
.
returns
([
fakeStore
.
getToastMessages
.
returns
([
fakeSuccessMessage
(),
fakeSuccessMessage
(),
fakeErrorMessage
(),
fakeErrorMessage
(),
fakeNoticeMessage
(),
]);
]);
});
});
...
...
src/sidebar/components/toast-messages.js
View file @
612a583e
...
@@ -16,7 +16,7 @@ import SvgIcon from '../../shared/components/svg-icon';
...
@@ -16,7 +16,7 @@ import SvgIcon from '../../shared/components/svg-icon';
function
ToastMessage
({
message
,
onDismiss
})
{
function
ToastMessage
({
message
,
onDismiss
})
{
// Capitalize the message type for prepending
// Capitalize the message type for prepending
const
prefix
=
message
.
type
.
charAt
(
0
).
toUpperCase
()
+
message
.
type
.
slice
(
1
);
const
prefix
=
message
.
type
.
charAt
(
0
).
toUpperCase
()
+
message
.
type
.
slice
(
1
);
const
iconName
=
message
.
type
===
'notice'
?
'cancel'
:
message
.
type
;
/**
/**
* a11y linting is disabled here: There is a click-to-remove handler on a
* a11y linting is disabled here: There is a click-to-remove handler on a
* non-interactive element. This allows sighted users to get the toast message
* non-interactive element. This allows sighted users to get the toast message
...
@@ -40,11 +40,25 @@ function ToastMessage({ message, onDismiss }) {
...
@@ -40,11 +40,25 @@ function ToastMessage({ message, onDismiss }) {
)}
)}
>
>
<
div
className
=
"toast-message__type"
>
<
div
className
=
"toast-message__type"
>
<
SvgIcon
name
=
{
message
.
typ
e
}
className
=
"toast-message__icon"
/>
<
SvgIcon
name
=
{
iconNam
e
}
className
=
"toast-message__icon"
/>
<
/div
>
<
/div
>
<
div
className
=
"toast-message__message"
>
<
div
className
=
"toast-message__message"
>
<
strong
>
{
prefix
}:
<
/strong
>
<
strong
>
{
prefix
}:
<
/strong
>
{
message
.
message
}
{
message
.
message
}
{
message
.
moreInfoURL
&&
(
<
div
className
=
"toast-message__link"
>
<
a
href
=
{
message
.
moreInfoURL
}
onClick
=
{
event
=>
event
.
stopPropagation
()
/* consume the event so that it does not dismiss the message */
}
target
=
"_new"
>
More
info
<
/a
>
<
/div
>
)}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/li
>
<
/li
>
...
...
src/sidebar/services/test/toast-messenger-test.js
View file @
612a583e
...
@@ -47,6 +47,21 @@ describe('toastMessenger', function () {
...
@@ -47,6 +47,21 @@ describe('toastMessenger', function () {
);
);
});
});
it
(
'passes along `moreInfoURL` when present'
,
()
=>
{
fakeStore
.
hasToastMessage
.
returns
(
false
);
service
.
success
(
'hooray'
,
{
moreInfoURL
:
'http://www.example.com'
});
assert
.
calledWith
(
fakeStore
.
addToastMessage
,
sinon
.
match
({
type
:
'success'
,
message
:
'hooray'
,
moreInfoURL
:
'http://www.example.com'
,
})
);
});
it
(
'dismisses the message after timeout fires'
,
()
=>
{
it
(
'dismisses the message after timeout fires'
,
()
=>
{
fakeStore
.
hasToastMessage
.
returns
(
false
);
fakeStore
.
hasToastMessage
.
returns
(
false
);
fakeStore
.
getToastMessage
.
returns
(
undefined
);
fakeStore
.
getToastMessage
.
returns
(
undefined
);
...
@@ -62,6 +77,19 @@ describe('toastMessenger', function () {
...
@@ -62,6 +77,19 @@ describe('toastMessenger', function () {
});
});
});
});
describe
(
'#notice'
,
()
=>
{
it
(
'adds a new notice toast message to the store'
,
()
=>
{
fakeStore
.
hasToastMessage
.
returns
(
false
);
service
.
notice
(
'boo'
);
assert
.
calledWith
(
fakeStore
.
addToastMessage
,
sinon
.
match
({
type
:
'notice'
,
message
:
'boo'
})
);
});
});
describe
(
'#error'
,
()
=>
{
describe
(
'#error'
,
()
=>
{
it
(
'does not add a new error message if one with the same message text already exists'
,
()
=>
{
it
(
'does not add a new error message if one with the same message text already exists'
,
()
=>
{
fakeStore
.
hasToastMessage
.
returns
(
true
);
fakeStore
.
hasToastMessage
.
returns
(
true
);
...
...
src/sidebar/services/toast-messenger.js
View file @
612a583e
...
@@ -15,6 +15,7 @@ const MESSAGE_DISMISS_DELAY = 500;
...
@@ -15,6 +15,7 @@ const MESSAGE_DISMISS_DELAY = 500;
*
*
* @typedef {Object} MessageOptions
* @typedef {Object} MessageOptions
* @prop {boolean} [autoDismiss=true] - Whether the toast message automatically disappears.
* @prop {boolean} [autoDismiss=true] - Whether the toast message automatically disappears.
* @prop {string} [moreInfoURL=''] - Optional URL for users to visit for "more info"
*/
*/
// @ngInject
// @ngInject
...
@@ -47,15 +48,23 @@ export default function toastMessenger(store) {
...
@@ -47,15 +48,23 @@ export default function toastMessenger(store) {
* @param {string} message - The message to be rendered
* @param {string} message - The message to be rendered
* @param {MessageOptions} [options]
* @param {MessageOptions} [options]
*/
*/
const
addMessage
=
(
type
,
message
,
{
autoDismiss
=
true
}
=
{})
=>
{
const
addMessage
=
(
type
,
messageText
,
{
autoDismiss
=
true
,
moreInfoURL
=
''
}
=
{}
)
=>
{
// Do not add duplicate messages (messages with the same type and text)
// Do not add duplicate messages (messages with the same type and text)
if
(
store
.
hasToastMessage
(
type
,
message
))
{
if
(
store
.
hasToastMessage
(
type
,
message
Text
))
{
return
;
return
;
}
}
const
id
=
generateHexString
(
10
);
const
id
=
generateHexString
(
10
);
const
message
=
{
type
,
id
,
message
:
messageText
,
moreInfoURL
};
store
.
addToastMessage
({
type
,
message
,
id
,
isDismissed
:
false
});
store
.
addToastMessage
({
isDismissed
:
false
,
...
message
,
});
if
(
autoDismiss
)
{
if
(
autoDismiss
)
{
// Attempt to dismiss message after a set time period. NB: The message may
// Attempt to dismiss message after a set time period. NB: The message may
...
@@ -87,9 +96,20 @@ export default function toastMessenger(store) {
...
@@ -87,9 +96,20 @@ export default function toastMessenger(store) {
addMessage
(
'success'
,
messageText
,
options
);
addMessage
(
'success'
,
messageText
,
options
);
};
};
/**
* Add a warn/notice toast message with `messageText`
*
* @param {string} messageText
* @param {MessageOptions} options
*/
const
notice
=
(
messageText
,
options
)
=>
{
addMessage
(
'notice'
,
messageText
,
options
);
};
return
{
return
{
dismiss
,
dismiss
,
error
,
error
,
success
,
success
,
notice
,
};
};
}
}
src/styles/sidebar/components/toast-messages.scss
View file @
612a583e
@use
"../../mixins/panel"
;
@use
"../../mixins/panel"
;
@use
"../mixins/responsive"
;
@use
"../../variables"
as
var
;
@use
"../../variables"
as
var
;
.toast-messages
{
.toast-messages
{
position
:
absolute
;
position
:
absolute
;
z-index
:
1
;
z-index
:
1
;
width
:
100%
;
left
:
0
;
left
:
0
;
padding
:
0
8px
;
width
:
100%
;
padding
:
0
0
.5em
;
@include
responsive
.
tablet-and-up
{
// When displaying in a wider viewport (desktop/tablet outside of sidebar)
max-width
:
responsive
.
$break-tablet
;
width
:
responsive
.
$break-tablet
;
padding-left
:
2rem
;
padding-right
:
2rem
;
left
:
50%
;
margin-left
:
calc
(
-0
.5
*
#{
responsive
.
$break-tablet
}
);
}
}
}
.toast-message-container
{
.toast-message-container
{
...
@@ -40,7 +51,13 @@
...
@@ -40,7 +51,13 @@
border
:
1px
solid
var
.
$color-error
;
border
:
1px
solid
var
.
$color-error
;
}
}
&
--notice
{
border
:
1px
solid
var
.
$color-notice
;
}
&
__type
{
&
__type
{
display
:
flex
;
align-items
:
center
;
padding
:
1em
;
padding
:
1em
;
color
:
white
;
color
:
white
;
}
}
...
@@ -53,6 +70,10 @@
...
@@ -53,6 +70,10 @@
background-color
:
var
.
$color-error
;
background-color
:
var
.
$color-error
;
}
}
&
--notice
&
__type
{
background-color
:
var
.
$color-notice
;
}
&
__icon
{
&
__icon
{
// Specific adjustments for success and error icons
// Specific adjustments for success and error icons
margin-top
:
2px
;
margin-top
:
2px
;
...
@@ -60,6 +81,12 @@
...
@@ -60,6 +81,12 @@
&
__message
{
&
__message
{
padding
:
1em
;
padding
:
1em
;
width
:
100%
;
}
&
__link
{
text-align
:
right
;
text-decoration
:
underline
;
}
}
}
}
...
...
src/styles/variables.scss
View file @
612a583e
...
@@ -35,19 +35,16 @@ $grey-6: #3f3f3f;
...
@@ -35,19 +35,16 @@ $grey-6: #3f3f3f;
$grey-7
:
#202020
;
$grey-7
:
#202020
;
// Colors
// Colors
$color-success
:
#00a36d
;
$color-success
:
#00a36d
;
$color-
warning
:
#fbc168
;
$color-
notice
:
#fbc168
;
$color-error
:
#d93c3f
;
$color-error
:
#d93c3f
;
$brand
:
#bd1c2b
;
$brand
:
#bd1c2b
;
$highlight
:
#58cef4
;
$highlight
:
#58cef4
;
$color-focus-outline
:
#51a7e8
;
$color-focus-outline
:
#51a7e8
;
$color-focus-shadow
:
color
.
scale
(
$color-focus-outline
,
$alpha
:
-50%
);
$color-focus-shadow
:
color
.
scale
(
$color-focus-outline
,
$alpha
:
-50%
);
$error-color
:
#f0480c
!
default
;
$success-color
:
#1cbd41
!
default
;
// Tint and shade functions from
// Tint and shade functions from
// https://css-tricks.com/snippets/sass/tint-shade-functions
// https://css-tricks.com/snippets/sass/tint-shade-functions
@function
tint
(
$color
,
$percent
)
{
@function
tint
(
$color
,
$percent
)
{
...
...
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