MM-64669 Fix keyboard navigation of settings sidebar (#32098)
* MM-64669 Fix keyboard navigation of settings sidebar and add Playwright test * MM-64669 Revert changes to Cypress test which masked keyboard bug The changes that were previously made caused Cypress to refocus the sidebar repeatedly which stopped the test from failing without fixing the bug. * Ensure focus highlight is always visible on sidebar tabs This may not have been broken by the changes that caused MM-64669, but I noticed it while I was in there and wanted to fix it. * Fix settings modal scrolling while changing sections using arrow keys * Remove accidentally-added field
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
62a4ce920e
Коммит
5ffb7607cc
@@ -3,7 +3,6 @@
|
||||
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import type {RefObject} from 'react';
|
||||
import {FormattedMessage} from 'react-intl';
|
||||
|
||||
import Constants from 'utils/constants';
|
||||
@@ -27,42 +26,12 @@ export type Props = {
|
||||
};
|
||||
|
||||
export default class SettingsSidebar extends React.PureComponent<Props> {
|
||||
buttonRefs: Map<string, RefObject<HTMLButtonElement>>;
|
||||
buttonRefs: Map<string, HTMLButtonElement>;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Initialize an empty Map for button refs
|
||||
this.buttonRefs = new Map();
|
||||
|
||||
// Initialize refs for all tabs
|
||||
this.initializeButtonRefs(props.tabs, props.pluginTabs);
|
||||
}
|
||||
|
||||
// Initialize or update button refs for all tabs
|
||||
private initializeButtonRefs(tabs: Tab[], pluginTabs?: Tab[]) {
|
||||
// Clear existing refs if reinitializing
|
||||
this.buttonRefs.clear();
|
||||
|
||||
// Create refs for all tabs, regardless of display status
|
||||
tabs.forEach((tab) => {
|
||||
this.buttonRefs.set(tab.name, React.createRef());
|
||||
});
|
||||
|
||||
// Create refs for plugin tabs if they exist
|
||||
if (pluginTabs?.length) {
|
||||
pluginTabs.forEach((tab) => {
|
||||
this.buttonRefs.set(tab.name, React.createRef());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update refs when props change
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
// Check if tabs or pluginTabs have changed
|
||||
if (prevProps.tabs !== this.props.tabs || prevProps.pluginTabs !== this.props.pluginTabs) {
|
||||
this.initializeButtonRefs(this.props.tabs, this.props.pluginTabs);
|
||||
}
|
||||
}
|
||||
|
||||
// Get all visible tabs in the correct order
|
||||
@@ -78,13 +47,13 @@ export default class SettingsSidebar extends React.PureComponent<Props> {
|
||||
(e.target as Element).closest('.settings-modal')?.classList.add('display--content');
|
||||
};
|
||||
|
||||
public handleKeyUp = (tab: Tab, e: React.KeyboardEvent) => {
|
||||
public handleKeyDown = (tab: Tab, e: React.KeyboardEvent) => {
|
||||
// Only handle UP and DOWN arrow keys
|
||||
if (!isKeyPressed(e, Constants.KeyCodes.UP) && !isKeyPressed(e, Constants.KeyCodes.DOWN)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default behavior
|
||||
// Prevent scrolling
|
||||
e.preventDefault();
|
||||
|
||||
// Get all visible tabs
|
||||
@@ -121,7 +90,7 @@ export default class SettingsSidebar extends React.PureComponent<Props> {
|
||||
this.props.updateTab(targetTab.name);
|
||||
|
||||
// Focus the target tab button directly
|
||||
const targetButton = this.buttonRefs.get(targetTab.name)?.current;
|
||||
const targetButton = this.buttonRefs.get(targetTab.name);
|
||||
if (targetButton) {
|
||||
// Use direct focus instead of a11yFocus to ensure Cypress tests can detect the focus change
|
||||
targetButton.focus();
|
||||
@@ -155,11 +124,17 @@ export default class SettingsSidebar extends React.PureComponent<Props> {
|
||||
{tab.newGroup && <hr/>}
|
||||
<button
|
||||
data-testid={`${tab.name}-tab-button`}
|
||||
ref={this.buttonRefs.get(tab.name)}
|
||||
ref={(element: HTMLButtonElement) => {
|
||||
if (element) {
|
||||
this.buttonRefs.set(tab.name, element);
|
||||
} else {
|
||||
this.buttonRefs.delete(tab.name);
|
||||
}
|
||||
}}
|
||||
id={`${tab.name}Button`}
|
||||
className={classNames('cursor--pointer style--none nav-pills__tab', {active: isActive})}
|
||||
onClick={this.handleClick.bind(null, tab)}
|
||||
onKeyUp={this.handleKeyUp.bind(null, tab)}
|
||||
onKeyDown={this.handleKeyDown.bind(null, tab)}
|
||||
aria-label={tab.uiName.toLowerCase()}
|
||||
role='tab'
|
||||
aria-selected={isActive}
|
||||
|
||||
@@ -648,6 +648,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove this when MM-62778 is completed
|
||||
&:focus-visible {
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 1px 3px rgba(var(--link-color-rgb), 0.5), 0 0 0 1px var(--link-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user