Merge pull request #174 from LemmyNet/more_accessibility

More accessibility
This commit is contained in:
Dessalines 2021-02-11 15:55:39 -05:00 committed by GitHub
commit 01152eccbb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 19 deletions

18
accessibility_tests.sh Executable file
View file

@ -0,0 +1,18 @@
#!/bin/bash
ignores="WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail"
base_url="http://192.168.50.60:1234"
test_urls=(
$base_url
$base_url/communities
$base_url/login
$base_url/search
$base_url/c/announcements
$base_url/u/dessalines
$base_url/post/34286
)
for i in "${test_urls[@]}"; do
pa11y --ignore="$ignores" "$i"
done

View file

@ -205,7 +205,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
onClick={linkEvent(node, this.handleCommentUpvote)}
data-tippy-content={this.pointsTippy}
>
<span class="mr-1 font-weight-bold">{this.state.score}</span>
<span
class="mr-1 font-weight-bold"
aria-label={i18n.t('number_of_points', {
count: this.state.score,
})}
>
{this.state.score}
</span>
</a>
<span className="mr-1"></span>
<span>
@ -409,6 +416,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleModRemoveShow
)}
aria-label={i18n.t('remove')}
>
{i18n.t('remove')}
</button>
@ -419,6 +427,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleModRemoveSubmit
)}
aria-label={i18n.t('restore')}
>
{i18n.t('restore')}
</button>
@ -436,6 +445,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleModBanFromCommunityShow
)}
aria-label={i18n.t('ban')}
>
{i18n.t('ban')}
</button>
@ -446,6 +456,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleModBanFromCommunitySubmit
)}
aria-label={i18n.t('unban')}
>
{i18n.t('unban')}
</button>
@ -459,6 +470,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleShowConfirmAppointAsMod
)}
aria-label={
this.isMod
? i18n.t('remove_as_mod')
: i18n.t('appoint_as_mod')
}
>
{this.isMod
? i18n.t('remove_as_mod')
@ -466,7 +482,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
</button>
) : (
<>
<button class="btn btn-link btn-animate text-muted">
<button
class="btn btn-link btn-animate text-muted"
aria-label={i18n.t('are_you_sure')}
>
{i18n.t('are_you_sure')}
</button>
<button
@ -475,6 +494,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleAddModToCommunity
)}
aria-label={i18n.t('yes')}
>
{i18n.t('yes')}
</button>
@ -484,6 +504,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleCancelConfirmAppointAsMod
)}
aria-label={i18n.t('no')}
>
{i18n.t('no')}
</button>
@ -502,12 +523,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleShowConfirmTransferCommunity
)}
aria-label={i18n.t('transfer_community')}
>
{i18n.t('transfer_community')}
</button>
) : (
<>
<button class="btn btn-link btn-animate text-muted">
<button
class="btn btn-link btn-animate text-muted"
aria-label={i18n.t('are_you_sure')}
>
{i18n.t('are_you_sure')}
</button>
<button
@ -516,6 +541,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleTransferCommunity
)}
aria-label={i18n.t('yes')}
>
{i18n.t('yes')}
</button>
@ -526,6 +552,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this
.handleCancelShowConfirmTransferCommunity
)}
aria-label={i18n.t('no')}
>
{i18n.t('no')}
</button>
@ -542,6 +569,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleModBanShow
)}
aria-label={i18n.t('ban_from_site')}
>
{i18n.t('ban_from_site')}
</button>
@ -552,6 +580,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleModBanSubmit
)}
aria-label={i18n.t('unban_from_site')}
>
{i18n.t('unban_from_site')}
</button>
@ -565,6 +594,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleShowConfirmAppointAsAdmin
)}
aria-label={
this.isAdmin
? i18n.t('remove_as_admin')
: i18n.t('appoint_as_admin')
}
>
{this.isAdmin
? i18n.t('remove_as_admin')
@ -581,6 +615,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleAddAdmin
)}
aria-label={i18n.t('yes')}
>
{i18n.t('yes')}
</button>
@ -590,6 +625,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleCancelConfirmAppointAsAdmin
)}
aria-label={i18n.t('no')}
>
{i18n.t('no')}
</button>
@ -608,12 +644,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleShowConfirmTransferSite
)}
aria-label={i18n.t('transfer_site')}
>
{i18n.t('transfer_site')}
</button>
) : (
<>
<button class="btn btn-link btn-animate text-muted">
<button
class="btn btn-link btn-animate text-muted"
aria-label={i18n.t('are_you_sure')}
>
{i18n.t('are_you_sure')}
</button>
<button
@ -622,6 +662,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleTransferSite
)}
aria-label={i18n.t('yes')}
>
{i18n.t('yes')}
</button>
@ -631,6 +672,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
this,
this.handleCancelShowConfirmTransferSite
)}
aria-label={i18n.t('no')}
>
{i18n.t('no')}
</button>
@ -652,14 +694,25 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
class="form-inline"
onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
>
<label
class="sr-only"
htmlFor={`mod-remove-reason-${cv.comment.id}`}
>
{i18n.t('reason')}
</label>
<input
type="text"
id={`mod-remove-reason-${cv.comment.id}`}
class="form-control mr-2"
placeholder={i18n.t('reason')}
value={this.state.removeReason}
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
/>
<button type="submit" class="btn btn-secondary">
<button
type="submit"
class="btn btn-secondary"
aria-label={i18n.t('remove_comment')}
>
{i18n.t('remove_comment')}
</button>
</form>
@ -702,7 +755,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
{/* </div> */}
<div class="form-group row">
<button type="submit" class="btn btn-secondary">
<button
type="submit"
class="btn btn-secondary"
aria-label={i18n.t('ban')}
>
{i18n.t('ban')} {cv.creator.name}
</button>
</div>

