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
3fedfbb8
Commit
3fedfbb8
authored
Feb 20, 2023
by
Lyza Danger Gardner
Committed by
Lyza Gardner
Feb 21, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add prototyped notification preference UI to `ProfileView`
parent
a1d1976f
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
155 additions
and
3 deletions
+155
-3
ProfileView.tsx
src/sidebar/components/ProfileView.tsx
+139
-3
tailwind.config.mjs
tailwind.config.mjs
+16
-0
No files found.
src/sidebar/components/ProfileView.tsx
View file @
3fedfbb8
import
{
Card
,
CardContent
,
Checkbox
,
CheckIcon
,
Scroll
,
SpinnerSpokesIcon
,
}
from
'@hypothesis/frontend-shared/lib/next'
;
import
type
{
PresentationalProps
}
from
'@hypothesis/frontend-shared/lib/types'
;
import
classnames
from
'classnames'
;
import
type
{
ComponentChildren
,
JSX
}
from
'preact'
;
import
{
useEffect
,
useState
}
from
'preact/hooks'
;
import
{
useSidebarStore
}
from
'../store'
;
import
{
useSidebarStore
}
from
'../store'
;
type
ToastBadgeProps
=
PresentationalProps
&
{
children
:
ComponentChildren
;
/**
* Callback invoked when toast is "done" and is requesting to be
* closed/removed
*/
onClose
?:
()
=>
void
;
}
&
JSX
.
HTMLAttributes
<
HTMLDivElement
>
;
/**
* Render a success "toast" badge.
*
* This uses an animation to pulse on render, then slowly fade out over several
* seconds. After its animation is complete, any provided `onClose` will be
* invoked.
*/
/* istanbul ignore next: Prototyped UI; add tests when solidified */
function
ToastBadge
({
classes
,
children
,
onClose
=
()
=>
{},
...
htmlAttributes
}:
ToastBadgeProps
)
{
return
(
<
div
className=
{
classnames
(
'flex items-center gap-x-1 py-1 px-2 rounded'
,
'bg-green-success/10 animate-pulse-fade-out'
,
classes
)
}
onAnimationEnd=
{
onClose
}
{
...
htmlAttributes
}
>
<
CheckIcon
className=
"text-green-success w-em h-em p-[0.125em]"
/>
<
div
className=
"text-sm"
>
{
children
}
</
div
>
</
div
>
);
}
/**
* Prototype of a subset of user-preferences/-profile management.
*/
/* istanbul ignore next: Prototyped UI; add tests when solidified */
export
default
function
ProfileView
()
{
export
default
function
ProfileView
()
{
const
store
=
useSidebarStore
();
const
store
=
useSidebarStore
();
/** Is there a "request in flight" (fake) to save the digest preference? */
const
[
loading
,
setLoading
]
=
useState
(
false
);
/**
* Increment each time a save is completed, then reset to 0 to close (remove)
* the success toast badge
*/
const
[
saveCount
,
setSaveCount
]
=
useState
(
0
);
useEffect
(
/**
* Fake a 1s network request delay after checkbox is checked or unchecked.
* This exercises the "loading" UI state for the checkbox.
*/
()
=>
{
let
savingTimeout
:
number
;
if
(
loading
)
{
savingTimeout
=
setTimeout
(()
=>
{
setLoading
(
false
);
// "Save was successful": increment the save count to ensure the
// toast badge gets re-rendered
setSaveCount
(
prevCount
=>
prevCount
+
1
);
},
1000
);
}
return
()
=>
{
clearTimeout
(
savingTimeout
);
};
},
[
loading
]
);
if
(
!
store
.
isFeatureEnabled
(
'client_user_profile'
))
{
if
(
!
store
.
isFeatureEnabled
(
'client_user_profile'
))
{
return
null
;
return
null
;
}
}
// Render save-success message after each successful save, but do not render
// it when a "request is in flight". This removal and re-adding across a
// sequence of saves ensures that the browser sees the message as newly- added
// to the accessiblity DOM and screen readers should announce it at the
// appropriate times.
const
withSaveMessage
=
saveCount
>
0
&&
!
loading
;
return
(
return
(
<
div
className=
"text-center"
data
-
testid=
"profile-container"
>
<
Card
data
-
testid=
"profile-container"
>
Profile
<
div
</
div
>
className=
{
classnames
(
// Ensure there is enough height to clear both the heading text and the
// success toast message without any danger of a jiggle
'h-12'
,
'px-3 border-b flex items-center'
)
}
>
<
div
className=
"grow"
>
<
h1
className=
"text-xl text-slate-7 font-normal"
>
Notifications
</
h1
>
</
div
>
<
ul
className=
"sr-only"
aria
-
live=
"polite"
>
{
withSaveMessage
&&
(
<
li
key=
{
saveCount
}
>
Notification preferences saved
</
li
>
)
}
</
ul
>
{
saveCount
>
0
&&
(
<
ToastBadge
key=
{
// The key is used here to ensure the `ToastBadge` re-renders and
// thus restarts its animation each time a save completes.
saveCount
}
onClose=
{
()
=>
setSaveCount
(
0
)
}
>
Saved
</
ToastBadge
>
)
}
</
div
>
<
Scroll
>
<
CardContent
size=
"lg"
>
<
Checkbox
defaultChecked=
{
true
}
disabled=
{
loading
}
onChange=
{
()
=>
setLoading
(
true
)
}
checkedIcon=
{
loading
?
SpinnerSpokesIcon
:
undefined
}
icon=
{
loading
?
SpinnerSpokesIcon
:
undefined
}
>
Email me a daily summary of activity in my courses
</
Checkbox
>
</
CardContent
>
</
Scroll
>
</
Card
>
);
);
}
}
tailwind.config.mjs
View file @
3fedfbb8
...
@@ -21,6 +21,7 @@ export default {
...
@@ -21,6 +21,7 @@ export default {
'fade-in'
:
'fade-in 0.3s forwards'
,
'fade-in'
:
'fade-in 0.3s forwards'
,
'fade-in-slow'
:
'fade-in 1s ease-in'
,
'fade-in-slow'
:
'fade-in 1s ease-in'
,
'fade-out'
:
'fade-out 0.3s forwards'
,
'fade-out'
:
'fade-out 0.3s forwards'
,
'pulse-fade-out'
:
'pulse-fade-out 5s ease-in-out forwards'
,
'slide-in-from-right'
:
'slide-in-from-right 0.3s forwards ease-in-out'
,
'slide-in-from-right'
:
'slide-in-from-right 0.3s forwards ease-in-out'
,
},
},
borderRadius
:
{
borderRadius
:
{
...
@@ -152,6 +153,21 @@ export default {
...
@@ -152,6 +153,21 @@ export default {
opacity
:
'0'
,
opacity
:
'0'
,
},
},
},
},
'pulse-fade-out'
:
{
'0%'
:
{
opacity
:
'1'
,
transform
:
'scale(1.1)'
,
},
'8%'
:
{
transform
:
'scale(1)'
,
},
'90%'
:
{
opacity
:
'0.8'
,
},
'100%'
:
{
opacity
:
'0'
,
},
},
'slide-in-from-right'
:
{
'slide-in-from-right'
:
{
'0%'
:
{
'0%'
:
{
opacity
:
'0'
,
opacity
:
'0'
,
...
...
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