From e945146ccd8baf7aca5f7a1e109b23463993bce2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Aug 2022 15:31:44 +0100 Subject: [PATCH 1/3] Move the UserMenu out of the SpacePanel ul list --- res/css/structures/_SpacePanel.pcss | 4 -- src/@types/common.ts | 16 +++++++- .../structures/AutoHideScrollbar.tsx | 38 ++++++++++--------- .../views/elements/AccessibleButton.tsx | 17 +-------- src/components/views/spaces/SpacePanel.tsx | 11 +++--- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/res/css/structures/_SpacePanel.pcss b/res/css/structures/_SpacePanel.pcss index 7fdb2500b45..72dbddf75e1 100644 --- a/res/css/structures/_SpacePanel.pcss +++ b/res/css/structures/_SpacePanel.pcss @@ -78,10 +78,6 @@ $activeBorderColor: $primary-content; margin: 0; list-style: none; padding: 0; - - > .mx_SpaceItem { - padding-left: 16px; - } } .mx_SpaceButton_toggleCollapse { diff --git a/src/@types/common.ts b/src/@types/common.ts index b4d01a75a54..d20007c7ec1 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { JSXElementConstructor } from "react"; +import React, { HTMLAttributes, InputHTMLAttributes, JSXElementConstructor } from "react"; // Based on https://stackoverflow.com/a/53229857/3532235 export type Without = {[P in Exclude]?: never}; @@ -49,3 +49,17 @@ export type KeysWithObjectShape = { ? (Input[P] extends Array ? never : P) : never; }[keyof Input]; + +/** + * This type construct allows us to specifically pass those props down to the element we’re creating that the element + * actually supports. + * + * e.g., if element is set to "a", we’ll support href and target, if it’s set to "input", we support type. + * + * To remain compatible with existing code, we’ll continue to support InputHTMLAttributes + */ +export type DynamicHtmlElementProps = + JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; +export type DynamicElementProps = + Partial> + & Omit, 'onClick'>; diff --git a/src/components/structures/AutoHideScrollbar.tsx b/src/components/structures/AutoHideScrollbar.tsx index d2134c33fef..f7e84c40d5e 100644 --- a/src/components/structures/AutoHideScrollbar.tsx +++ b/src/components/structures/AutoHideScrollbar.tsx @@ -15,18 +15,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { HTMLAttributes, WheelEvent } from "react"; +import classNames from "classnames"; +import React, { ReactHTML, WheelEvent } from "react"; -interface IProps extends Omit, "onScroll"> { +import { DynamicHtmlElementProps } from "../../@types/common"; + +type IProps = DynamicHtmlElementProps & { + element?: T; className?: string; onScroll?: (event: Event) => void; onWheel?: (event: WheelEvent) => void; style?: React.CSSProperties; tabIndex?: number; wrappedRef?: (ref: HTMLDivElement) => void; -} +}; + +export default class AutoHideScrollbar extends React.Component> { + static defaultProps = { + element: 'div' as keyof ReactHTML, + }; -export default class AutoHideScrollbar extends React.Component { public readonly containerRef: React.RefObject = React.createRef(); public componentDidMount() { @@ -36,9 +44,7 @@ export default class AutoHideScrollbar extends React.Component { this.containerRef.current.addEventListener("scroll", this.props.onScroll, { passive: true }); } - if (this.props.wrappedRef) { - this.props.wrappedRef(this.containerRef.current); - } + this.props.wrappedRef?.(this.containerRef.current); } public componentWillUnmount() { @@ -49,19 +55,15 @@ export default class AutoHideScrollbar extends React.Component { public render() { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { className, onScroll, onWheel, style, tabIndex, wrappedRef, children, ...otherProps } = this.props; + const { element, className, onScroll, tabIndex, wrappedRef, children, ...otherProps } = this.props; - return (
- { children } -
); + tabIndex: tabIndex ?? -1, + }, children); } } diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index f54a8d4bff5..9ada938f2ac 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -14,11 +14,12 @@ limitations under the License. */ -import React, { HTMLAttributes, InputHTMLAttributes, ReactHTML, ReactNode } from 'react'; +import React, { ReactHTML, ReactNode } from 'react'; import classnames from 'classnames'; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; +import { DynamicHtmlElementProps } from "../../../@types/common"; export type ButtonEvent = React.MouseEvent | React.KeyboardEvent | React.FormEvent; @@ -35,20 +36,6 @@ type AccessibleButtonKind = | 'primary' | 'confirm_sm' | 'cancel_sm'; -/** - * This type construct allows us to specifically pass those props down to the element we’re creating that the element - * actually supports. - * - * e.g., if element is set to "a", we’ll support href and target, if it’s set to "input", we support type. - * - * To remain compatible with existing code, we’ll continue to support InputHTMLAttributes - */ -type DynamicHtmlElementProps = - JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; -type DynamicElementProps = - Partial> - & Omit, 'onClick'>; - /** * children: React's magic prop. Represents all children given to the element. * element: (optional) The base element type. "div" by default. diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index e1f62445f74..b47f15f581b 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -282,6 +282,9 @@ const InnerSpacePanel = React.memo(({ style={isDraggingOver ? { pointerEvents: "none", } : undefined} + element="ul" + role="tree" + aria-label={_t("Spaces")} > { metaSpacesSection } { invites.map(s => ( @@ -321,7 +324,7 @@ const InnerSpacePanel = React.memo(({ const SpacePanel = () => { const [isPanelCollapsed, setPanelCollapsed] = useState(true); - const ref = useRef(); + const ref = useRef(); useLayoutEffect(() => { UIStore.instance.trackElementDimensions("SpacePanel", ref.current); return () => UIStore.instance.stopTrackingElementDimensions("SpacePanel"); @@ -340,11 +343,9 @@ const SpacePanel = () => { }}> { ({ onKeyDownHandler }) => ( -
    @@ -381,7 +382,7 @@ const SpacePanel = () => { -
+ ) }
From 914e7feef705143bf0fd859caf9197398d8cadab Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 9 Aug 2022 15:35:11 +0100 Subject: [PATCH 2/3] Apply aria-selected to the spacepanel treeview --- src/components/views/spaces/SpacePanel.tsx | 1 + src/components/views/spaces/SpaceTreeLevel.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index b47f15f581b..129e6f3584e 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -132,6 +132,7 @@ const MetaSpaceButton = ({ selected, isPanelCollapsed, ...props }: IMetaSpaceBut "collapsed": isPanelCollapsed, })} role="treeitem" + aria-selected={selected} > ; diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index 80d678d3b61..cab7bc3c76b 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -315,6 +315,7 @@ export class SpaceItem extends React.PureComponent { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { tabIndex, ...restDragHandleProps } = dragHandleProps || {}; + const selected = activeSpaces.includes(space.roomId); return (
  • { className={itemClasses} ref={innerRef} aria-expanded={hasChildren ? !collapsed : undefined} + aria-selected={selected} role="treeitem" > Date: Wed, 10 Aug 2022 07:41:49 +0100 Subject: [PATCH 3/3] Fix typing --- src/@types/common.ts | 16 +--------------- .../structures/AutoHideScrollbar.tsx | 8 +++++--- .../structures/IndicatorScrollbar.tsx | 19 ++++++++++--------- .../dialogs/AddExistingToSpaceDialog.tsx | 2 +- .../views/elements/AccessibleButton.tsx | 17 +++++++++++++++-- .../views/emojipicker/EmojiPicker.tsx | 2 +- 6 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/@types/common.ts b/src/@types/common.ts index d20007c7ec1..b4d01a75a54 100644 --- a/src/@types/common.ts +++ b/src/@types/common.ts @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { HTMLAttributes, InputHTMLAttributes, JSXElementConstructor } from "react"; +import React, { JSXElementConstructor } from "react"; // Based on https://stackoverflow.com/a/53229857/3532235 export type Without = {[P in Exclude]?: never}; @@ -49,17 +49,3 @@ export type KeysWithObjectShape = { ? (Input[P] extends Array ? never : P) : never; }[keyof Input]; - -/** - * This type construct allows us to specifically pass those props down to the element we’re creating that the element - * actually supports. - * - * e.g., if element is set to "a", we’ll support href and target, if it’s set to "input", we support type. - * - * To remain compatible with existing code, we’ll continue to support InputHTMLAttributes - */ -export type DynamicHtmlElementProps = - JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; -export type DynamicElementProps = - Partial> - & Omit, 'onClick'>; diff --git a/src/components/structures/AutoHideScrollbar.tsx b/src/components/structures/AutoHideScrollbar.tsx index f7e84c40d5e..9a6f5b26a0a 100644 --- a/src/components/structures/AutoHideScrollbar.tsx +++ b/src/components/structures/AutoHideScrollbar.tsx @@ -16,11 +16,13 @@ limitations under the License. */ import classNames from "classnames"; -import React, { ReactHTML, WheelEvent } from "react"; +import React, { HTMLAttributes, ReactHTML, WheelEvent } from "react"; -import { DynamicHtmlElementProps } from "../../@types/common"; +type DynamicHtmlElementProps = + JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; +type DynamicElementProps = Partial>; -type IProps = DynamicHtmlElementProps & { +export type IProps = DynamicHtmlElementProps & { element?: T; className?: string; onScroll?: (event: Event) => void; diff --git a/src/components/structures/IndicatorScrollbar.tsx b/src/components/structures/IndicatorScrollbar.tsx index 4b122345b32..ea876f9ae29 100644 --- a/src/components/structures/IndicatorScrollbar.tsx +++ b/src/components/structures/IndicatorScrollbar.tsx @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ComponentProps, createRef } from "react"; +import React, { createRef } from "react"; -import AutoHideScrollbar from "./AutoHideScrollbar"; +import AutoHideScrollbar, { IProps as AutoHideScrollbarProps } from "./AutoHideScrollbar"; import UIStore, { UI_EVENTS } from "../../stores/UIStore"; -interface IProps extends Omit, "onWheel"> { +export type IProps = Omit, "onWheel"> & { // If true, the scrollbar will append mx_IndicatorScrollbar_leftOverflowIndicator // and mx_IndicatorScrollbar_rightOverflowIndicator elements to the list for positioning // by the parent element. @@ -31,21 +31,22 @@ interface IProps extends Omit, "onWheel verticalScrollsHorizontally?: boolean; children: React.ReactNode; - className: string; -} +}; interface IState { leftIndicatorOffset: string; rightIndicatorOffset: string; } -export default class IndicatorScrollbar extends React.Component { - private autoHideScrollbar = createRef(); +export default class IndicatorScrollbar< + T extends keyof JSX.IntrinsicElements, +> extends React.Component, IState> { + private autoHideScrollbar = createRef>(); private scrollElement: HTMLDivElement; private likelyTrackpadUser: boolean = null; private checkAgainForTrackpad = 0; // ts in milliseconds to recheck this._likelyTrackpadUser - constructor(props: IProps) { + constructor(props: IProps) { super(props); this.state = { @@ -65,7 +66,7 @@ export default class IndicatorScrollbar extends React.Component } }; - public componentDidUpdate(prevProps: IProps): void { + public componentDidUpdate(prevProps: IProps): void { const prevLen = React.Children.count(prevProps.children); const curLen = React.Children.count(this.props.children); // check overflow only if amount of children changes. diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index 606f96553d7..6251faee41f 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -130,7 +130,7 @@ export const AddExistingToSpace: React.FC = ({ const cli = useContext(MatrixClientContext); const visibleRooms = useMemo(() => cli.getVisibleRooms().filter(r => r.getMyMembership() === "join"), [cli]); - const scrollRef = useRef(); + const scrollRef = useRef>(); const [scrollState, setScrollState] = useState({ // these are estimates which update as soon as it mounts scrollTop: 0, diff --git a/src/components/views/elements/AccessibleButton.tsx b/src/components/views/elements/AccessibleButton.tsx index 9ada938f2ac..f54a8d4bff5 100644 --- a/src/components/views/elements/AccessibleButton.tsx +++ b/src/components/views/elements/AccessibleButton.tsx @@ -14,12 +14,11 @@ limitations under the License. */ -import React, { ReactHTML, ReactNode } from 'react'; +import React, { HTMLAttributes, InputHTMLAttributes, ReactHTML, ReactNode } from 'react'; import classnames from 'classnames'; import { getKeyBindingsManager } from "../../../KeyBindingsManager"; import { KeyBindingAction } from "../../../accessibility/KeyboardShortcuts"; -import { DynamicHtmlElementProps } from "../../../@types/common"; export type ButtonEvent = React.MouseEvent | React.KeyboardEvent | React.FormEvent; @@ -36,6 +35,20 @@ type AccessibleButtonKind = | 'primary' | 'confirm_sm' | 'cancel_sm'; +/** + * This type construct allows us to specifically pass those props down to the element we’re creating that the element + * actually supports. + * + * e.g., if element is set to "a", we’ll support href and target, if it’s set to "input", we support type. + * + * To remain compatible with existing code, we’ll continue to support InputHTMLAttributes + */ +type DynamicHtmlElementProps = + JSX.IntrinsicElements[T] extends HTMLAttributes<{}> ? DynamicElementProps : DynamicElementProps<"div">; +type DynamicElementProps = + Partial> + & Omit, 'onClick'>; + /** * children: React's magic prop. Represents all children given to the element. * element: (optional) The base element type. "div" by default. diff --git a/src/components/views/emojipicker/EmojiPicker.tsx b/src/components/views/emojipicker/EmojiPicker.tsx index e32f20b78b6..c178084826a 100644 --- a/src/components/views/emojipicker/EmojiPicker.tsx +++ b/src/components/views/emojipicker/EmojiPicker.tsx @@ -55,7 +55,7 @@ class EmojiPicker extends React.Component { private readonly memoizedDataByCategory: Record; private readonly categories: ICategory[]; - private scrollRef = React.createRef(); + private scrollRef = React.createRef>(); constructor(props: IProps) { super(props);