From 04955cc45ecfab8b9da5f17521acadc382a76c0e Mon Sep 17 00:00:00 2001 From: Mitch Lillie <mlillie87@gmail.com> Date: Sat, 6 Feb 2021 12:20:41 -0800 Subject: [PATCH] Add aria attributes where possible (#156) * Add aria attributes where possible * Bump submodule to get aria translations --- lemmy-translations | 2 +- src/shared/components/banner-icon-header.tsx | 3 +- src/shared/components/comment-node.tsx | 30 ++++++++++++++++++- src/shared/components/communities.tsx | 2 ++ src/shared/components/image-upload-form.tsx | 6 +++- src/shared/components/inbox.tsx | 1 + src/shared/components/login.tsx | 1 + src/shared/components/main.tsx | 2 ++ src/shared/components/markdown-textarea.tsx | 8 +++++ src/shared/components/password_change.tsx | 6 ++-- src/shared/components/pictrs-image.tsx | 11 ++++++- src/shared/components/post-form.tsx | 3 +- src/shared/components/post-listing.tsx | 16 ++++++++++ .../components/private-message-form.tsx | 2 ++ src/shared/components/private-message.tsx | 14 +++++++++ src/shared/components/search.tsx | 8 +++-- src/shared/components/sidebar.tsx | 12 ++++++++ src/shared/components/sort-select.tsx | 5 ++-- src/shared/components/user.tsx | 24 ++++++++++----- 19 files changed, 137 insertions(+), 19 deletions(-) diff --git a/lemmy-translations b/lemmy-translations index a47ae3f..084ce53 160000 --- a/lemmy-translations +++ b/lemmy-translations @@ -1 +1 @@ -Subproject commit a47ae3f825f74ad7a6106b92e21d3c66b10d3fe8 +Subproject commit 084ce539fff5253317d6460598b10a6867999c20 diff --git a/src/shared/components/banner-icon-header.tsx b/src/shared/components/banner-icon-header.tsx index cb84eb2..050ace1 100644 --- a/src/shared/components/banner-icon-header.tsx +++ b/src/shared/components/banner-icon-header.tsx @@ -14,12 +14,13 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> { render() { return ( <div class="position-relative mb-2"> - {this.props.banner && <PictrsImage src={this.props.banner} />} + {this.props.banner && <PictrsImage src={this.props.banner} alt="" />} {this.props.icon && ( <PictrsImage src={this.props.icon} iconOverlay pushup={!!this.props.banner} + alt="" /> )} </div> diff --git a/src/shared/components/comment-node.tsx b/src/shared/components/comment-node.tsx index 071c238..26eeaf0 100644 --- a/src/shared/components/comment-node.tsx +++ b/src/shared/components/comment-node.tsx @@ -198,6 +198,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { <button class="btn btn-sm text-muted" onClick={linkEvent(this, this.handleCommentCollapse)} + aria-label={ + this.state.collapsed ? i18n.t('expand') : i18n.t('collapse') + } > {this.state.collapsed ? '+' : 'â'} </button> @@ -248,6 +251,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { ? i18n.t('mark_as_unread') : i18n.t('mark_as_read') } + aria-label={ + this.commentOrMentionRead + ? i18n.t('mark_as_unread') + : i18n.t('mark_as_read') + } > {this.state.readLoading ? ( this.loadingIcon @@ -270,6 +278,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { }`} onClick={linkEvent(node, this.handleCommentUpvote)} data-tippy-content={i18n.t('upvote')} + aria-label={i18n.t('upvote')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-arrow-up1"></use> @@ -287,6 +296,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { }`} onClick={linkEvent(node, this.handleCommentDownvote)} data-tippy-content={i18n.t('downvote')} + aria-label={i18n.t('downvote')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-arrow-down1"></use> @@ -300,6 +310,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleReplyClick)} data-tippy-content={i18n.t('reply')} + aria-label={i18n.t('reply')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-reply1"></use> @@ -310,6 +321,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { className="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleShowAdvanced)} data-tippy-content={i18n.t('more')} + aria-label={i18n.t('more')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-more-vertical"></use> @@ -340,6 +352,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { data-tippy-content={ cv.saved ? i18n.t('unsave') : i18n.t('save') } + aria-label={ + cv.saved ? i18n.t('unsave') : i18n.t('save') + } > {this.state.saveLoading ? ( this.loadingIcon @@ -357,6 +372,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { className="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleViewSource)} data-tippy-content={i18n.t('view_source')} + aria-label={i18n.t('view_source')} > <svg class={`icon icon-inline ${ @@ -372,6 +388,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleEditClick)} data-tippy-content={i18n.t('edit')} + aria-label={i18n.t('edit')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-edit"></use> @@ -388,6 +405,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { ? i18n.t('delete') : i18n.t('restore') } + aria-label={ + !cv.comment.deleted + ? i18n.t('delete') + : i18n.t('restore') + } > <svg class={`icon icon-inline ${ @@ -667,9 +689,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {this.state.showBanDialog && ( <form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}> <div class="form-group row"> - <label class="col-form-label">{i18n.t('reason')}</label> + <label + class="col-form-label" + htmlFor={`mod-ban-reason-${cv.comment.id}`} + > + {i18n.t('reason')} + </label> <input type="text" + id={`mod-ban-reason-${cv.comment.id}`} class="form-control mr-2" placeholder={i18n.t('reason')} value={this.state.banReason} diff --git a/src/shared/components/communities.tsx b/src/shared/components/communities.tsx index c852fe2..e90c09d 100644 --- a/src/shared/components/communities.tsx +++ b/src/shared/components/communities.tsx @@ -158,6 +158,7 @@ export class Communities extends Component<any, CommunitiesState> { {cv.subscribed ? ( <span class="pointer btn-link" + role="button" onClick={linkEvent( cv.community.id, this.handleUnsubscribe @@ -168,6 +169,7 @@ export class Communities extends Component<any, CommunitiesState> { ) : ( <span class="pointer btn-link" + role="button" onClick={linkEvent( cv.community.id, this.handleSubscribe diff --git a/src/shared/components/image-upload-form.tsx b/src/shared/components/image-upload-form.tsx index c9ffea4..4b8866f 100644 --- a/src/shared/components/image-upload-form.tsx +++ b/src/shared/components/image-upload-form.tsx @@ -2,6 +2,7 @@ import { Component, linkEvent } from 'inferno'; import { pictrsUri } from '../env'; import { UserService } from '../services'; import { toast, randomStr } from '../utils'; +import { i18n } from '../i18next'; interface ImageUploadFormProps { uploadTitle: string; @@ -48,7 +49,10 @@ export class ImageUploadForm extends Component< this.props.rounded ? 'rounded-circle' : '' }`} /> - <a onClick={linkEvent(this, this.handleRemoveImage)}> + <a + onClick={linkEvent(this, this.handleRemoveImage)} + aria-label={i18n.t('remove')} + > <svg class="icon mini-overlay"> <use xlinkHref="#icon-x"></use> </svg> diff --git a/src/shared/components/inbox.tsx b/src/shared/components/inbox.tsx index 4390b8e..ab0c312 100644 --- a/src/shared/components/inbox.tsx +++ b/src/shared/components/inbox.tsx @@ -172,6 +172,7 @@ export class Inbox extends Component<any, InboxState> { <li className="list-inline-item"> <span class="pointer" + role="button" onClick={linkEvent(this, this.markAllAsRead)} > {i18n.t('mark_all_as_read')} diff --git a/src/shared/components/login.tsx b/src/shared/components/login.tsx index acd05a3..7006ae7 100644 --- a/src/shared/components/login.tsx +++ b/src/shared/components/login.tsx @@ -326,6 +326,7 @@ export class Login extends Component<any, State> { class="rounded-top img-fluid" src={this.captchaPngSrc()} style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;" + alt={i18n.t('captcha')} /> {this.state.captcha.ok.wav && ( <button diff --git a/src/shared/components/main.tsx b/src/shared/components/main.tsx index 1c7b7da..28795b5 100644 --- a/src/shared/components/main.tsx +++ b/src/shared/components/main.tsx @@ -508,7 +508,9 @@ export class Main extends Component<any, MainState> { <li className="list-inline-item-action"> <span class="pointer" + role="button" onClick={linkEvent(this, this.handleEditClick)} + aria-label={i18n.t('edit')} data-tippy-content={i18n.t('edit')} > <svg class="icon icon-inline"> diff --git a/src/shared/components/markdown-textarea.tsx b/src/shared/components/markdown-textarea.tsx index ded6dd1..cffc432 100644 --- a/src/shared/components/markdown-textarea.tsx +++ b/src/shared/components/markdown-textarea.tsx @@ -233,6 +233,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('header')} + aria-label={i18n.t('header')} onClick={linkEvent(this, this.handleInsertHeader)} > <svg class="icon icon-inline"> @@ -242,6 +243,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('strikethrough')} + aria-label={i18n.t('strikethrough')} onClick={linkEvent(this, this.handleInsertStrikethrough)} > <svg class="icon icon-inline"> @@ -251,6 +253,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('quote')} + aria-label={i18n.t('quote')} onClick={linkEvent(this, this.handleInsertQuote)} > <svg class="icon icon-inline"> @@ -260,6 +263,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('list')} + aria-label={i18n.t('list')} onClick={linkEvent(this, this.handleInsertList)} > <svg class="icon icon-inline"> @@ -269,6 +273,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('code')} + aria-label={i18n.t('code')} onClick={linkEvent(this, this.handleInsertCode)} > <svg class="icon icon-inline"> @@ -278,6 +283,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('subscript')} + aria-label={i18n.t('subscript')} onClick={linkEvent(this, this.handleInsertSubscript)} > <svg class="icon icon-inline"> @@ -287,6 +293,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('superscript')} + aria-label={i18n.t('superscript')} onClick={linkEvent(this, this.handleInsertSuperscript)} > <svg class="icon icon-inline"> @@ -296,6 +303,7 @@ export class MarkdownTextArea extends Component< <button class="btn btn-sm text-muted" data-tippy-content={i18n.t('spoiler')} + aria-label={i18n.t('spoiler')} onClick={linkEvent(this, this.handleInsertSpoiler)} > <svg class="icon icon-inline"> diff --git a/src/shared/components/password_change.tsx b/src/shared/components/password_change.tsx index 6ffa3e0..373d10f 100644 --- a/src/shared/components/password_change.tsx +++ b/src/shared/components/password_change.tsx @@ -80,11 +80,12 @@ export class PasswordChange extends Component<any, State> { return ( <form onSubmit={linkEvent(this, this.handlePasswordChangeSubmit)}> <div class="form-group row"> - <label class="col-sm-2 col-form-label"> + <label class="col-sm-2 col-form-label" htmlFor="new-password"> {i18n.t('new_password')} </label> <div class="col-sm-10"> <input + id="new-password" type="password" value={this.state.passwordChangeForm.password} onInput={linkEvent(this, this.handlePasswordChange)} @@ -94,11 +95,12 @@ export class PasswordChange extends Component<any, State> { </div> </div> <div class="form-group row"> - <label class="col-sm-2 col-form-label"> + <label class="col-sm-2 col-form-label" htmlFor="verify-password"> {i18n.t('verify_password')} </label> <div class="col-sm-10"> <input + id="verify-password" type="password" value={this.state.passwordChangeForm.password_verify} onInput={linkEvent(this, this.handleVerifyPasswordChange)} diff --git a/src/shared/components/pictrs-image.tsx b/src/shared/components/pictrs-image.tsx index b044721..1f5655d 100644 --- a/src/shared/components/pictrs-image.tsx +++ b/src/shared/components/pictrs-image.tsx @@ -6,6 +6,7 @@ const maxImageSize = 3000; interface PictrsImageProps { src: string; + alt?: string; icon?: boolean; thumbnail?: boolean; nsfw?: boolean; @@ -25,13 +26,14 @@ export class PictrsImage extends Component<PictrsImageProps, any> { <source srcSet={this.src('jpg')} type="image/jpeg" /> <img src={this.src('jpg')} + alt={this.alt()} className={` ${!this.props.icon && !this.props.iconOverlay && 'img-fluid '} ${ this.props.thumbnail && !this.props.icon ? 'thumbnail rounded ' : 'img-expanded ' - } + } ${this.props.thumbnail && this.props.nsfw && 'img-blur '} ${this.props.icon && 'rounded-circle img-icon mr-2 '} ${this.props.iconOverlay && 'ml-2 mb-0 rounded-circle avatar-overlay '} @@ -71,4 +73,11 @@ export class PictrsImage extends Component<PictrsImageProps, any> { return out; } + + alt(): string { + if (this.props.icon) { + return ''; + } + return this.props.alt || ''; + } } diff --git a/src/shared/components/post-form.tsx b/src/shared/components/post-form.tsx index 4295a62..7f5c7fb 100644 --- a/src/shared/components/post-form.tsx +++ b/src/shared/components/post-form.tsx @@ -180,6 +180,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> { {this.state.suggestedTitle && ( <div class="mt-1 text-muted small font-weight-bold pointer" + role="button" onClick={linkEvent(this, this.copySuggestedTitle)} > {i18n.t('copy_suggested_title', { @@ -227,7 +228,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> { </svg> )} {isImage(this.state.postForm.url) && ( - <img src={this.state.postForm.url} class="img-fluid" /> + <img src={this.state.postForm.url} class="img-fluid" alt="" /> )} {this.state.crossPosts.length > 0 && ( <> diff --git a/src/shared/components/post-listing.tsx b/src/shared/components/post-listing.tsx index d75584b..2d0192b 100644 --- a/src/shared/components/post-listing.tsx +++ b/src/shared/components/post-listing.tsx @@ -169,6 +169,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { <PictrsImage src={src} thumbnail + alt="" nsfw={post_view.post.nsfw || post_view.community.nsfw} /> ); @@ -200,6 +201,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="float-right text-body pointer d-inline-block position-relative mb-2" data-tippy-content={i18n.t('expand_here')} onClick={linkEvent(this, this.handleImageExpandClick)} + role="button" + aria-label={i18n.t('expand_here')} > {this.imgThumb(this.getImageSrc())} <svg class="icon mini-overlay"> @@ -328,6 +331,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { previewLines(post_view.post.body) )} data-tippy-allowHtml={true} + aria-label={i18n.t('upvote')} to={`/post/${post_view.post.id}`} > <svg class="mr-1 icon icon-inline"> @@ -350,6 +354,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { }`} onClick={linkEvent(this, this.handlePostLike)} data-tippy-content={i18n.t('upvote')} + aria-label={i18n.t('upvote')} > <svg class="icon upvote"> <use xlinkHref="#icon-arrow-up1"></use> @@ -368,6 +373,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { }`} onClick={linkEvent(this, this.handlePostDisLike)} data-tippy-content={i18n.t('downvote')} + aria-label={i18n.t('downvote')} > <svg class="icon downvote"> <use xlinkHref="#icon-arrow-down1"></use> @@ -504,6 +510,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { <button class="btn text-muted py-0 pr-0" data-tippy-content={this.pointsTippy} + aria-label={i18n.t('downvote')} > <small> <svg class="icon icon-inline mr-1"> @@ -520,6 +527,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { data-tippy-content={ post_view.saved ? i18n.t('unsave') : i18n.t('save') } + aria-label={post_view.saved ? i18n.t('unsave') : i18n.t('save')} > <small> <svg @@ -545,6 +553,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { }`} data-tippy-content={this.pointsTippy} onClick={linkEvent(this, this.handlePostLike)} + aria-label={i18n.t('upvote')} > <svg class="small icon icon-inline mr-2"> <use xlinkHref="#icon-arrow-up1"></use> @@ -558,6 +567,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { }`} onClick={linkEvent(this, this.handlePostDisLike)} data-tippy-content={this.pointsTippy} + aria-label={i18n.t('downvote')} > <svg class="small icon icon-inline mr-2"> <use xlinkHref="#icon-arrow-down1"></use> @@ -571,6 +581,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { <button class="btn btn-link btn-animate text-muted py-0 pl-1 pr-0" onClick={linkEvent(this, this.handleSavePostClick)} + aria-label={post_view.saved ? i18n.t('unsave') : i18n.t('save')} data-tippy-content={ post_view.saved ? i18n.t('unsave') : i18n.t('save') } @@ -586,6 +597,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { <button class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleShowMoreMobile)} + aria-label={i18n.t('more')} data-tippy-content={i18n.t('more')} > <svg class="icon icon-inline"> @@ -639,6 +651,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> { data-tippy-content={ post_view.saved ? i18n.t('unsave') : i18n.t('save') } + aria-label={ + post_view.saved ? i18n.t('unsave') : i18n.t('save') + } > <svg class={`icon icon-inline ${ @@ -694,6 +709,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleShowAdvanced)} data-tippy-content={i18n.t('more')} + aria-label={i18n.t('more')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-more-vertical"></use> diff --git a/src/shared/components/private-message-form.tsx b/src/shared/components/private-message-form.tsx index 5688869..b4a43a1 100644 --- a/src/shared/components/private-message-form.tsx +++ b/src/shared/components/private-message-form.tsx @@ -116,8 +116,10 @@ export class PrivateMessageForm extends Component< {i18n.t('message')} <span onClick={linkEvent(this, this.handleShowDisclaimer)} + role="button" class="ml-2 pointer text-danger" data-tippy-content={i18n.t('disclaimer')} + aria-label={i18n.t('disclaimer')} > <svg class={`icon icon-inline`}> <use xlinkHref="#icon-alert-triangle"></use> diff --git a/src/shared/components/private-message.tsx b/src/shared/components/private-message.tsx index 0121ea8..7b22b74 100644 --- a/src/shared/components/private-message.tsx +++ b/src/shared/components/private-message.tsx @@ -77,6 +77,7 @@ export class PrivateMessage extends Component< </li> <li className="list-inline-item"> <div + role="button" className="pointer text-monospace" onClick={linkEvent(this, this.handleMessageCollapse)} > @@ -123,6 +124,11 @@ export class PrivateMessage extends Component< ? i18n.t('mark_as_unread') : i18n.t('mark_as_read') } + aria-label={ + message_view.private_message.read + ? i18n.t('mark_as_unread') + : i18n.t('mark_as_read') + } > <svg class={`icon icon-inline ${ @@ -138,6 +144,7 @@ export class PrivateMessage extends Component< class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleReplyClick)} data-tippy-content={i18n.t('reply')} + aria-label={i18n.t('reply')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-reply1"></use> @@ -153,6 +160,7 @@ export class PrivateMessage extends Component< class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleEditClick)} data-tippy-content={i18n.t('edit')} + aria-label={i18n.t('edit')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-edit"></use> @@ -168,6 +176,11 @@ export class PrivateMessage extends Component< ? i18n.t('delete') : i18n.t('restore') } + aria-label={ + !message_view.private_message.deleted + ? i18n.t('delete') + : i18n.t('restore') + } > <svg class={`icon icon-inline ${ @@ -186,6 +199,7 @@ export class PrivateMessage extends Component< class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleViewSource)} data-tippy-content={i18n.t('view_source')} + aria-label={i18n.t('view_source')} > <svg class={`icon icon-inline ${ diff --git a/src/shared/components/search.tsx b/src/shared/components/search.tsx index f4ef430..1b61e3b 100644 --- a/src/shared/components/search.tsx +++ b/src/shared/components/search.tsx @@ -209,6 +209,7 @@ export class Search extends Component<any, SearchState> { class="form-control mr-2 mb-2" value={this.state.searchText} placeholder={`${i18n.t('search')}...`} + aria-label={i18n.t('search')} onInput={linkEvent(this, this.handleQChange)} required minLength={3} @@ -233,8 +234,11 @@ export class Search extends Component<any, SearchState> { value={this.state.type_} onChange={linkEvent(this, this.handleTypeChange)} class="custom-select w-auto mb-2" + aria-label={i18n.t('type')} > - <option disabled>{i18n.t('type')}</option> + <option disabled aria-hidden="true"> + {i18n.t('type')} + </option> <option value={SearchType.All}>{i18n.t('all')}</option> <option value={SearchType.Comments}>{i18n.t('comments')}</option> <option value={SearchType.Posts}>{i18n.t('posts')}</option> @@ -382,7 +386,7 @@ export class Search extends Component<any, SearchState> { <span> <CommunityLink community={community_view.community} /> </span> - <span>{` - + <span>{` - ${i18n.t('number_of_subscribers', { count: community_view.counts.subscribers, })} diff --git a/src/shared/components/sidebar.tsx b/src/shared/components/sidebar.tsx index 03675dd..db96efa 100644 --- a/src/shared/components/sidebar.tsx +++ b/src/shared/components/sidebar.tsx @@ -299,9 +299,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { <> <li className="list-inline-item-action"> <span + role="button" class="pointer" onClick={linkEvent(this, this.handleEditClick)} data-tippy-content={i18n.t('edit')} + aria-label={i18n.t('edit')} > <svg class="icon icon-inline"> <use xlinkHref="#icon-edit"></use> @@ -313,6 +315,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { <li className="list-inline-item-action"> <span class="pointer" + role="button" onClick={linkEvent( this, this.handleShowConfirmLeaveModTeamClick @@ -329,6 +332,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { <li className="list-inline-item-action"> <span class="pointer" + role="button" onClick={linkEvent(this, this.handleLeaveModTeamClick)} > {i18n.t('yes')} @@ -337,6 +341,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { <li className="list-inline-item-action"> <span class="pointer" + role="button" onClick={linkEvent( this, this.handleCancelLeaveModTeamClick @@ -357,6 +362,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { ? i18n.t('delete') : i18n.t('restore') } + aria-label={ + !community_view.community.deleted + ? i18n.t('delete') + : i18n.t('restore') + } > <svg class={`icon icon-inline ${ @@ -375,6 +385,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { {!this.props.community_view.community.removed ? ( <span class="pointer" + role="button" onClick={linkEvent(this, this.handleModRemoveShow)} > {i18n.t('remove')} @@ -382,6 +393,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { ) : ( <span class="pointer" + role="button" onClick={linkEvent(this, this.handleModRemoveSubmit)} > {i18n.t('restore')} diff --git a/src/shared/components/sort-select.tsx b/src/shared/components/sort-select.tsx index ef14a6e..5ed2bc6 100644 --- a/src/shared/components/sort-select.tsx +++ b/src/shared/components/sort-select.tsx @@ -40,8 +40,9 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> { value={this.state.sort} onChange={linkEvent(this, this.handleSortChange)} class="custom-select w-auto mr-2 mb-2" + aria-label={i18n.t('sort_type')} > - <option disabled>{i18n.t('sort_type')}</option> + <option disabled aria-hidden="true">{i18n.t('sort_type')}</option> {!this.props.hideHot && [ <option value={SortType.Hot}>{i18n.t('hot')}</option>, <option value={SortType.Active}>{i18n.t('active')}</option>, @@ -52,7 +53,7 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> { {i18n.t('most_comments')} </option> )} - <option disabled>âââââ</option> + <option disabled aria-hidden="true">âââââ</option> <option value={SortType.TopDay}>{i18n.t('top_day')}</option> <option value={SortType.TopWeek}>{i18n.t('top_week')}</option> <option value={SortType.TopMonth}>{i18n.t('top_month')}</option> diff --git a/src/shared/components/user.tsx b/src/shared/components/user.tsx index e838851..0dbe252 100644 --- a/src/shared/components/user.tsx +++ b/src/shared/components/user.tsx @@ -526,28 +526,36 @@ export class User extends Component<any, UserState> { /> </div> <div class="form-group"> - <label>{i18n.t('language')}</label> + <label htmlFor="user-language">{i18n.t('language')}</label> <select + id="user-language" value={this.state.userSettingsForm.lang} onChange={linkEvent(this, this.handleUserSettingsLangChange)} class="ml-2 custom-select w-auto" > - <option disabled>{i18n.t('language')}</option> + <option disabled aria-hidden="true"> + {i18n.t('language')} + </option> <option value="browser">{i18n.t('browser_default')}</option> - <option disabled>ââ</option> + <option disabled aria-hidden="true"> + ââ + </option> {languages.map(lang => ( <option value={lang.code}>{lang.name}</option> ))} </select> </div> <div class="form-group"> - <label>{i18n.t('theme')}</label> + <label htmlFor="user-theme">{i18n.t('theme')}</label> <select + id="user-theme" value={this.state.userSettingsForm.theme} onChange={linkEvent(this, this.handleUserSettingsThemeChange)} class="ml-2 custom-select w-auto" > - <option disabled>{i18n.t('theme')}</option> + <option disabled aria-hidden="true"> + {i18n.t('theme')} + </option> <option value="browser">{i18n.t('browser_default')}</option> {themes.map(theme => ( <option value={theme}>{theme}</option> @@ -584,11 +592,12 @@ export class User extends Component<any, UserState> { /> </form> <div class="form-group row"> - <label class="col-lg-5 col-form-label"> + <label class="col-lg-5 col-form-label" htmlFor="display-name"> {i18n.t('display_name')} </label> <div class="col-lg-7"> <input + id="display-name" type="text" class="form-control" placeholder={i18n.t('optional')} @@ -636,13 +645,14 @@ export class User extends Component<any, UserState> { </div> </div> <div class="form-group row"> - <label class="col-lg-5 col-form-label"> + <label class="col-lg-5 col-form-label" htmlFor="matrix-user-id"> <a href={elementUrl} target="_blank" rel="noopener"> {i18n.t('matrix_user_id')} </a> </label> <div class="col-lg-7"> <input + id="matrix-user-id" type="text" class="form-control" placeholder="@user:example.com" -- 2.44.1