Skip to content

Commit 19df51f

Browse files
committed
Fix and Improve period selection via keyboard navigation
1 parent 1c0268d commit 19df51f

File tree

7 files changed

+168
-93
lines changed

7 files changed

+168
-93
lines changed

plugins/CoreHome/vue/dist/CoreHome.umd.js

Lines changed: 67 additions & 42 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/CoreHome/vue/dist/CoreHome.umd.min.js

Lines changed: 29 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/CoreHome/vue/src/ExpandOnClick/ExpandOnClick.ts

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,61 @@ import DirectiveUtilities from '../directiveUtilities';
1212
interface ExpandOnClickArgs {
1313
// input (specified by user)
1414
expander: string | HTMLElement,
15-
onClosed?: () => void;
15+
onClosed?: (event: MouseEvent|KeyboardEvent) => void;
16+
onExpand?: (event: MouseEvent|KeyboardEvent) => void;
1617

1718
// state
1819
isMouseDown?: boolean;
1920
hasScrolled?: boolean;
2021

21-
// event handlers
22-
onExpand?: () => void;
22+
// internal event handlers
23+
onClickOnExpander?: (event: MouseEvent|KeyboardEvent) => void;
2324
onClickOutsideElement?: (event: MouseEvent) => void;
2425
onScroll?: () => void;
2526
onMouseDown?: () => void;
2627
onEscapeHandler?: (event: KeyboardEvent) => void;
2728
}
2829

