[MM-61610]: Added aria-activedescendant to the textbox (#29900)

* [MA-17]: Added aria-activedescendant to the textbox

* [MA-17]: Fixed id and ARIA attribute

* Rebased with master branch

* [MA-17]: Removed irrelevant attribute

* [MA-17]: Updated the logic to add textbox id to At mention suggestion

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
ayush-chauhan233
2025-02-19 20:15:09 +05:30
коммит произвёл GitHub
родитель a7905541b8
Коммит 8b2e1483c9
8 изменённых файлов: 57 добавлений и 17 удалений

Просмотреть файл

@@ -110,6 +110,7 @@ exports[`components/TextBox should match snapshot with additional, optional prop
"priorityProfiles": undefined,
"requestStarted": false,
"searchAssociatedGroupsForReference": [Function],
"textboxId": "someid",
"triggerCharacter": "@",
"useChannelMentions": true,
},
@@ -257,6 +258,7 @@ exports[`components/TextBox should match snapshot with required props 1`] = `
"priorityProfiles": undefined,
"requestStarted": false,
"searchAssociatedGroupsForReference": [Function],
"textboxId": "someid",
"triggerCharacter": "@",
"useChannelMentions": true,
},
@@ -404,6 +406,7 @@ exports[`components/TextBox should throw error when new property is too long 1`]
"priorityProfiles": undefined,
"requestStarted": false,
"searchAssociatedGroupsForReference": [Function],
"textboxId": "someid",
"triggerCharacter": "@",
"useChannelMentions": true,
},
@@ -551,6 +554,7 @@ exports[`components/TextBox should throw error when value is too long 1`] = `
"priorityProfiles": undefined,
"requestStarted": false,
"searchAssociatedGroupsForReference": [Function],
"textboxId": "someid",
"triggerCharacter": "@",
"useChannelMentions": true,
},

Просмотреть файл

@@ -9,6 +9,7 @@ exports[`at mention suggestion Should display nick name of non signed in user 1`
"id": "userid2",
"last_name": "b",
"nickname": "c",
"textboxId": "post_textbox",
"username": "user2",
}
}
@@ -19,6 +20,7 @@ exports[`at mention suggestion Should display nick name of non signed in user 1`
>
<SuggestionContainer
data-testid="mentionSuggestion_user2"
id="post_textbox_@user2"
isSelection={false}
item={
Object {
@@ -26,20 +28,23 @@ exports[`at mention suggestion Should display nick name of non signed in user 1`
"id": "userid2",
"last_name": "b",
"nickname": "c",
"textboxId": "post_textbox",
"username": "user2",
}
}
matchedPretext="@"
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
role="option"
term="@user"
>
<li
className="suggestion-list__item"
data-testid="mentionSuggestion_user2"
id="post_textbox_@user2"
onClick={[Function]}
onMouseMove={[Function]}
role="button"
role="option"
tabIndex={-1}
>
<span
@@ -103,6 +108,7 @@ exports[`at mention suggestion Should not display nick name of the signed in use
"isCurrentUser": true,
"last_name": "b",
"nickname": "c",
"textboxId": "post_textbox",
"username": "user",
}
}
@@ -113,6 +119,7 @@ exports[`at mention suggestion Should not display nick name of the signed in use
>
<SuggestionContainer
data-testid="mentionSuggestion_user"
id="post_textbox_@user"
isSelection={false}
item={
Object {
@@ -121,20 +128,23 @@ exports[`at mention suggestion Should not display nick name of the signed in use
"isCurrentUser": true,
"last_name": "b",
"nickname": "c",
"textboxId": "post_textbox",
"username": "user",
}
}
matchedPretext="@"
onClick={[MockFunction]}
onMouseMove={[MockFunction]}
role="option"
term="@user"
>
<li
className="suggestion-list__item"
data-testid="mentionSuggestion_user"
id="post_textbox_@user"
onClick={[Function]}
onMouseMove={[Function]}
role="button"
role="option"
tabIndex={-1}
>
<span

Просмотреть файл

