diff --git a/src/accessibility/context_menu/ContextMenuButton.tsx b/src/accessibility/context_menu/ContextMenuButton.tsx
new file mode 100644
index 0000000000..c358155e10
--- /dev/null
+++ b/src/accessibility/context_menu/ContextMenuButton.tsx
@@ -0,0 +1,51 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+import AccessibleButton, {IProps as IAccessibleButtonProps} from "../../components/views/elements/AccessibleButton";
+
+interface IProps extends IAccessibleButtonProps {
+ label?: string;
+ // whether or not the context menu is currently open
+ isExpanded: boolean;
+}
+
+// Semantic component for representing the AccessibleButton which launches a
+export const ContextMenuButton: React.FC = ({
+ label,
+ isExpanded,
+ children,
+ onClick,
+ onContextMenu,
+ ...props
+}) => {
+ return (
+
+ { children }
+
+ );
+};
diff --git a/src/accessibility/context_menu/MenuGroup.tsx b/src/accessibility/context_menu/MenuGroup.tsx
new file mode 100644
index 0000000000..f4b7b6bc56
--- /dev/null
+++ b/src/accessibility/context_menu/MenuGroup.tsx
@@ -0,0 +1,31 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+interface IProps extends React.HTMLAttributes {
+ label: string;
+ className?: string;
+}
+
+// Semantic component for representing a role=group for grouping menu radios/checkboxes
+export const MenuGroup: React.FC = ({children, label, ...props}) => {
+ return
+ { children }
+
;
+};
diff --git a/src/accessibility/context_menu/MenuItem.tsx b/src/accessibility/context_menu/MenuItem.tsx
new file mode 100644
index 0000000000..8e33d55de4
--- /dev/null
+++ b/src/accessibility/context_menu/MenuItem.tsx
@@ -0,0 +1,36 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+import AccessibleButton, {ButtonEvent, IProps as IAccessibleButtonProps} from "../../components/views/elements/AccessibleButton";
+
+interface IProps extends IAccessibleButtonProps {
+ label?: string;
+ className?: string;
+ onClick(ev: ButtonEvent);
+}
+
+// Semantic component for representing a role=menuitem
+export const MenuItem: React.FC = ({children, label, ...props}) => {
+ return (
+
+ { children }
+
+ );
+};
diff --git a/src/accessibility/context_menu/MenuItemCheckbox.tsx b/src/accessibility/context_menu/MenuItemCheckbox.tsx
new file mode 100644
index 0000000000..e2cc04b5a6
--- /dev/null
+++ b/src/accessibility/context_menu/MenuItemCheckbox.tsx
@@ -0,0 +1,45 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+import AccessibleButton, {ButtonEvent, IProps as IAccessibleButtonProps} from "../../components/views/elements/AccessibleButton";
+
+interface IProps extends IAccessibleButtonProps {
+ label?: string;
+ active: boolean;
+ disabled?: boolean;
+ className?: string;
+ onClick(ev: ButtonEvent);
+}
+
+// Semantic component for representing a role=menuitemcheckbox
+export const MenuItemCheckbox: React.FC = ({children, label, active, disabled, ...props}) => {
+ return (
+
+ { children }
+
+ );
+};
diff --git a/src/accessibility/context_menu/MenuItemRadio.tsx b/src/accessibility/context_menu/MenuItemRadio.tsx
new file mode 100644
index 0000000000..21732220df
--- /dev/null
+++ b/src/accessibility/context_menu/MenuItemRadio.tsx
@@ -0,0 +1,45 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+import AccessibleButton, {ButtonEvent, IProps as IAccessibleButtonProps} from "../../components/views/elements/AccessibleButton";
+
+interface IProps extends IAccessibleButtonProps {
+ label?: string;
+ active: boolean;
+ disabled?: boolean;
+ className?: string;
+ onClick(ev: ButtonEvent);
+}
+
+// Semantic component for representing a role=menuitemradio
+export const MenuItemRadio: React.FC = ({children, label, active, disabled, ...props}) => {
+ return (
+
+ { children }
+
+ );
+};
diff --git a/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx b/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx
new file mode 100644
index 0000000000..f5a510f517
--- /dev/null
+++ b/src/accessibility/context_menu/StyledMenuItemCheckbox.tsx
@@ -0,0 +1,64 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+import {Key} from "../../Keyboard";
+import StyledCheckbox from "../../components/views/elements/StyledCheckbox";
+
+interface IProps extends React.ComponentProps {
+ label?: string;
+ onChange();
+ onClose(): void; // gets called after onChange on Key.ENTER
+}
+
+// Semantic component for representing a styled role=menuitemcheckbox
+export const StyledMenuItemCheckbox: React.FC = ({children, label, onChange, onClose, ...props}) => {
+ const onKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === Key.ENTER || e.key === Key.SPACE) {
+ e.stopPropagation();
+ e.preventDefault();
+ onChange();
+ // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12
+ if (e.key === Key.ENTER) {
+ onClose();
+ }
+ }
+ };
+ const onKeyUp = (e: React.KeyboardEvent) => {
+ // prevent the input default handler as we handle it on keydown to match
+ // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html
+ if (e.key === Key.SPACE || e.key === Key.ENTER) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ };
+ return (
+
+ { children }
+
+ );
+};
diff --git a/src/accessibility/context_menu/StyledMenuItemRadio.tsx b/src/accessibility/context_menu/StyledMenuItemRadio.tsx
new file mode 100644
index 0000000000..be87ccc683
--- /dev/null
+++ b/src/accessibility/context_menu/StyledMenuItemRadio.tsx
@@ -0,0 +1,65 @@
+/*
+Copyright 2015, 2016 OpenMarket Ltd
+Copyright 2018 New Vector Ltd
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import React from "react";
+
+import {Key} from "../../Keyboard";
+import StyledRadioButton from "../../components/views/elements/StyledRadioButton";
+
+interface IProps extends React.ComponentProps {
+ label?: string;
+ disabled?: boolean;
+ onChange(): void;
+ onClose(): void; // gets called after onChange on Key.ENTER
+}
+
+// Semantic component for representing a styled role=menuitemradio
+export const StyledMenuItemRadio: React.FC = ({children, label, onChange, onClose, ...props}) => {
+ const onKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === Key.ENTER || e.key === Key.SPACE) {
+ e.stopPropagation();
+ e.preventDefault();
+ onChange();
+ // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12
+ if (e.key === Key.ENTER) {
+ onClose();
+ }
+ }
+ };
+ const onKeyUp = (e: React.KeyboardEvent) => {
+ // prevent the input default handler as we handle it on keydown to match
+ // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html
+ if (e.key === Key.SPACE || e.key === Key.ENTER) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ };
+ return (
+
+ { children }
+
+ );
+};
diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx
index 872a8b0cd9..cb1349da4b 100644
--- a/src/components/structures/ContextMenu.tsx
+++ b/src/components/structures/ContextMenu.tsx
@@ -21,10 +21,7 @@ import ReactDOM from "react-dom";
import classNames from "classnames";
import {Key} from "../../Keyboard";
-import AccessibleButton, { IProps as IAccessibleButtonProps, ButtonEvent } from "../views/elements/AccessibleButton";
import {Writeable} from "../../@types/common";
-import StyledCheckbox from "../views/elements/StyledCheckbox";
-import StyledRadioButton from "../views/elements/StyledRadioButton";
// Shamelessly ripped off Modal.js. There's probably a better way
// of doing reusable widgets like dialog boxes & menus where we go and
@@ -390,187 +387,6 @@ export class ContextMenu extends React.PureComponent {
}
}
-interface IContextMenuButtonProps extends IAccessibleButtonProps {
- label?: string;
- // whether or not the context menu is currently open
- isExpanded: boolean;
-}
-
-// Semantic component for representing the AccessibleButton which launches a
-export const ContextMenuButton: React.FC = ({ label, isExpanded, children, onClick, onContextMenu, ...props }) => {
- return (
-
- { children }
-
- );
-};
-
-interface IMenuItemProps extends IAccessibleButtonProps {
- label?: string;
- className?: string;
- onClick(ev: ButtonEvent);
-}
-
-// Semantic component for representing a role=menuitem
-export const MenuItem: React.FC = ({children, label, ...props}) => {
- return (
-
- { children }
-
- );
-};
-
-interface IMenuGroupProps extends React.HTMLAttributes {
- label: string;
- className?: string;
-}
-
-// Semantic component for representing a role=group for grouping menu radios/checkboxes
-export const MenuGroup: React.FC = ({children, label, ...props}) => {
- return
- { children }
-
;
-};
-
-interface IMenuItemCheckboxProps extends IAccessibleButtonProps {
- label?: string;
- active: boolean;
- disabled?: boolean;
- className?: string;
- onClick(ev: ButtonEvent);
-}
-
-// Semantic component for representing a role=menuitemcheckbox
-export const MenuItemCheckbox: React.FC = ({children, label, active = false, disabled = false, ...props}) => {
- return (
-
- { children }
-
- );
-};
-
-interface IStyledMenuItemCheckboxProps extends IAccessibleButtonProps {
- label?: string;
- active: boolean;
- disabled?: boolean;
- className?: string;
- onChange();
- onClose(): void; // gets called after onChange on Key.ENTER
-}
-
-// Semantic component for representing a styled role=menuitemcheckbox
-export const StyledMenuItemCheckbox: React.FC = ({children, label, onChange, onClose, checked, disabled=false, ...props}) => {
- const onKeyDown = (e) => {
- if (e.key === Key.ENTER || e.key === Key.SPACE) {
- e.stopPropagation();
- e.preventDefault();
- onChange();
- // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12
- if (e.key === Key.ENTER) {
- onClose();
- }
- }
- };
- const onKeyUp = (e) => {
- // prevent the input default handler as we handle it on keydown to match
- // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html
- if (e.key === Key.SPACE || e.key === Key.ENTER) {
- e.stopPropagation();
- e.preventDefault();
- }
- };
- return (
-
- { children }
-
- );
-};
-
-interface IMenuItemRadioProps extends IAccessibleButtonProps {
- label?: string;
- active: boolean;
- disabled?: boolean;
- className?: string;
- onClick(ev: ButtonEvent);
-}
-
-// Semantic component for representing a role=menuitemradio
-export const MenuItemRadio: React.FC = ({children, label, active = false, disabled = false, ...props}) => {
- return (
-
- { children }
-
- );
-};
-
-
-interface IStyledMenuItemRadioProps extends IAccessibleButtonProps {
- label?: string;
- active: boolean;
- disabled?: boolean;
- className?: string;
- onChange();
- onClose(): void; // gets called after onChange on Key.ENTER
-}
-
-// Semantic component for representing a styled role=menuitemradio
-export const StyledMenuItemRadio: React.FC = ({children, label, onChange, onClose, checked=false, disabled=false, ...props}) => {
- const onKeyDown = (e) => {
- if (e.key === Key.ENTER || e.key === Key.SPACE) {
- e.stopPropagation();
- e.preventDefault();
- onChange();
- // Implements https://www.w3.org/TR/wai-aria-practices/#keyboard-interaction-12
- if (e.key === Key.ENTER) {
- onClose();
- }
- }
- };
- const onKeyUp = (e) => {
- // prevent the input default handler as we handle it on keydown to match
- // https://www.w3.org/TR/wai-aria-practices/examples/menubar/menubar-2/menubar-2.html
- if (e.key === Key.SPACE || e.key === Key.ENTER) {
- e.stopPropagation();
- e.preventDefault();
- }
- };
- return (
-
- { children }
-
- );
-};
-
// Placement method for to position context menu to right of elementRect with chevronOffset
export const toRightOf = (elementRect: DOMRect, chevronOffset = 12) => {
const left = elementRect.right + window.pageXOffset + 3;
@@ -639,3 +455,12 @@ export function createMenu(ElementClass, props) {
return {close: onFinished};
}
+
+// re-export the semantic helper components for simplicity
+export {ContextMenuButton} from "../../accessibility/context_menu/ContextMenuButton";
+export {MenuGroup} from "../../accessibility/context_menu/MenuGroup";
+export {MenuItem} from "../../accessibility/context_menu/MenuItem";
+export {MenuItemCheckbox} from "../../accessibility/context_menu/MenuItemCheckbox";
+export {MenuItemRadio} from "../../accessibility/context_menu/MenuItemRadio";
+export {StyledMenuItemCheckbox} from "../../accessibility/context_menu/StyledMenuItemCheckbox";
+export {StyledMenuItemRadio} from "../../accessibility/context_menu/StyledMenuItemRadio";