29-
function onExpand(element: HTMLElement) {
30-
element.classList.toggle('expanded');
30+
function expand(
31+
element: HTMLElement,
32+
binding: DirectiveBinding<ExpandOnClickArgs>,
33+
event: MouseEvent|KeyboardEvent,
34+
) {
35+
element.classList.add('expanded');
36+
if (binding.value?.onExpand) {
37+
binding.value.onExpand(event);
38+
}
3139

3240
const positionElement = element.querySelector('.dropdown.positionInViewport');
3341
if (positionElement) {
3442
Matomo.helper.setMarginLeftToBeInViewport(positionElement);
3543
}
3644
}
3745

46+
function close(
47+
element: HTMLElement,
48+
binding: DirectiveBinding<ExpandOnClickArgs>,
49+
event: MouseEvent|KeyboardEvent,
50+
) {
51+
element.classList.remove('expanded');
52+
53+
if (binding.value?.onClosed) {
54+
binding.value.onClosed(event);
55+
}
56+
}
57+
58+
function onClickOnExpander(
59+
element: HTMLElement,
60+
binding: DirectiveBinding<ExpandOnClickArgs>,
61+
event: MouseEvent|KeyboardEvent,
62+
) {
63+
if (element.classList.contains('expanded')) {
64+
close(element, binding, event);
65+
} else {
66+
expand(element, binding, event);
67+
}
68+
}
69+
3870
function onClickOutsideElement(
3971
element: HTMLElement,
4072
binding: DirectiveBinding<ExpandOnClickArgs>,
@@ -49,11 +81,7 @@ function onClickOutsideElement(
4981
}
5082

5183
if (!element.contains(event.target as HTMLElement)) {
52-
element.classList.remove('expanded');
53-
54-
if (binding.value?.onClosed) {
55-
binding.value.onClosed();
56-
}
84+
close(element, binding, event);
5785
}
5886
}
5987

@@ -71,15 +99,14 @@ function onEscapeHandler(
7199
binding: DirectiveBinding<ExpandOnClickArgs>,
72100
event: KeyboardEvent,
73101
) {
74-
if (event.which === 27) {
102+
if (event.key === 'Escape') {
75103
binding.value.isMouseDown = false;
76104
binding.value.hasScrolled = false;
77-
element.classList.remove('expanded');
105+
close(element, binding, event);
78106
}
79107
}
80108

81109
const doc = document.documentElement;
82-
const { $ } = window;
83110

84111
/**
85112
* Usage (in a component):
@@ -93,7 +120,7 @@ export default {
93120
mounted(el: HTMLElement, binding: DirectiveBinding<ExpandOnClickArgs>): void {
94121
binding.value.isMouseDown = false;
95122
binding.value.hasScrolled = false;
96-
binding.value.onExpand = onExpand.bind(null, el);
123+
binding.value.onClickOnExpander = onClickOnExpander.bind(null, el, binding);
97124
binding.value.onEscapeHandler = onEscapeHandler.bind(null, el, binding);
98125
binding.value.onMouseDown = onMouseDown.bind(null, binding);
99126
binding.value.onClickOutsideElement = onClickOutsideElement.bind(null, el, binding);
@@ -102,7 +129,7 @@ export default {
102129
setTimeout(() => {
103130
const expander = DirectiveUtilities.getRef(binding.value.expander, binding);
104131
if (expander) {
105-
$(expander).on('click', binding.value.onExpand!);
132+
expander.addEventListener('click', binding.value.onClickOnExpander!);
106133
}
107134
});
108135
doc.addEventListener('keyup', binding.value.onEscapeHandler);
@@ -113,7 +140,7 @@ export default {
113140
unmounted(el: HTMLElement, binding: DirectiveBinding<ExpandOnClickArgs>): void {
114141
const expander = DirectiveUtilities.getRef(binding.value.expander, binding);
115142
if (expander) {
116-
$(expander).off('click', binding.value.onExpand!);
143+
doc.removeEventListener('click', binding.value.onClickOnExpander!);
117144
}
118145
doc.removeEventListener('keyup', binding.value.onEscapeHandler!);
119146
doc.removeEventListener('mousedown', binding.value.onMouseDown!);

plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@
105105
}
106106

107107
.periodSelector-withPrevNext {
108-
a.title {
108+
button.title {
109109
width: calc(~'100% - 40px');
110110
text-align: center;
111111

plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.vue

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@
1010
ref="root"
1111
class="periodSelector piwikSelector"
1212
:class="{'periodSelector-withPrevNext': canShowMovePeriod}"
13-
v-expand-on-click="{ expander: 'title' }"
13+
v-expand-on-click="{
14+
expander: 'title',
15+
onExpand: onExpand,
16+
onClosed: onClosed,
17+
}"
1418
>
1519
<button
1620
v-if="canShowMovePeriod"
@@ -21,17 +25,17 @@
2125
<span class="icon-chevron-left"></span>
2226
</button>
2327

24-
<a
28+
<button
2529
ref="title"
2630
id="date"
2731
class="title"
28-
tabindex="-1"
32+
tabindex="4"
2933
v-tooltips
3034
:title="translate('General_ChooseDate', currentlyViewingText)"
3135
>
3236
<span class="icon icon-calendar" />
3337
{{ currentlyViewingText }}
34-
</a>
38+
</button>
3539

3640
<div
3741
id="periodMore"
@@ -436,6 +440,18 @@ export default defineComponent({
436440
},
437441
},
438442
methods: {
443+
onExpand(event: MouseEvent|KeyboardEvent) {
444+
const isKeyboardEvent = event.detail === 0;
445+
if (isKeyboardEvent) {
446+
window.$(this.$refs.root as HTMLElement).find('.ui-datepicker-month').focus();
447+
}
448+
},
449+
onClosed(event: MouseEvent|KeyboardEvent) {
450+
const isKeyboardEvent = event.detail === 0;
451+
if (isKeyboardEvent) {
452+
window.$(this.$refs.title as HTMLElement).focus();
453+
}
454+
},
439455
handleZIndexPositionRelativeCompareDropdownIssue() {
440456
const $element = window.$(this.$refs.root as HTMLElement);
441457
$element.on('focus', '#comparePeriodToDropdown .select-dropdown', () => {

plugins/Morpheus/stylesheets/general/_default.less

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ a {
132132
}
133133

134134
a:focus-visible,
135-
button:focus-visible {
135+
button:focus-visible,
136+
input[type="button"]:focus-visible,
137+
select:focus-visible {
136138
outline: 2px solid @theme-color-focus-ring;
137139
outline-offset: -2px;
138140
}

plugins/Morpheus/stylesheets/ui/_buttons.less

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ input[type="submit"].btn,
2424
text-decoration: none;
2525
}
2626

27+
&:focus-visible {
28+
outline: 2px solid @theme-color-focus-ring;
29+
outline-offset: -2px;
30+
}
31+
2732
em {
2833
font-style: normal;
2934
}

0 commit comments

Comments
 (0)