@@ -45,6 +45,7 @@ type Results = {
type ResultsCallback = (results: Results) => void;
export type Props = {
textboxId?: string;
currentUserId: string;
channelId: string;
autocompleteUsersInChannel: (prefix: string) => Promise<ActionResult>;
@@ -58,6 +59,7 @@ export type Props = {
// users in the channel and users not in the channel. It mixes together results from the local
// store with results fetched from the server.
export default class AtMentionProvider extends Provider {
public textboxId?: string;
public currentUserId: string;
public channelId: string;
public autocompleteUsersInChannel: (prefix: string) => Promise<ActionResult>;
@@ -75,8 +77,9 @@ export default class AtMentionProvider extends Provider {
constructor(props: Props) {
super();
const {currentUserId, channelId, autocompleteUsersInChannel, useChannelMentions, autocompleteGroups, searchAssociatedGroupsForReference, priorityProfiles} = props;
const {currentUserId, channelId, autocompleteUsersInChannel, useChannelMentions, autocompleteGroups, searchAssociatedGroupsForReference, priorityProfiles, textboxId} = props;
this.textboxId = textboxId;
this.currentUserId = currentUserId;
this.channelId = channelId;
this.autocompleteUsersInChannel = autocompleteUsersInChannel;
@@ -93,7 +96,8 @@ export default class AtMentionProvider extends Provider {
this.addLastViewAtToProfiles = makeAddLastViewAtToProfiles();
}
setProps({currentUserId, channelId, autocompleteUsersInChannel, useChannelMentions, autocompleteGroups, searchAssociatedGroupsForReference, priorityProfiles}: Props) {
setProps({currentUserId, channelId, autocompleteUsersInChannel, useChannelMentions, autocompleteGroups, searchAssociatedGroupsForReference, priorityProfiles, textboxId}: Props) {
this.textboxId = textboxId;
this.currentUserId = currentUserId;
this.channelId = channelId;
this.autocompleteUsersInChannel = autocompleteUsersInChannel;
@@ -362,19 +366,24 @@ export default class AtMentionProvider extends Provider {
} else if (this.lastPrefixWithNoResults === this.latestPrefix) {
this.lastPrefixWithNoResults = '';
}
const mentions = items.map((item) => {
const mentions: string[] = [];
// Add the textboxId for each suggestions
const modifiedItems = items.map((item) => {
if (item.username) {
return '@' + item.username;
mentions.push('@' + item.username);
} else if (item.name) {
return '@' + item.name;
mentions.push('@' + item.name);
} else {
mentions.push('');
}
return '';
return {...item, textboxId: this.textboxId};
});
resultCallback({
matchedPretext: `@${this.latestPrefix}`,
terms: mentions,
items,
items: modifiedItems,
component: AtMentionSuggestion,
});
}

Просмотреть файл

@@ -20,6 +20,7 @@ describe('at mention suggestion', () => {
last_name: 'b',
nickname: 'c',
isCurrentUser: true,
textboxId: 'post_textbox',
} as Item;
const userid2 = {
@@ -28,6 +29,7 @@ describe('at mention suggestion', () => {
first_name: 'a',
last_name: 'b',
nickname: 'c',
textboxId: 'post_textbox',
} as Item;
const baseProps = {

Просмотреть файл

@@ -28,6 +28,7 @@ export interface Item extends UserProfile {
name: string;
isCurrentUser: boolean;
type: string;
textboxId?: string;
}
interface Group extends Item {
@@ -52,7 +53,10 @@ const AtMentionSuggestion = React.forwardRef<HTMLLIElement, SuggestionProps<Item
/>
);
icon = (
<span className='suggestion-list__icon suggestion-list__icon--large'>
<span
className='suggestion-list__icon suggestion-list__icon--large'
aria-hidden='true'
>
<i
className='icon icon-account-multiple-outline'
title={intl.formatMessage({id: 'generic_icons.member', defaultMessage: 'Member Icon'})}
@@ -68,7 +72,10 @@ const AtMentionSuggestion = React.forwardRef<HTMLLIElement, SuggestionProps<Item
/>
);
icon = (
<span className='suggestion-list__icon suggestion-list__icon--large'>
<span
className='suggestion-list__icon suggestion-list__icon--large'
aria-hidden='true'
>
<i
className='icon icon-account-multiple-outline'
title={intl.formatMessage({id: 'generic_icons.member', defaultMessage: 'Member Icon'})}
@@ -84,7 +91,10 @@ const AtMentionSuggestion = React.forwardRef<HTMLLIElement, SuggestionProps<Item
/>
);
icon = (
<span className='suggestion-list__icon suggestion-list__icon--large'>
<span
className='suggestion-list__icon suggestion-list__icon--large'
aria-hidden='true'
>
<i
className='icon icon-account-multiple-outline'
title={intl.formatMessage({id: 'generic_icons.member', defaultMessage: 'Member Icon'})}
@@ -97,7 +107,10 @@ const AtMentionSuggestion = React.forwardRef<HTMLLIElement, SuggestionProps<Item
<span className='ml-1'>{'- '}{item.display_name}</span>
);
icon = (
<span className='suggestion-list__icon suggestion-list__icon--large'>
<span
className='suggestion-list__icon suggestion-list__icon--large'
aria-hidden='true'
>
<i
className='icon icon-account-multiple-outline'
title={intl.formatMessage({id: 'generic_icons.member', defaultMessage: 'Member Icon'})}
@@ -175,8 +188,10 @@ const AtMentionSuggestion = React.forwardRef<HTMLLIElement, SuggestionProps<Item
return (
<SuggestionContainer
ref={ref}
role='option'
{...props}
data-testid={`mentionSuggestion_${itemname}`}
id={`${item.textboxId}_@${itemname}`}
>
{icon}
<span className='suggestion-list__ellipsis'>

Просмотреть файл

@@ -206,7 +206,7 @@ export default class SuggestionList extends React.PureComponent<Props> {
renderDivider(type: string) {
const id = type ? 'suggestion.' + type : 'suggestion.default';
return (
<div
<li
key={type + '-divider'}
className='suggestion-list__divider'
role='separator'
@@ -214,7 +214,7 @@ export default class SuggestionList extends React.PureComponent<Props> {
<span>
<FormattedMessage id={id}/>
</span>
</div>
</li>
);
}

Просмотреть файл

@@ -330,8 +330,6 @@ function mapStateToPropsForSwitchChannelSuggestion(state: GlobalState, ownProps:
collapsedThreads,
team,
isPartOfOnlyOneTeam,
// id: 'quickSwitchInput',
};
}

Просмотреть файл

@@ -105,6 +105,7 @@ export default class Textbox extends React.PureComponent<Props> {
this.suggestionProviders.push(
new AtMentionProvider({
textboxId: this.props.id,
currentUserId: this.props.currentUserId,
channelId: this.props.channelId,
autocompleteUsersInChannel: (prefix: string) => this.props.actions.autocompleteUsersInChannel(prefix, this.props.channelId),
@@ -146,6 +147,7 @@ export default class Textbox extends React.PureComponent<Props> {
for (const provider of this.suggestionProviders) {
if (provider instanceof AtMentionProvider) {
provider.setProps({
textboxId: this.props.id,
currentUserId: this.props.currentUserId,
channelId: this.props.channelId,
autocompleteUsersInChannel: (prefix: string) => this.props.actions.autocompleteUsersInChannel(prefix, this.props.channelId),