Skip to content

Commit 0226fc2

Browse files
committed
[B] Update to a disclosure pattern similar to the header nested navigation menus
- Updates search, visibility, and appearance menus to a nested disclosure pattern
1 parent e6eba15 commit 0226fc2

File tree

14 files changed

+184
-72
lines changed

14 files changed

+184
-72
lines changed

client/src/frontend/components/collecting/Toggle/index.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { useState, useEffect, useMemo } from "react";
22
import PropTypes from "prop-types";
3-
import { useTranslation } from "react-i18next";
43
import classNames from "classnames";
54
import { collectingAPI, requests } from "api";
65
import { useDispatch } from "react-redux";
@@ -58,8 +57,6 @@ function CollectingToggle({
5857
const [isCollecting, setIsCollecting] = useState(false);
5958
const [dialogVisible, setDialogVisible] = useState(false);
6059

61-
const { t } = useTranslation();
62-
6360
const myCollectableReadingGroups = useMemo(() => {
6461
if (!Array.isArray(myReadingGroups)) return [];
6562
return myReadingGroups.filter(rg => rg?.attributes?.abilities?.update);

client/src/global/components/UIPanel/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default function UIPanel(props) {
2323
escapeDeactivates: e => props.hidePanel(e)
2424
}}
2525
>
26-
<Styled.Panel inert={!visible ? "" : undefined}>
26+
<Styled.Panel id={props.id} inert={!visible ? "" : undefined}>
2727
{React.createElement(props.bodyComponent, {
2828
...props,
2929
closeCallback: props.hidePanel,

client/src/global/components/UserMenuButton/index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ const UserMenuButton = forwardRef(
3535
{...(authenticated
3636
? props
3737
: {
38-
"aria-expanded": props["aria-expanded"],
39-
"aria-haspopup": "dialog"
38+
"aria-expanded": props["aria-expanded"]
4039
})}
4140
>
4241
<span className="screen-reader-text">

client/src/global/components/atomic/DisclosureNavigationMenu/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ DisclosureNavigationMenu.displayName = "Global.Atomic.DisclosureNavigationMenu";
3131
DisclosureNavigationMenu.propTypes = {
3232
disclosure: PropTypes.node.isRequired,
3333
children: PropTypes.oneOfType([PropTypes.node, PropTypes.string]).isRequired,
34-
visible: PropTypes.bool
34+
visible: PropTypes.bool,
35+
onBlur: PropTypes.func
3536
};
3637

3738
export default DisclosureNavigationMenu;

client/src/global/components/search/menu/Body.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ export class SearchMenuBody extends PureComponent {
99
toggleVisibility: PropTypes.func.isRequired,
1010
match: PropTypes.object.isRequired,
1111
history: PropTypes.object.isRequired,
12-
visibility: PropTypes.object.isRequired,
1312
searchType: PropTypes.string.isRequired,
1413
onSubmit: PropTypes.func,
1514
facets: PropTypes.array,

client/src/reader/components/Header/index.js

Lines changed: 67 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -166,32 +166,81 @@ export default function Header(props) {
166166
icon="notes24"
167167
label={t("glossary.note_title_case_other")}
168168
active={visibility.uiPanels.notes}
169+
ariaHasPopup="dialog"
170+
ariaControls="notes"
169171
/>
170172
</li>
171173
</Authorize>
172174
<li className="reader-header__nav-item">
173-
<ControlMenu.Button
174-
onClick={panelToggleHandler("visibility")}
175-
icon="eyeball24"
176-
label={t("common.visibility_title_case")}
177-
active={visibility.uiPanels.visibility}
178-
/>
175+
<DisclosureNavigationMenu
176+
visible={visibility.uiPanels.visibility}
177+
disclosure={
178+
<ControlMenu.DisclosureButton
179+
icon="eyeball24"
180+
label={t("common.visibility_title_case")}
181+
/>
182+
}
183+
>
184+
<ControlMenu.DisclosurePanel direction="right">
185+
<ControlMenu.VisibilityMenuBody
186+
className="panel"
187+
filter={visibility.visibilityFilters}
188+
filterChangeHandler={handleVisibilityFilterChange}
189+
/>
190+
</ControlMenu.DisclosurePanel>
191+
</DisclosureNavigationMenu>
179192
</li>
180193
<li className="reader-header__nav-item">
181-
<ControlMenu.Button
182-
onClick={panelToggleHandler("appearance")}
183-
icon="text24"
184-
label={t("reader.header.reader_appearance")}
185-
active={visibility.uiPanels.appearance}
186-
/>
194+
<DisclosureNavigationMenu
195+
visible={visibility.uiPanels.appearance}
196+
disclosure={
197+
<ControlMenu.DisclosureButton
198+
icon="text24"
199+
label={t("reader.header.reader_appearance")}
200+
/>
201+
}
202+
>
203+
<ControlMenu.DisclosurePanel direction="right">
204+
<ControlMenu.AppearanceMenuBody
205+
// Props required by body component
206+
appearance={appearance}
207+
selectFont={selectFont}
208+
setColorScheme={setColorScheme}
209+
incrementFontSize={incrementFontSize}
210+
decrementFontSize={decrementFontSize}
211+
incrementMargins={incrementMargins}
212+
decrementMargins={decrementMargins}
213+
resetTypography={resetTypography}
214+
className="panel"
215+
/>
216+
</ControlMenu.DisclosurePanel>
217+
</DisclosureNavigationMenu>
187218
</li>
188219
<li className="reader-header__nav-item">
189-
<SearchMenu.Button
190-
toggleSearchMenu={panelToggleHandler("search")}
191-
active={visibility.uiPanels.search}
192-
className="reader-header__button reader-header__button--pad-narrow"
193-
iconSize={32}
194-
/>
220+
<DisclosureNavigationMenu
221+
visible={visibility.uiPanels.search}
222+
disclosure={
223+
<ControlMenu.DisclosureButton
224+
icon="search24"
225+
label={t("search.title")}
226+
/>
227+
}
228+
>
229+
<ControlMenu.DisclosurePanel direction="right">
230+
<SearchMenu.Body
231+
toggleVisibility={panelToggleHandler("search")}
232+
initialState={{
233+
keyword: "",
234+
scope: "text"
235+
}}
236+
projectId={projectId}
237+
textId={textId}
238+
sectionId={sectionId}
239+
searchType="reader"
240+
className="panel search-menu"
241+
/>
242+
</ControlMenu.DisclosurePanel>
243+
</DisclosureNavigationMenu>
195244
</li>
196245
<li className="reader-header__nav-item">
197246
<DisclosureNavigationMenu
@@ -287,45 +336,6 @@ export default function Header(props) {
287336
history={history}
288337
hidePanel={commonActions.hideNotesPanel}
289338
/>
290-
<UIPanel
291-
id="visibility"
292-
visibility={visibility.uiPanels}
293-
filter={visibility.visibilityFilters}
294-
filterChangeHandler={handleVisibilityFilterChange}
295-
bodyComponent={ControlMenu.VisibilityMenuBody}
296-
hidePanel={commonActions.hideVisibilityPanel}
297-
/>
298-
<UIPanel
299-
id="search"
300-
visibility={visibility.uiPanels}
301-
toggleVisibility={panelToggleHandler("search")}
302-
initialState={{
303-
keyword: "",
304-
scope: "text"
305-
}}
306-
projectId={projectId}
307-
textId={textId}
308-
sectionId={sectionId}
309-
searchType="reader"
310-
bodyComponent={SearchMenu.Body}
311-
bodyClassName="search-menu"
312-
hidePanel={commonActions.hideSearchPanel}
313-
/>
314-
<UIPanel
315-
id="appearance"
316-
visibility={visibility.uiPanels}
317-
bodyComponent={ControlMenu.AppearanceMenuBody}
318-
// Props required by body component
319-
appearance={appearance}
320-
selectFont={selectFont}
321-
setColorScheme={setColorScheme}
322-
incrementFontSize={incrementFontSize}
323-
decrementFontSize={decrementFontSize}
324-
incrementMargins={incrementMargins}
325-
decrementMargins={decrementMargins}
326-
resetTypography={resetTypography}
327-
hidePanel={commonActions.hideAppearancePanel}
328-
/>
329339
</div>
330340
</>
331341
)}

client/src/reader/components/control-menu/Button.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ export default class ControlMenuButton extends PureComponent {
1010
onClick: PropTypes.func.isRequired,
1111
icon: PropTypes.string.isRequired,
1212
label: PropTypes.string.isRequired,
13-
active: PropTypes.bool
13+
active: PropTypes.bool,
14+
ariaControls: PropTypes.string,
15+
ariaHasPopup: PropTypes.string
1416
};
1517

1618
handleClick = event => {
@@ -30,8 +32,11 @@ export default class ControlMenuButton extends PureComponent {
3032
className={buttonClass}
3133
onClick={this.handleClick}
3234
data-id={`toggle-${this.props.label}`}
33-
aria-haspopup
3435
aria-expanded={this.props.active}
36+
aria-controls={this.props.ariaControls}
37+
{...(this.props.ariaHasPopup
38+
? { "aria-haspopup": this.props.ariaHasPopup }
39+
: {})}
3540
>
3641
<Utility.IconComposer icon={this.props.icon} size={32} />
3742
<span className="screen-reader-text">{this.props.label}</span>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React, { forwardRef } from "react";
2+
import PropTypes from "prop-types";
3+
import classNames from "classnames";
4+
import Utility from "global/components/utility";
5+
6+
const ControlMenuDisclosureButton = (props, ref) => {
7+
const { label, visible, icon, ...rest } = props;
8+
9+
const buttonClass = classNames({
10+
"reader-header__button": true,
11+
"reader-header__button--pad-narrow": true,
12+
"button-active": visible
13+
});
14+
15+
return (
16+
<button
17+
ref={ref}
18+
className={buttonClass}
19+
data-id={`toggle-${label}`}
20+
aria-expanded={visible}
21+
{...rest}
22+
>
23+
<Utility.IconComposer icon={icon} size={32} />
24+
<span className="screen-reader-text">{label}</span>
25+
</button>
26+
);
27+
};
28+
29+
ControlMenuDisclosureButton.propTypes = {
30+
onClick: PropTypes.func.isRequired,
31+
icon: PropTypes.string.isRequired,
32+
label: PropTypes.string.isRequired,
33+
ariaControls: PropTypes.string,
34+
visible: PropTypes.bool
35+
};
36+
37+
ControlMenuDisclosureButton.displayName = "ControlMenu.Button";
38+
39+
export default forwardRef(ControlMenuDisclosureButton);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import React, { forwardRef } from "react";
2+
import PropTypes from "prop-types";
3+
import * as Styled from "./styles";
4+
5+
const ControlMenuDisclosurePanel = forwardRef((props, ref) => {
6+
const { id, visible, direction, children, ...rest } = props;
7+
return (
8+
<Styled.Panel
9+
id={id}
10+
ref={ref}
11+
{...(!visible ? { inert: "" } : {})}
12+
className={`reader-header__panels ${
13+
direction ? `reader-header__panels--${direction}` : ""
14+
}`}
15+
{...rest}
16+
>
17+
{children}
18+
</Styled.Panel>
19+
);
20+
});
21+
22+
ControlMenuDisclosurePanel.displayName = "ControlMenu.DisclosurePanel";
23+
24+
ControlMenuDisclosurePanel.propTypes = {
25+
id: PropTypes.string,
26+
children: PropTypes.node,
27+
visible: PropTypes.bool,
28+
direction: PropTypes.oneOf(["left", "right"])
29+
};
30+
31+
export default ControlMenuDisclosurePanel;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import DisclosurePanel from "./DisclosurePanel";
2+
3+
export default DisclosurePanel;

0 commit comments

Comments
 (0)