Issue 11450 Adding keyboard accessibility to task board controls and purchases (#12363)

* intial draft adding keyboard accessibility to task board controls

* cleanup

* finish adding keyboard accessibility for task dropdown, rewards

* add notEnough conditions to disable purchase button

* fix(lint): remove console.log from buy modal

* add missing comma, use focus-within instead of focus-visible

* missed one more focus visible

* override browser default focus styling

* move focus styling to tasks only

* add rounded border

* fix element height on focus, rounded borders

* fix dropdown margin to avoid element resizing

* styling updates on focus

* fix spacing around task checklist

* fix border around dropdown item

* remove spacing that made tasks with notes jump

* keep disabled habit control styling when not focused

* revert unintended spacing

Co-authored-by: Matteo Pagliazzi <matteopagliazzi@gmail.com>
This commit is contained in:
Alexandrea Beh
2020-09-25 03:29:13 -05:00
committed by GitHub
parent 411ac94986
commit b056763f09
5 changed files with 114 additions and 16 deletions

View File

@@ -38,6 +38,9 @@
}, controlClass.up.inner]"
@click="(isUser && task.up && (!task.group.approval.requested
|| task.group.approval.approved)) ? score('up') : null"
@keypress.enter="(isUser && task.up && (!task.group.approval.requested
|| task.group.approval.approved)) ? score('up') : null"
tabindex="0"
>
<div
v-if="!isUser"
@@ -65,6 +68,9 @@
:class="controlClass.inner"
@click="isUser && !task.group.approval.requested
? score(task.completed ? 'down' : 'up' ) : null"
@keypress.enter="isUser && !task.group.approval.requested
? score(task.completed ? 'down' : 'up' ) : null"
tabindex="0"
>
<div
v-if="!isUser"
@@ -92,6 +98,8 @@
class="task-clickable-area"
:class="{'task-clickable-area-user': isUser}"
@click="edit($event, task)"
@keypress.enter="edit($event, task)"
tabindex="0"
>
<div class="d-flex justify-content-between">
<h3
@@ -103,6 +111,7 @@
v-if="!isRunningYesterdailies && showOptions"
ref="taskDropdown"
v-b-tooltip.hover.top="$t('options')"
tabindex="0"
class="task-dropdown"
:right="task.type === 'reward'"
>
@@ -117,6 +126,8 @@
v-if="showEdit"
ref="editTaskItem"
class="dropdown-item edit-task-item"
tabindex="0"
@keypress.enter="edit($event, task)"
>
<span class="dropdown-icon-item">
<span
@@ -130,6 +141,8 @@
v-if="isUser"
class="dropdown-item"
@click="moveToTop"
tabindex="0"
@keypress.enter="moveToTop"
>
<span class="dropdown-icon-item">
<span
@@ -143,6 +156,8 @@
v-if="isUser"
class="dropdown-item"
@click="moveToBottom"
tabindex="0"
@keypress.enter="moveToBottom"
>
<span class="dropdown-icon-item">
<span
@@ -156,6 +171,8 @@
v-if="showDelete"
class="dropdown-item"
@click="destroy"
tabindex="0"
@keypress.enter="destroy"
>
<span class="dropdown-icon-item delete-task-item">
<span
@@ -185,7 +202,9 @@
? 'expand': 'collapse'}Checklist`)"
class="collapse-checklist mb-2 d-flex align-items-center expand-toggle"
:class="{open: !task.collapseChecklist}"
tabindex="0"
@click="collapseChecklist(task)"
@keypress.enter="collapseChecklist(task)"
>
<div
v-once
@@ -207,10 +226,12 @@
<input
:id="`checklist-${item.id}-${random}`"
class="custom-control-input"
tabindex="0"
type="checkbox"
:checked="item.completed"
:disabled="castingSpell || !isUser"
@change="toggleChecklistItem(item)"
@keypress.enter="toggleChecklistItem(item)"
>
<label
v-markdown="item.text"
@@ -330,6 +351,9 @@
}, controlClass.down.inner]"
@click="(isUser && task.down && (!task.group.approval.requested
|| task.group.approval.approved)) ? score('down') : null"
@keypress.enter="(isUser && task.down && (!task.group.approval.requested
|| task.group.approval.approved)) ? score('down') : null"
tabindex="0"
>
<div
v-if="!isUser"
@@ -350,6 +374,8 @@
class="right-control d-flex align-items-center justify-content-center reward-control"
:class="controlClass.bg"
@click="isUser ? score('down') : null"
@keypress.enter="isUser ? score('down') : null"
tabindex="0"
>
<div
class="svg-icon"
@@ -373,6 +399,19 @@
<!-- eslint-disable max-len -->
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
.task-best-control-inner-habit:focus {
transition: none;
}
*:focus {
outline: none;
transition: none;
border: $purple-400 solid 1px;
:not(task-best-control-inner-habit) { // round icon
border-radius: 2px;
}
}
.control-bottom-box {
border-bottom-left-radius: 0 !important;
@@ -395,14 +434,16 @@
border-radius: 2px;
position: relative;
&:hover:not(.task-not-editable) {
&:hover:not(.task-not-editable),
&:focus-within:not(.task-not-editable) {
box-shadow: 0 1px 8px 0 rgba($black, 0.12), 0 4px 4px 0 rgba($black, 0.16);
z-index: 11;
}
}
.task:not(.groupTask) {
&:hover {
&:hover,
&:focus-within {
.left-control, .right-control, .task-content {
border-color: $purple-400;
}
@@ -410,7 +451,8 @@
}
.task.groupTask {
&:hover:not(.task-not-editable) {
&:hover:not(.task-not-editable),
&:focus-within:not(.task-not-editable) {
border: $purple-400 solid 1px;
border-radius: 3px;
margin: -1px; // to counter the border width
@@ -455,10 +497,16 @@
.task-clickable-area {
padding: 7px 8px;
padding-bottom: 0px;
border: transparent solid 1px;
&-user {
padding-right: 0px;
}
&:focus {
border-radius: 2px;
border: $purple-400 solid 1px;
}
}
.task-title + .task-dropdown ::v-deep .dropdown-menu {
@@ -482,6 +530,20 @@
opacity: 1;
}
.task:focus-within ::v-deep .habitica-menu-dropdown .habitica-menu-dropdown-toggle {
opacity: 1;
}
.task ::v-deep .habitica-menu-dropdown:focus-within {
opacity: 1;
border: $purple-400 solid 1px;
border-radius: 2px;
}
.task ::v-deep .habitica-menu-dropdown {
border: transparent solid 1px;
}
.task-clickable-area ::v-deep .habitica-menu-dropdown.open .habitica-menu-dropdown-toggle {
opacity: 1;
@@ -494,20 +556,26 @@
color: $purple-400 !important;
}
.task-clickable-area ::v-deep .habitica-menu-dropdown .habitica-menu-dropdown-toggle:focus-within .svg-icon {
color: $purple-400 !important;
}
.task-dropdown {
max-height: 16px;
max-height: 18px;
}
.task-dropdown ::v-deep .dropdown-menu {
.dropdown-item {
cursor: pointer !important;
transition: none;
border: transparent solid 1px;
* {
transition: none;
}
&:hover {
&:hover,
&:focus {
color: $purple-300;
.svg-icon.push-to-top, .svg-icon.push-to-bottom {
@@ -516,6 +584,11 @@
}
}
}
&:focus {
border-radius: 2px;
border: $purple-400 solid 1px;
}
}
}
@@ -551,12 +624,8 @@
}
}
.checklist {
&.isOpen {
margin-bottom: 2px;
}
margin-top: -3px;
.checklist.isOpen {
margin-bottom: 2px;
}
.collapse-checklist {
@@ -567,8 +636,10 @@
line-height: 1.2;
text-align: center;
color: $gray-200;
border: transparent solid 1px;
&.open {
&:focus {
border: $purple-400 solid 1px;
}
span {
@@ -706,6 +777,11 @@
transition-duration: 0.15s;
transition-property: border-color, background, color;
transition-timing-function: ease-in;
border: transparent solid 1px;
&:focus {
border: $purple-400 solid 1px;
}
}
.left-control {
border-top-left-radius: 2px;
@@ -1012,7 +1088,6 @@ export default {
},
edit (e, task) {
if (this.isRunningYesterdailies || !this.showEdit) return;
const target = e.target || e.srcElement;
/*