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
5f0e7b76
Commit
5f0e7b76
authored
Nov 27, 2023
by
Alejandro Celaya
Committed by
Alejandro Celaya
Nov 28, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Migrate Help sidebar panel to tabs
parent
b34060fa
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
93 additions
and
122 deletions
+93
-122
HelpPanel.tsx
src/sidebar/components/HelpPanel.tsx
+61
-64
HelpPanel-test.js
src/sidebar/components/test/HelpPanel-test.js
+32
-58
No files found.
src/sidebar/components/HelpPanel.tsx
View file @
5f0e7b76
import
{
Link
,
LinkButton
}
from
'@hypothesis/frontend-shared'
;
import
{
Card
,
Link
,
Tab
}
from
'@hypothesis/frontend-shared'
;
import
{
ArrowRightIcon
,
ExternalIcon
}
from
'@hypothesis/frontend-shared'
;
import
{
ExternalIcon
}
from
'@hypothesis/frontend-shared'
;
import
type
{
ComponentChildren
as
Children
}
from
'preact
'
;
import
classnames
from
'classnames
'
;
import
{
useCallback
,
useMemo
,
useState
}
from
'preact/hooks'
;
import
{
useCallback
,
use
Id
,
use
Memo
,
useState
}
from
'preact/hooks'
;
import
{
username
}
from
'../helpers/account-id'
;
import
{
username
}
from
'../helpers/account-id'
;
import
{
VersionData
}
from
'../helpers/version-data'
;
import
{
VersionData
}
from
'../helpers/version-data'
;
import
{
withServices
}
from
'../service-context'
;
import
{
withServices
}
from
'../service-context'
;
import
type
{
SessionService
}
from
'../services/session'
;
import
type
{
SessionService
}
from
'../services/session'
;
import
{
useSidebarStore
}
from
'../store'
;
import
{
useSidebarStore
}
from
'../store'
;
import
TabHeader
from
'./ShareDialog/TabHeader'
;
import
TabPanel
from
'./ShareDialog/TabPanel'
;
import
SidebarPanel
from
'./SidebarPanel'
;
import
SidebarPanel
from
'./SidebarPanel'
;
import
Tutorial
from
'./Tutorial'
;
import
Tutorial
from
'./Tutorial'
;
import
VersionInfo
from
'./VersionInfo'
;
import
VersionInfo
from
'./VersionInfo'
;
type
HelpPanelNavigationButtonProps
=
{
children
:
Children
;
onClick
:
(
e
:
Event
)
=>
void
;
};
/**
* Navigation link-button to swap between sub-panels in the help panel
*/
function
HelpPanelNavigationButton
({
children
,
onClick
,
}:
HelpPanelNavigationButtonProps
)
{
return
(
<
LinkButton
variant=
"brand"
onClick=
{
onClick
}
underline=
"hover"
>
<
div
className=
"flex items-center gap-x-1"
>
{
children
}
<
ArrowRightIcon
className=
"w-em h-em"
/>
</
div
>
</
LinkButton
>
);
}
type
HelpPanelTabProps
=
{
type
HelpPanelTabProps
=
{
/** What the tab's link should say. */
/** What the tab's link should say. */
linkText
:
string
;
linkText
:
string
;
...
@@ -42,7 +22,7 @@ type HelpPanelTabProps = {
...
@@ -42,7 +22,7 @@ type HelpPanelTabProps = {
};
};
/**
/**
* External link "tabs"
inside
of the help panel.
* External link "tabs"
at the bottom
of the help panel.
*/
*/
function
HelpPanelTab
({
linkText
,
url
}:
HelpPanelTabProps
)
{
function
HelpPanelTab
({
linkText
,
url
}:
HelpPanelTabProps
)
{
return
(
return
(
...
@@ -64,6 +44,8 @@ type HelpPanelProps = {
...
@@ -64,6 +44,8 @@ type HelpPanelProps = {
session
:
SessionService
;
session
:
SessionService
;
};
};
type
PanelKey
=
'tutorial'
|
'versionInfo'
;
/**
/**
* A help sidebar panel with two sub-panels: tutorial and version info.
* A help sidebar panel with two sub-panels: tutorial and version info.
*/
*/
...
@@ -74,6 +56,10 @@ function HelpPanel({ session }: HelpPanelProps) {
...
@@ -74,6 +56,10 @@ function HelpPanel({ session }: HelpPanelProps) {
const
profile
=
store
.
profile
();
const
profile
=
store
.
profile
();
const
displayName
=
const
displayName
=
profile
.
user_info
?.
display_name
??
username
(
profile
.
userid
);
profile
.
user_info
?.
display_name
??
username
(
profile
.
userid
);
const
tutorialTabId
=
useId
();
const
tutorialPanelId
=
useId
();
const
versionTabId
=
useId
();
const
versionPanelId
=
useId
();
// Should this panel be auto-opened at app launch? Note that the actual
// Should this panel be auto-opened at app launch? Note that the actual
// auto-open triggering of this panel is owned by the `HypothesisApp` component.
// auto-open triggering of this panel is owned by the `HypothesisApp` component.
...
@@ -82,12 +68,6 @@ function HelpPanel({ session }: HelpPanelProps) {
...
@@ -82,12 +68,6 @@ function HelpPanel({ session }: HelpPanelProps) {
const
hasAutoDisplayPreference
=
const
hasAutoDisplayPreference
=
!!
store
.
profile
().
preferences
.
show_sidebar_tutorial
;
!!
store
.
profile
().
preferences
.
show_sidebar_tutorial
;
const
subPanelTitles
=
{
tutorial
:
'Getting started'
,
versionInfo
:
'About this version'
,
};
type
PanelKey
=
keyof
typeof
subPanelTitles
;
// The "Tutorial" (getting started) subpanel is the default panel shown
// The "Tutorial" (getting started) subpanel is the default panel shown
const
[
activeSubPanel
,
setActiveSubPanel
]
=
useState
<
PanelKey
>
(
'tutorial'
);
const
[
activeSubPanel
,
setActiveSubPanel
]
=
useState
<
PanelKey
>
(
'tutorial'
);
...
@@ -115,11 +95,6 @@ function HelpPanel({ session }: HelpPanelProps) {
...
@@ -115,11 +95,6 @@ function HelpPanel({ session }: HelpPanelProps) {
// create-new-ticket form
// create-new-ticket form
const
supportTicketURL
=
`https://web.hypothes.is/get-help/?sys_info=
${
versionData
.
asEncodedURLString
()}
`
;
const
supportTicketURL
=
`https://web.hypothes.is/get-help/?sys_info=
${
versionData
.
asEncodedURLString
()}
`
;
const
openSubPanel
=
(
e
:
Event
,
panelName
:
PanelKey
)
=>
{
e
.
preventDefault
();
setActiveSubPanel
(
panelName
);
};
const
onActiveChanged
=
useCallback
(
const
onActiveChanged
=
useCallback
(
(
active
:
boolean
)
=>
{
(
active
:
boolean
)
=>
{
if
(
!
active
&&
hasAutoDisplayPreference
)
{
if
(
!
active
&&
hasAutoDisplayPreference
)
{
...
@@ -137,41 +112,63 @@ function HelpPanel({ session }: HelpPanelProps) {
...
@@ -137,41 +112,63 @@ function HelpPanel({ session }: HelpPanelProps) {
title=
"Help"
title=
"Help"
panelName=
"help"
panelName=
"help"
onActiveChanged=
{
onActiveChanged
}
onActiveChanged=
{
onActiveChanged
}
variant=
"custom"
>
>
<
div
className=
"space-y-4"
>
<
TabHeader
>
<
div
className=
"flex items-center"
>
<
Tab
<
h3
className=
"grow text-md font-medium"
data
-
testid=
"subpanel-title"
>
id=
{
tutorialTabId
}
{
subPanelTitles
[
activeSubPanel
]
}
aria
-
controls=
{
tutorialPanelId
}
</
h3
>
variant=
"tab"
{
activeSubPanel
===
'versionInfo'
&&
(
textContent=
"Help"
<
HelpPanelNavigationButton
selected=
{
activeSubPanel
===
'tutorial'
}
onClick=
{
e
=>
openSubPanel
(
e
,
'tutorial'
)
}
onClick=
{
()
=>
setActiveSubPanel
(
'tutorial'
)
}
data
-
testid=
"tutorial-tab"
>
>
Getting started
Help
</
HelpPanelNavigationButton
>
</
Tab
>
)
}
<
Tab
{
activeSubPanel
===
'tutorial'
&&
(
id=
{
versionTabId
}
<
HelpPanelNavigationButton
aria
-
controls=
{
versionPanelId
}
onClick=
{
e
=>
openSubPanel
(
e
,
'versionInfo'
)
}
variant=
"tab"
textContent=
"Version"
selected=
{
activeSubPanel
===
'versionInfo'
}
onClick=
{
()
=>
setActiveSubPanel
(
'versionInfo'
)
}
data
-
testid=
"version-info-tab"
>
Version
</
Tab
>
</
TabHeader
>
<
Card
classes=
{
classnames
({
'rounded-tl-none'
:
activeSubPanel
===
'tutorial'
,
})
}
>
<
div
className=
"border-b"
>
<
TabPanel
id=
{
tutorialPanelId
}
aria
-
labelledby=
{
tutorialTabId
}
active=
{
activeSubPanel
===
'tutorial'
}
title=
"Getting started"
>
<
Tutorial
/>
</
TabPanel
>
<
TabPanel
id=
{
versionPanelId
}
aria
-
labelledby=
{
versionTabId
}
active=
{
activeSubPanel
===
'versionInfo'
}
title=
"Version details"
>
>
About this version
</
HelpPanelNavigationButton
>
)
}
</
div
>
<
div
className=
"border-y py-4"
>
{
activeSubPanel
===
'tutorial'
&&
<
Tutorial
/>
}
{
activeSubPanel
===
'versionInfo'
&&
(
<
VersionInfo
versionData=
{
versionData
}
/>
<
VersionInfo
versionData=
{
versionData
}
/>
)
}
</
TabPanel
>
</
div
>
</
div
>
<
div
className=
"flex items-center"
>
<
div
className=
"flex items-center
p-3
"
>
<
HelpPanelTab
<
HelpPanelTab
linkText=
"Help topics"
linkText=
"Help topics"
url=
"https://web.hypothes.is/help/"
url=
"https://web.hypothes.is/help/"
/>
/>
<
HelpPanelTab
linkText=
"New support ticket"
url=
{
supportTicketURL
}
/>
<
HelpPanelTab
linkText=
"New support ticket"
url=
{
supportTicketURL
}
/>
</
div
>
</
div
>
</
div
>
</
Card
>
</
SidebarPanel
>
</
SidebarPanel
>
);
);
}
}
...
...
src/sidebar/components/test/HelpPanel-test.js
View file @
5f0e7b76
import
{
Tab
}
from
'@hypothesis/frontend-shared'
;
import
{
import
{
checkAccessibility
,
checkAccessibility
,
mockImportedComponents
,
mockImportedComponents
,
...
@@ -45,45 +46,46 @@ describe('HelpPanel', () => {
...
@@ -45,45 +46,46 @@ describe('HelpPanel', () => {
'../store'
:
{
useSidebarStore
:
()
=>
fakeStore
},
'../store'
:
{
useSidebarStore
:
()
=>
fakeStore
},
'../helpers/version-data'
:
{
VersionData
:
FakeVersionData
},
'../helpers/version-data'
:
{
VersionData
:
FakeVersionData
},
});
});
$imports
.
$restore
({
// Rendering TabHeader and TabPanel is needed for a11y tests
'./ShareDialog/TabHeader'
:
true
,
'./ShareDialog/TabPanel'
:
true
,
});
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
$imports
.
$restore
();
$imports
.
$restore
();
});
});
const
getActivePanel
=
wrapper
=>
wrapper
.
find
(
'TabPanel[active=true]'
);
const
clickTab
=
(
wrapper
,
tabId
)
=>
wrapper
.
find
(
`button[data-testid="
${
tabId
}
"]`
).
simulate
(
'click'
);
context
(
'when viewing tutorial sub-panel'
,
()
=>
{
context
(
'when viewing tutorial sub-panel'
,
()
=>
{
it
(
'should show tutorial by default'
,
()
=>
{
it
(
'should show tutorial by default'
,
()
=>
{
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
const
subHeader
=
wrapper
.
find
(
'[data-testid="subpanel-title"]'
);
const
selectedTab
=
wrapper
.
find
(
Tab
)
.
findWhere
(
tab
=>
tab
.
prop
(
'selected'
));
const
activePanel
=
getActivePanel
(
wrapper
);
assert
.
include
(
s
ubHeader
.
text
(),
'Getting started
'
);
assert
.
include
(
s
electedTab
.
text
(),
'Help
'
);
assert
.
isTrue
(
wrapper
.
find
(
'Tutorial'
).
exists
());
assert
.
isTrue
(
activePanel
.
find
(
'Tutorial'
).
exists
());
assert
.
isFalse
(
wrapper
.
find
(
'VersionInfo'
).
exists
());
assert
.
isFalse
(
activePanel
.
find
(
'VersionInfo'
).
exists
());
});
});
it
(
'should s
how navigation link to versionInfo sub-panel'
,
()
=>
{
it
(
'should s
witch to versionInfo sub-panel when second tab is clicked'
,
async
()
=>
{
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
const
button
=
wrapper
.
find
(
'HelpPanelNavigationButton'
);
clickTab
(
wrapper
,
'version-info-tab'
);
assert
.
include
(
button
.
text
(),
'About this version'
);
});
it
(
'should switch to versionInfo sub-panel when navigation button clicked'
,
async
()
=>
{
const
activePanel
=
getActivePanel
(
wrapper
);
const
wrapper
=
createComponent
();
act
(()
=>
{
wrapper
.
find
(
'LinkButton'
)
.
getDOMNode
()
.
dispatchEvent
(
new
Event
(
'click'
));
});
wrapper
.
update
();
assert
.
isTrue
(
wrapper
.
find
(
'VersionInfo'
).
exists
());
assert
.
isTrue
(
activePanel
.
find
(
'VersionInfo'
).
exists
());
assert
.
equal
(
assert
.
equal
(
wrapper
.
find
(
'VersionInfo'
).
prop
(
'versionData'
),
activePanel
.
find
(
'VersionInfo'
).
prop
(
'versionData'
),
fakeVersionData
,
fakeVersionData
,
);
);
assert
.
isFalse
(
wrapper
.
find
(
'Tutorial'
).
exists
());
assert
.
isFalse
(
activePanel
.
find
(
'Tutorial'
).
exists
());
});
});
});
});
...
@@ -137,46 +139,18 @@ describe('HelpPanel', () => {
...
@@ -137,46 +139,18 @@ describe('HelpPanel', () => {
]);
]);
});
});
it
(
'should show navigation link back to tutorial sub-panel'
,
()
=>
{
it
(
'should switch to tutorial sub-panel when first tab is clicked'
,
()
=>
{
const
wrapper
=
createComponent
();
act
(()
=>
{
wrapper
.
find
(
'LinkButton'
)
.
getDOMNode
()
.
dispatchEvent
(
new
Event
(
'click'
));
});
wrapper
.
update
();
const
link
=
wrapper
.
find
(
'LinkButton'
);
assert
.
isTrue
(
wrapper
.
find
(
'VersionInfo'
).
exists
());
assert
.
isFalse
(
wrapper
.
find
(
'Tutorial'
).
exists
());
assert
.
include
(
link
.
text
(),
'Getting started'
);
});
it
(
'should switch to tutorial sub-panel when link clicked'
,
()
=>
{
const
wrapper
=
createComponent
();
const
wrapper
=
createComponent
();
// Click to get to VersionInfo sub-panel...
// Click to get to VersionInfo sub-panel...
act
(()
=>
{
clickTab
(
wrapper
,
'version-info-tab'
);
wrapper
.
find
(
'LinkButton'
)
.
getDOMNode
()
.
dispatchEvent
(
new
Event
(
'click'
));
});
wrapper
.
update
();
// Click again to get back to tutorial sub-panel
// Click again to get back to tutorial sub-panel
act
(()
=>
{
clickTab
(
wrapper
,
'tutorial-tab'
);
wrapper
.
find
(
'LinkButton'
)
const
activePanel
=
getActivePanel
(
wrapper
);
.
getDOMNode
()
.
dispatchEvent
(
new
Event
(
'click'
));
});
wrapper
.
update
();
assert
.
isFalse
(
wrapper
.
find
(
'VersionInfo'
).
exists
());
assert
.
isFalse
(
activePanel
.
find
(
'VersionInfo'
).
exists
());
assert
.
isTrue
(
wrapper
.
find
(
'Tutorial'
).
exists
());
assert
.
isTrue
(
activePanel
.
find
(
'Tutorial'
).
exists
());
});
});
});
});
...
@@ -272,7 +246,7 @@ describe('HelpPanel', () => {
...
@@ -272,7 +246,7 @@ describe('HelpPanel', () => {
it
(
it
(
'should pass a11y checks'
,
'should pass a11y checks'
,
checkAccessibility
({
checkAccessibility
({
content
:
()
=>
createComponent
()
,
content
:
()
=>
<
HelpPanel
session
=
{
fakeSessionService
}
/>
,
}),
}),
);
);
});
});
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