Skip to content

Commit

Permalink
Merge pull request #55 from Qvant-lab/datepicker-accessibility
Browse files Browse the repository at this point in the history
[QDatePicker] add keyboard nav
  • Loading branch information
cheesytim authored Feb 8, 2021
2 parents 77ca2a9 + de17091 commit 97c4891
Show file tree
Hide file tree
Showing 23 changed files with 683 additions and 88 deletions.
2 changes: 1 addition & 1 deletion src/qComponents/QButton/src/q-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
outline: none;
}

&:hover {
&:hover:not([disabled]) {
background-color: var(--color-primary);
background-image: none;
box-shadow: -1px -1px 4px rgba(var(--color-rgb-white), 0.25),
Expand Down
94 changes: 53 additions & 41 deletions src/qComponents/QDatePicker/src/QDatePicker.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<template>
<div v-click-outside="handleClose">
<q-input
v-if="!ranged"
v-if="!isRanged"
ref="reference"
class="q-date-editor"
:class="['q-date-editor', { 'q-input_focus': pickerVisible }]"
:readonly="!editable"
:disabled="pickerDisabled"
:name="name"
:placeholder="placeholder || $t('QDatePicker.placeholder')"
:value="displayValue"
@focus="handleFocus"
@keydown.native="handleKeydown"
@keyup.native="handleKeyUp"
@input="handleInput"
@change="handleChange"
@mouseenter.native="handleMouseEnter"
Expand All @@ -27,10 +27,12 @@
v-else
ref="reference"
:class="rangeClasses"
tabindex="0"
@click="handleRangeClick"
@mouseenter="handleMouseEnter"
@mouseleave="showClose = false"
@keydown="handleKeydown"
@keyup="handleKeyUp"
@focus="handleFocus"
>
<input
autocomplete="off"
Expand All @@ -40,7 +42,7 @@
:disabled="pickerDisabled"
:name="name && name[0]"
readonly
@focus="handleFocus"
tabindex="-1"
/>
<slot name="range-separator">
<span class="q-range-separator">{{ rangeSeparator }}</span>
Expand All @@ -53,7 +55,7 @@
:name="name && name[1]"
class="q-range-input"
readonly
@focus="handleFocus"
tabindex="-1"
/>
<span
:class="iconClass"
Expand Down Expand Up @@ -305,7 +307,8 @@ export default {
return {
'q-date-editor': true,
'q-range-editor': true,
'q-range-editor_disabled': this.pickerDisabled
'q-range-editor_disabled': this.pickerDisabled,
'q-range-editor_focused': this.pickerVisible
};
},
iconClass() {
Expand All @@ -330,7 +333,7 @@ export default {
}
},
ranged() {
isRanged() {
return this.type.includes('range');
},
Expand Down Expand Up @@ -447,6 +450,7 @@ export default {
});
panel.style.zIndex = this.$Q?.zIndex ?? 2000;
document.addEventListener('keyup', this.handleKeyUp, true);
},
destroyPopper() {
Expand All @@ -459,10 +463,12 @@ export default {
if (dropdown?.parentNode === document.body) {
document.body.removeChild(dropdown);
}
document.removeEventListener('keyup', this.handleKeyUp, true);
},
focus() {
if (!this.ranged) {
if (!this.isRanged) {
this.$refs.reference.focus();
} else {
this.handleFocus();
Expand All @@ -484,6 +490,44 @@ export default {
}
},
handleKeyUp(e) {
// if user is typing, do not let picker handle key input
if (this.userInput) {
e.stopPropagation();
}
switch (e.key) {
case 'ArrowRight':
case 'ArrowUp':
case 'ArrowLeft':
case 'ArrowDown': {
this.$refs.panel.navigateDropdown(e);
break;
}
case 'Escape': {
this.pickerVisible = false;
e.stopPropagation();
break;
}
case 'Tab': {
if (this.$el.contains(document.activeElement)) {
this.$refs.panel.navigateDropdown(e);
return;
}
if (!this.isRanged) {
this.handleChange();
}
this.pickerVisible = false;
e.stopPropagation();
break;
}
default:
break;
}
},
handleChange() {
let value;
let format;
Expand Down Expand Up @@ -563,38 +607,6 @@ export default {
);
},
handleKeydown(event) {
const keyCode = event.keyCode;
// ESC
if (keyCode === 27) {
this.pickerVisible = false;
event.stopPropagation();
return;
}
// Tab
if (keyCode === 9) {
if (!this.ranged) {
this.handleChange();
this.pickerVisible = false;
event.stopPropagation();
} else {
// user may change focus between two input
setTimeout(() => {
this.pickerVisible = false;
event.stopPropagation();
}, 0);
}
return;
}
// if user is typing, do not let picker handle key input
if (this.userInput) {
event.stopPropagation();
}
},
handleRangeClick() {
this.pickerVisible = true;
this.$emit('focus', this);
Expand Down
3 changes: 2 additions & 1 deletion src/qComponents/QDatePicker/src/basic/date-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<button
:class="getCellClasses(cell)"
type="button"
tabindex="-1"
@click="handleClick(cell)"
>
{{ cell.text }}
Expand Down Expand Up @@ -243,7 +244,7 @@ export default {
methods: {
getCellClasses(cell) {
const classes = ['cell'];
const classes = ['cell', 'cell_date'];
if (['today', 'prev-month', 'next-month'].includes(cell.type)) {
classes.push(`cell_${cell.type}`);
}
Expand Down
7 changes: 5 additions & 2 deletions src/qComponents/QDatePicker/src/basic/month-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
:class="getCellClasses(cell)"
:disabled="cell.disabled"
type="button"
tabindex="-1"
@click="handleMonthTableClick(cell)"
>
{{ getMonthName(cell.text) }}
Expand Down Expand Up @@ -123,12 +124,14 @@ export default {
let maxDate = this.maxDate;
let minDate = this.minDate;
if (this.rangeState.selecting) {
maxDate = this.rangeState.endDate;
}
minDate = startOfMonth(minDate);
maxDate = startOfMonth(maxDate) || minDate;
maxDate = maxDate ? startOfMonth(maxDate) : minDate;
[minDate, maxDate] = [
Math.min(minDate, maxDate),
Math.max(minDate, maxDate)
Expand Down Expand Up @@ -161,7 +164,7 @@ export default {
},
getCellClasses(cell) {
const classes = ['cell'];
const classes = ['cell', 'cell_month'];
if (this.value && isSameMonth(this.value, cell.month))
classes.push('cell_current');
if (cell.type === 'today') classes.push('cell_today');
Expand Down
5 changes: 3 additions & 2 deletions src/qComponents/QDatePicker/src/basic/year-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
:class="getCellClasses(cell)"
:disabled="cell.disabled"
type="button"
tabindex="-1"
>
{{ cell.year.getFullYear() }}
</button>
Expand Down Expand Up @@ -114,7 +115,7 @@ export default {
}
minDate = startOfMonth(minDate);
maxDate = startOfMonth(maxDate) || minDate;
maxDate = maxDate ? startOfMonth(maxDate) : minDate;
[minDate, maxDate] = [
Math.min(minDate, maxDate),
Math.max(minDate, maxDate)
Expand Down Expand Up @@ -153,7 +154,7 @@ export default {
},
getCellClasses(cell) {
const style = ['cell'];
const style = ['cell', 'cell_year'];
if (this.selectionMode === 'year') {
if (
isDate(this.value) &&
Expand Down
38 changes: 27 additions & 11 deletions src/qComponents/QDatePicker/src/panel/date-range.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
</button>
</div>
</slot>
<div :class="leftPanelClasses">
<div
ref="leftPanel"
:class="leftPanelClasses"
>
<div class="q-picker-panel__header">
<button
type="button"
Expand Down Expand Up @@ -64,7 +67,10 @@
@pick="handleRangePick"
/>
</div>
<div :class="rightPanelClasses">
<div
ref="rightPanel"
:class="rightPanelClasses"
>
<div class="q-picker-panel__header">
<button
type="button"
Expand Down Expand Up @@ -115,8 +121,10 @@
>
<div class="q-picker-panel__timepicker">
<time-panel
ref="leftTimePanel"
class="time-panel time-panel_no-left-borders time-panel_no-right-borders"
:value="parsedLeftTime"
:panel-in-focus="panelInFocus === 'timeLeft'"
:disabled="isLeftTimeDisabled"
:disabled-values="disabledValues.time"
:prefix-to-time="$t('QDatePicker.timeFrom')"
Expand All @@ -125,6 +133,8 @@
</div>
<div class="q-picker-panel__timepicker">
<time-panel
ref="rightTimePanel"
:panel-in-focus="panelInFocus === 'timeRight'"
:disabled="isLeftTimeDisabled"
class="time-panel time-panel_no-left-borders"
:value="parsedRightTime"
Expand All @@ -142,16 +152,19 @@
<script>
import { addMonths, isDate, isSameDay, isSameMonth, subMonths } from 'date-fns';
import DateTable from '../basic/date-table';
import rangeMixin from './mixin';
import rangeMixin from './range-mixin';
import focusMixin from './focus-mixin';
import focusTimeMixin from './focus-time-mixin';
import { setTimeToDate } from '../helpers';
import { addZero } from '../../../helpers/dateHelpers';
import TimePanel from '../../../QTimePicker/src/components/panel';
const MONTH_COUNT = 12;
const MONTHS_COUNT = 12;
export default {
name: 'QDatePickerPanelDateRange',
components: { DateTable, TimePanel },
mixins: [rangeMixin],
mixins: [rangeMixin, focusMixin, focusTimeMixin],
props: {
firstDayOfWeek: {
type: Number,
Expand Down Expand Up @@ -192,7 +205,8 @@ export default {
rangeState: {
endDate: null,
selecting: false
}
},
isRanged: true
};
},
Expand Down Expand Up @@ -248,15 +262,17 @@ export default {
return {
'q-picker-panel__content': true,
'q-picker-panel__content_no-left-borders': true,
'q-picker-panel__content_no-right-borders': this.showTime
'q-picker-panel__content_no-right-borders': this.showTime,
'q-picker-panel__content_focused': this.panelInFocus === 'right'
};
},
leftPanelClasses() {
return {
'q-picker-panel__content': true,
'q-picker-panel__content_no-left-borders':
this.$slots.sidebar || this.shortcuts.length,
'q-picker-panel__content_no-right-borders': true
'q-picker-panel__content_no-right-borders': true,
'q-picker-panel__content_focused': this.panelInFocus === 'left'
};
},
Expand All @@ -275,10 +291,10 @@ export default {
enableYearArrow() {
return (
this.rightYear * MONTH_COUNT +
this.rightYear * MONTHS_COUNT +
this.rightMonth -
(this.leftYear * MONTH_COUNT + this.leftMonth + 1) >=
MONTH_COUNT
(this.leftYear * MONTHS_COUNT + this.leftMonth + 1) >=
MONTHS_COUNT
);
}
},
Expand Down
Loading

0 comments on commit 97c4891

Please sign in to comment.