View file

@ -198,6 +198,7 @@ export class Communities extends Component<any, CommunitiesState> {
>
<input
type="text"
id="communities-search"
class="form-control mr-2 mb-2"
value={this.state.searchText}
placeholder={`${i18n.t('search')}...`}
@ -205,6 +206,9 @@ export class Communities extends Component<any, CommunitiesState> {
required
minLength={3}
/>
<label class="sr-only" htmlFor="communities-search">
{i18n.t('search')}
</label>
<button type="submit" class="btn btn-secondary mr-2 mb-2">
<span>{i18n.t('search')}</span>
</button>

View file

@ -253,6 +253,7 @@ export class Login extends Component<any, State> {
type="button"
class="btn btn-secondary"
onClick={linkEvent(this, this.handleRegenCaptcha)}
aria-label={i18n.t('captcha')}
>
<Icon icon="refresh-cw" classes="icon-refresh-cw" />
</button>

View file

@ -139,6 +139,9 @@ export class MarkdownTextArea extends Component<
/>
)}
</div>
<label class="sr-only" htmlFor={this.id}>
{i18n.t('body')}
</label>
</div>
<div class="row">
<div class="col-sm-12 d-flex flex-wrap">
@ -179,6 +182,7 @@ export class MarkdownTextArea extends Component<
<button
class="btn btn-sm text-muted"
data-tippy-content={i18n.t('bold')}
aria-label={i18n.t('bold')}
onClick={linkEvent(this, this.handleInsertBold)}
>
<Icon icon="bold" classes="icon-inline" />
@ -186,6 +190,7 @@ export class MarkdownTextArea extends Component<
<button
class="btn btn-sm text-muted"
data-tippy-content={i18n.t('italic')}
aria-label={i18n.t('italic')}
onClick={linkEvent(this, this.handleInsertItalic)}
>
<Icon icon="italic" classes="icon-inline" />
@ -193,6 +198,7 @@ export class MarkdownTextArea extends Component<
<button
class="btn btn-sm text-muted"
data-tippy-content={i18n.t('link')}
aria-label={i18n.t('link')}
onClick={linkEvent(this, this.handleInsertLink)}
>
<Icon icon="link" classes="icon-inline" />

View file

@ -201,7 +201,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
>
<Icon icon="bell" />
{this.state.unreadCount > 0 && (
<span class="mx-1 badge badge-light">
<span
class="mx-1 badge badge-light"
aria-label={`${this.state.unreadCount} ${i18n.t(
'unread_messages'
)}`}
>
{this.state.unreadCount}
</span>
)}
@ -281,6 +286,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
onSubmit={linkEvent(this, this.handleSearchSubmit)}
>
<input
id="search-input"
class={`form-control mr-0 search-input ${
this.state.toggleSearch ? 'show-input' : 'hide-input'
}`}
@ -291,11 +297,15 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
placeholder={i18n.t('search')}
onBlur={linkEvent(this, this.handleSearchBlur)}
></input>
<label class="sr-only" htmlFor="search-input">
{i18n.t('search')}
</label>
<button
name="search-btn"
onClick={linkEvent(this, this.handleSearchBtn)}
class="px-1 btn btn-link"
style="color: var(--gray)"
aria-label={i18n.t('search')}
>
<Icon icon="search" />
</button>
@ -312,7 +322,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
>
<Icon icon="bell" />
{this.state.unreadCount > 0 && (
<span class="ml-1 badge badge-light">
<span
class="ml-1 badge badge-light"
aria-label={`${this.state.unreadCount} ${i18n.t(
'unread_messages'
)}`}
>
{this.state.unreadCount}
</span>
)}

View file

@ -27,7 +27,6 @@ import {
debounce,
isImage,
toast,
randomStr,
setupTippy,
hostname,
pictrsDeleteToast,
@ -72,7 +71,6 @@ interface PostFormState {
}
export class PostForm extends Component<PostFormProps, PostFormState> {
private id = `post-form-${randomStr()}`;
private subscription: Subscription;
private choices: any;
private emptyState: PostFormState = {
@ -278,9 +276,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label" htmlFor={this.id}>
{i18n.t('body')}
</label>
<label class="col-sm-2 col-form-label">{i18n.t('body')}</label>
<div class="col-sm-10">
<MarkdownTextArea
initialContent={this.state.postForm.body}

View file

@ -639,6 +639,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleEditClick)}
data-tippy-content={i18n.t('edit')}
aria-label={i18n.t('edit')}
>
<Icon icon="edit" classes="icon-inline" />
</button>
@ -648,6 +649,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
data-tippy-content={
!post_view.post.deleted ? i18n.t('delete') : i18n.t('restore')
}
aria-label={
!post_view.post.deleted ? i18n.t('delete') : i18n.t('restore')
}
>
<Icon
icon="trash"
@ -675,6 +679,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleViewSource)}
data-tippy-content={i18n.t('view_source')}
aria-label={i18n.t('view_source')}
>
<Icon
icon="file-text"
@ -692,6 +697,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
data-tippy-content={
post_view.post.locked ? i18n.t('unlock') : i18n.t('lock')
}
aria-label={
post_view.post.locked ? i18n.t('unlock') : i18n.t('lock')
}
>
<Icon
icon="lock"
@ -708,6 +716,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
? i18n.t('unsticky')
: i18n.t('sticky')
}
aria-label={
post_view.post.stickied
? i18n.t('unsticky')
: i18n.t('sticky')
}
>
<Icon
icon="pin"
@ -724,6 +737,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<button
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleModRemoveShow)}
aria-label={i18n.t('remove')}
>
{i18n.t('remove')}
</button>
@ -731,6 +745,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<button
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleModRemoveSubmit)}
aria-label={i18n.t('restore')}
>
{i18n.t('restore')}
</button>
@ -745,6 +760,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this,
this.handleModBanFromCommunityShow
)}
aria-label={i18n.t('ban')}
>
{i18n.t('ban')}
</button>
@ -755,6 +771,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this,
this.handleModBanFromCommunitySubmit
)}
aria-label={i18n.t('unban')}
>
{i18n.t('unban')}
</button>
@ -764,6 +781,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<button
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleAddModToCommunity)}
aria-label={
this.isMod
? i18n.t('remove_as_mod')
: i18n.t('appoint_as_mod')
}
>
{this.isMod
? i18n.t('remove_as_mod')
@ -783,16 +805,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this,
this.handleShowConfirmTransferCommunity
)}
aria-label={i18n.t('transfer_community')}
>
{i18n.t('transfer_community')}
</button>
) : (
<>
<button class="d-inline-block mr-1 btn btn-link btn-animate text-muted py-0">
<button
class="d-inline-block mr-1 btn btn-link btn-animate text-muted py-0"
aria-label={i18n.t('are_you_sure')}
>
{i18n.t('are_you_sure')}
</button>
<button
class="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
aria-label={i18n.t('yes')}
onClick={linkEvent(this, this.handleTransferCommunity)}
>
{i18n.t('yes')}
@ -803,6 +830,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this,
this.handleCancelShowConfirmTransferCommunity
)}
aria-label={i18n.t('no')}
>
{i18n.t('no')}
</button>
@ -816,6 +844,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<button
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleModBanShow)}
aria-label={i18n.t('ban_from_site')}
>
{i18n.t('ban_from_site')}
</button>
@ -823,6 +852,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<button
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleModBanSubmit)}
aria-label={i18n.t('unban_from_site')}
>
{i18n.t('unban_from_site')}
</button>
@ -831,6 +861,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
<button
class="btn btn-link btn-animate text-muted py-0"
onClick={linkEvent(this, this.handleAddAdmin)}
aria-label={
this.isAdmin
? i18n.t('remove_as_admin')
: i18n.t('appoint_as_admin')
}
>
{this.isAdmin
? i18n.t('remove_as_admin')
@ -849,17 +884,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this,
this.handleShowConfirmTransferSite
)}
aria-label={i18n.t('transfer_site')}
>
{i18n.t('transfer_site')}
</button>
) : (
<>
<button class="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1">
<button
class="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
aria-label={i18n.t('are_you_sure')}
>
{i18n.t('are_you_sure')}
</button>
<button
class="btn btn-link btn-animate text-muted py-0 d-inline-block mr-1"
onClick={linkEvent(this, this.handleTransferSite)}
aria-label={i18n.t('yes')}
>
{i18n.t('yes')}
</button>
@ -869,6 +909,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
this,
this.handleCancelShowConfirmTransferSite
)}
aria-label={i18n.t('no')}
>
{i18n.t('no')}
</button>
@ -890,14 +931,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
class="form-inline"
onSubmit={linkEvent(this, this.handleModRemoveSubmit)}
>
<label class="sr-only" htmlFor="post-listing-remove-reason">
{i18n.t('reason')}
</label>
<input
type="text"
id="post-listing-remove-reason"
class="form-control mr-2"
placeholder={i18n.t('reason')}
value={this.state.removeReason}
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
/>
<button type="submit" class="btn btn-secondary">
<button
type="submit"
class="btn btn-secondary"
aria-label={i18n.t('remove_post')}
>
{i18n.t('remove_post')}
</button>
</form>
@ -905,12 +954,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{this.state.showBanDialog && (
<form onSubmit={linkEvent(this, this.handleModBanBothSubmit)}>
<div class="form-group row">
<label class="col-form-label" htmlFor="post-listing-reason">
<label class="col-form-label" htmlFor="post-listing-ban-reason">
{i18n.t('reason')}
</label>
<input
type="text"
id="post-listing-reason"
id="post-listing-ban-reason"
class="form-control mr-2"
placeholder={i18n.t('reason')}
value={this.state.banReason}
@ -937,7 +986,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
{/* <input type="date" class="form-control mr-2" placeholder={i18n.t('expires')} value={this.state.banExpires} onInput={linkEvent(this, this.handleModBanExpiresChange)} /> */}
{/* </div> */}
<div class="form-group row">
<button type="submit" class="btn btn-secondary">
<button
type="submit"
class="btn btn-secondary"
aria-label={i18n.t('ban')}
>
{i18n.t('ban')} {post.creator.name}
</button>
</div>