[MM-36428][MM-36434][MM-36435] Keyboard navigation and menu updates for new tab/dropdown layout (#1695)
* [MM-36428][MM-36434][MM-36435] Keyboard navigation and menu updates for new tab/dropdown layout * Shortcuts for Windows/Linux * Update config.yml * Fixed up the shortcuts * Fixed the new server modal popping up where there are GPO teams only
This commit is contained in:
@@ -400,6 +400,7 @@ export default class MainPage extends React.PureComponent<Props, State> {
|
||||
<DotsVerticalIcon/>
|
||||
</button>
|
||||
<TeamDropdownButton
|
||||
isDisabled={this.state.modalOpen}
|
||||
activeServerName={this.state.activeServerName}
|
||||
totalMentionCount={totalMentionCount}
|
||||
hasUnreads={totalUnreadCount > 0}
|
||||
|
@@ -10,6 +10,7 @@ import '../css/components/TeamDropdownButton.scss';
|
||||
import '../css/compass-icons.css';
|
||||
|
||||
type Props = {
|
||||
isDisabled?: boolean;
|
||||
activeServerName: string;
|
||||
totalMentionCount: number;
|
||||
hasUnreads: boolean;
|
||||
@@ -18,7 +19,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const TeamDropdownButton: React.FC<Props> = (props: Props) => {
|
||||
const {activeServerName, totalMentionCount, hasUnreads, isMenuOpen, darkMode} = props;
|
||||
const {isDisabled, activeServerName, totalMentionCount, hasUnreads, isMenuOpen, darkMode} = props;
|
||||
|
||||
const handleToggleButton = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -41,7 +42,9 @@ const TeamDropdownButton: React.FC<Props> = (props: Props) => {
|
||||
|
||||
return (
|
||||
<button
|
||||
disabled={isDisabled}
|
||||
className={classNames('TeamDropdownButton', {
|
||||
disabled: isDisabled,
|
||||
isMenuOpen,
|
||||
darkMode,
|
||||
})}
|
||||
|
@@ -7,7 +7,11 @@
|
||||
align-items: center;
|
||||
font-family: Open Sans;
|
||||
|
||||
&:hover {
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
&:not(.disabled):hover {
|
||||
background-color: #f4f4f4;
|
||||
|
||||
.TeamDropdownButton__badge-count, .TeamDropdownButton__badge-unreads {
|
||||
@@ -15,7 +19,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:focus, &.isMenuOpen {
|
||||
&:not(.disabled):focus, &.isMenuOpen {
|
||||
background-color: #fff;
|
||||
|
||||
.TeamDropdownButton__badge-count, .TeamDropdownButton__badge-unreads {
|
||||
|
@@ -48,12 +48,19 @@ function getStyle(style?: DraggingStyle | NotDraggingStyle) {
|
||||
return style;
|
||||
}
|
||||
class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
||||
buttonRefs: Map<number, HTMLButtonElement>;
|
||||
addServerRef: React.RefObject<HTMLButtonElement>;
|
||||
focusedIndex: number | null;
|
||||
|
||||
constructor(props: Record<string, never>) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isAnyDragging: false,
|
||||
};
|
||||
this.focusedIndex = null;
|
||||
|
||||
this.buttonRefs = new Map();
|
||||
this.addServerRef = React.createRef();
|
||||
window.addEventListener('message', this.handleMessageEvent);
|
||||
}
|
||||
|
||||
@@ -137,6 +144,7 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
||||
componentDidMount() {
|
||||
window.postMessage({type: REQUEST_TEAMS_DROPDOWN_INFO}, window.location.href);
|
||||
window.addEventListener('click', this.closeMenu);
|
||||
window.addEventListener('keydown', this.handleKeyboardShortcuts);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
@@ -145,6 +153,53 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('click', this.closeMenu);
|
||||
window.removeEventListener('keydown', this.handleKeyboardShortcuts);
|
||||
}
|
||||
|
||||
setButtonRef = (teamIndex: number, refMethod?: (element: HTMLButtonElement) => any) => {
|
||||
return (ref: HTMLButtonElement) => {
|
||||
this.addButtonRef(teamIndex, ref);
|
||||
refMethod?.(ref);
|
||||
};
|
||||
}
|
||||
|
||||
addButtonRef = (teamIndex: number, ref: HTMLButtonElement | null) => {
|
||||
if (ref) {
|
||||
this.buttonRefs.set(teamIndex, ref);
|
||||
ref.addEventListener('focusin', () => {
|
||||
this.focusedIndex = teamIndex;
|
||||
});
|
||||
ref.addEventListener('blur', () => {
|
||||
this.focusedIndex = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyboardShortcuts = (event: KeyboardEvent) => {
|
||||
if (event.key === 'ArrowDown') {
|
||||
if (this.focusedIndex === null) {
|
||||
this.focusedIndex = 0;
|
||||
} else {
|
||||
this.focusedIndex = (this.focusedIndex + 1) % this.buttonRefs.size;
|
||||
}
|
||||
this.buttonRefs.get(this.focusedIndex)?.focus();
|
||||
}
|
||||
if (event.key === 'ArrowUp') {
|
||||
if (this.focusedIndex === null || this.focusedIndex === 0) {
|
||||
this.focusedIndex = this.buttonRefs.size - 1;
|
||||
} else {
|
||||
this.focusedIndex = (this.focusedIndex - 1) % this.buttonRefs.size;
|
||||
}
|
||||
this.buttonRefs.get(this.focusedIndex)?.focus();
|
||||
}
|
||||
if (event.key === 'Escape') {
|
||||
this.closeMenu();
|
||||
}
|
||||
this.buttonRefs.forEach((button, index) => {
|
||||
if (event.key === String(index + 1)) {
|
||||
button.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleClickOnDragHandle = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
@@ -238,7 +293,7 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
||||
anyDragging: this.state.isAnyDragging,
|
||||
active: this.isActiveTeam(team),
|
||||
})}
|
||||
ref={provided.innerRef}
|
||||
ref={this.setButtonRef(orderedIndex, provided.innerRef)}
|
||||
{...provided.draggableProps}
|
||||
onClick={this.selectServer(team)}
|
||||
style={getStyle(provided.draggableProps.style)}
|
||||
@@ -284,6 +339,9 @@ class TeamDropdown extends React.PureComponent<Record<string, never>, State> {
|
||||
<hr className='TeamDropdown__divider'/>
|
||||
{this.state.enableServerManagement &&
|
||||
<button
|
||||
ref={(ref) => {
|
||||
this.addButtonRef(this.state.orderedTeams?.length || 0, ref);
|
||||
}}
|
||||
className='TeamDropdown__button addServer'
|
||||
onClick={this.addServer}
|
||||
>
|
||||
|
Reference in New Issue
Block a user