fix: Keyboard accessible context menus (#1768)

- Makes menus fully accessible and keyboard driven
- Currently adds 2.8% to initial bundle size due to the inclusion of Reakit and its dependency, popperjs.
- Converts all menus to functional components
- Remove old custom menu system
- Various layout and flow improvements around the menus

closes #1766
This commit is contained in:
Tom Moor
2021-01-13 22:00:25 -08:00
committed by GitHub
parent 47369dd968
commit e8b7782f5e
54 changed files with 1788 additions and 1881 deletions

View File

@ -1,53 +1,32 @@
// @flow
import { observable } from "mobx";
import { observer, inject } from "mobx-react";
import { observer } from "mobx-react";
import * as React from "react";
import { Trans, withTranslation, type TFunction } from "react-i18next";
import { Redirect } from "react-router-dom";
import CollectionsStore from "stores/CollectionsStore";
import { useTranslation, Trans } from "react-i18next";
import { useMenuState, MenuButton } from "reakit/Menu";
import Document from "models/Document";
import { DropdownMenu } from "components/DropdownMenu";
import DropdownMenuItems from "components/DropdownMenu/DropdownMenuItems";
import ContextMenu from "components/ContextMenu";
import Template from "components/ContextMenu/Template";
import useStores from "hooks/useStores";
import { newDocumentUrl } from "utils/routeHelpers";
type Props = {
label?: React.Node,
label?: (any) => React.Node,
document: Document,
collections: CollectionsStore,
t: TFunction,
};
@observer
class NewChildDocumentMenu extends React.Component<Props> {
@observable redirectTo: ?string;
function NewChildDocumentMenu({ document, label }: Props) {
const menu = useMenuState({ modal: true });
const { collections } = useStores();
const { t } = useTranslation();
const collection = collections.get(document.collectionId);
const collectionName = collection ? collection.name : t("collection");
componentDidUpdate() {
this.redirectTo = undefined;
}
handleNewDocument = () => {
const { document } = this.props;
this.redirectTo = newDocumentUrl(document.collectionId);
};
handleNewChild = () => {
const { document } = this.props;
this.redirectTo = newDocumentUrl(document.collectionId, {
parentDocumentId: document.id,
});
};
render() {
if (this.redirectTo) return <Redirect to={this.redirectTo} push />;
const { label, document, collections, t } = this.props;
const collection = collections.get(document.collectionId);
const collectionName = collection ? collection.name : t("collection");
return (
<DropdownMenu label={label}>
<DropdownMenuItems
return (
<>
<MenuButton {...menu}>{label}</MenuButton>
<ContextMenu {...menu} aria-label={t("New child document")}>
<Template
{...menu}
items={[
{
title: (
@ -57,19 +36,19 @@ class NewChildDocumentMenu extends React.Component<Props> {
</Trans>
</span>
),
onClick: this.handleNewDocument,
to: newDocumentUrl(document.collectionId),
},
{
title: t("New nested document"),
onClick: this.handleNewChild,
to: newDocumentUrl(document.collectionId, {
parentDocumentId: document.id,
}),
},
]}
/>
</DropdownMenu>
);
}
</ContextMenu>
</>
);
}
export default withTranslation()<NewChildDocumentMenu>(
inject("collections")(NewChildDocumentMenu)
);
export default observer(NewChildDocumentMenu);