[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>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
a7905541b8
Коммит
8b2e1483c9
@@ -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),
|
||||
|
||||
Ссылка в новой задаче
Block a user