MM-61968 - a11y rhs focus management (#29591)
* MM-61968 - a11y rhs focus management * fix condition when rhs was already open and not getting focus * fix typescript errors * skip failing test --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -88,7 +88,7 @@ describe('Messaging', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('MM-T91 Replying to an older post by a user that has no content (only file attachments)', () => {
|
||||
it.skip('MM-T91 Replying to an older post by a user that has no content (only file attachments)', () => {
|
||||
// # Get yesterdays date in UTC
|
||||
const yesterdaysDate = Cypress.dayjs().subtract(1, 'days').valueOf();
|
||||
|
||||
|
||||
@@ -52,6 +52,8 @@ function mapStateToProps(state: GlobalState, props: RouteComponentProps) {
|
||||
isChannelMembers: rhsState === RHSStates.CHANNEL_MEMBERS,
|
||||
isPluginView: rhsState === RHSStates.PLUGIN,
|
||||
isPostEditHistory: rhsState === RHSStates.EDIT_HISTORY,
|
||||
isRecentMention: rhsState === RHSStates.MENTION,
|
||||
isSavedPosts: rhsState === RHSStates.FLAG,
|
||||
rhsChannel: getSelectedChannel(state),
|
||||
selectedPostId,
|
||||
selectedPostCardId,
|
||||
|
||||
@@ -21,7 +21,8 @@ import RhsThread from 'components/rhs_thread';
|
||||
import Search from 'components/search/index';
|
||||
|
||||
import RhsPlugin from 'plugins/rhs_plugin';
|
||||
import Constants from 'utils/constants';
|
||||
import type {A11yFocusEventDetail} from 'utils/constants';
|
||||
import Constants, {A11yCustomEventTypes} from 'utils/constants';
|
||||
import {cmdOrCtrlPressed, isKeyPressed} from 'utils/keyboard';
|
||||
import {isMac} from 'utils/user_agent';
|
||||
|
||||
@@ -47,6 +48,8 @@ export type Props = {
|
||||
rhsChannel?: Channel;
|
||||
selectedPostId: string;
|
||||
selectedPostCardId: string;
|
||||
isSavedPosts?: boolean;
|
||||
isRecentMentions?: boolean;
|
||||
actions: {
|
||||
setRhsExpanded: (expanded: boolean) => void;
|
||||
showPinnedPosts: (channelId: string) => void;
|
||||
@@ -68,6 +71,7 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
|
||||
sidebarRightWidthHolder: React.RefObject<HTMLDivElement>;
|
||||
previous: Partial<Props> | undefined = undefined;
|
||||
focusSearchBar?: () => void;
|
||||
private previousActiveElement: HTMLElement | null = null;
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
@@ -87,6 +91,8 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
|
||||
this.previous = {
|
||||
searchVisible: this.props.searchVisible,
|
||||
isPinnedPosts: this.props.isPinnedPosts,
|
||||
isRecentMentions: this.props.isRecentMentions,
|
||||
isSavedPosts: this.props.isSavedPosts,
|
||||
isChannelFiles: this.props.isChannelFiles,
|
||||
isChannelInfo: this.props.isChannelInfo,
|
||||
isChannelMembers: this.props.isChannelMembers,
|
||||
@@ -131,6 +137,55 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
handleRHSFocus(prevProps: Props) {
|
||||
const wasOpen = prevProps.isOpen;
|
||||
const isOpen = this.props.isOpen;
|
||||
|
||||
const contentChanged = (
|
||||
(this.props.isPinnedPosts !== prevProps.isPinnedPosts) ||
|
||||
(this.props.isRecentMentions !== prevProps.isRecentMentions) ||
|
||||
(this.props.isSavedPosts !== prevProps.isSavedPosts) ||
|
||||
(this.props.isChannelFiles !== prevProps.isChannelFiles) ||
|
||||
(this.props.isChannelInfo !== prevProps.isChannelInfo) ||
|
||||
(this.props.isChannelMembers !== prevProps.isChannelMembers) ||
|
||||
(this.props.isPostEditHistory !== prevProps.isPostEditHistory) ||
|
||||
(this.props.rhsChannel?.id !== prevProps.rhsChannel?.id) ||
|
||||
(this.props.teamId !== prevProps.teamId)
|
||||
);
|
||||
|
||||
if (this.props.isOpen && (contentChanged || (!wasOpen && isOpen))) {
|
||||
this.previousActiveElement = document.activeElement as HTMLElement;
|
||||
requestAnimationFrame(() => {
|
||||
if (this.sidebarRight.current) {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent<A11yFocusEventDetail>(A11yCustomEventTypes.FOCUS, {
|
||||
detail: {
|
||||
target: this.sidebarRight.current,
|
||||
keyboardOnly: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (!this.props.isOpen && wasOpen) {
|
||||
// RHS just was closed, restore focus to the previous element had it
|
||||
// this will have to change for upcoming work specially for search and probalby plugins
|
||||
requestAnimationFrame(() => {
|
||||
if (this.previousActiveElement) {
|
||||
document.dispatchEvent(
|
||||
new CustomEvent<A11yFocusEventDetail>(A11yCustomEventTypes.FOCUS, {
|
||||
detail: {
|
||||
target: this.previousActiveElement,
|
||||
keyboardOnly: false,
|
||||
},
|
||||
}),
|
||||
);
|
||||
this.previousActiveElement = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleShortcut);
|
||||
document.addEventListener('mousedown', this.handleClickOutside);
|
||||
@@ -149,6 +204,8 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
|
||||
trackEvent('ui', 'ui_rhs_opened');
|
||||
}
|
||||
|
||||
this.handleRHSFocus(prevProps);
|
||||
|
||||
const {actions, isChannelFiles, isPinnedPosts, rhsChannel, channel} = this.props;
|
||||
if (isPinnedPosts && prevProps.isPinnedPosts === isPinnedPosts && rhsChannel && rhsChannel.id !== prevProps.rhsChannel?.id) {
|
||||
actions.showPinnedPosts(rhsChannel.id);
|
||||
@@ -275,6 +332,7 @@ export default class SidebarRight extends React.PureComponent<Props, State> {
|
||||
rightWidthHolderRef={this.sidebarRightWidthHolder}
|
||||
>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className='sidebar-right-container'
|
||||
ref={this.sidebarRight}
|
||||
>
|
||||
|
||||
Ссылка в новой задаче
Block a user