From 0094cbb7d1ba2deb7f3fea3f65e140dc5c8ff3ac Mon Sep 17 00:00:00 2001 From: Agate <me@agate.blue> Date: Fri, 3 Jul 2020 16:07:44 +0200 Subject: [PATCH] Fix #1128: Lock focus in modals to improve accessibility --- changes/changelog.d/1121.enhancement | 1 + front/package.json | 1 + front/src/components/Queue.vue | 5 ++++- front/src/components/SetInstanceModal.vue | 2 +- front/src/components/ShortcutsModal.vue | 2 +- front/src/components/auth/LoginForm.vue | 3 +-- front/src/components/common/DangerousButton.vue | 8 ++++---- front/src/components/moderation/FilterModal.vue | 4 ++-- front/src/components/moderation/ReportModal.vue | 2 +- front/src/components/semantic/Modal.vue | 12 +++++++++++- front/yarn.lock | 13 +++++++++++++ 11 files changed, 40 insertions(+), 13 deletions(-) create mode 100644 changes/changelog.d/1121.enhancement diff --git a/changes/changelog.d/1121.enhancement b/changes/changelog.d/1121.enhancement new file mode 100644 index 000000000..188865c94 --- /dev/null +++ b/changes/changelog.d/1121.enhancement @@ -0,0 +1 @@ +Use semantic headers for accessibility (#1121) \ No newline at end of file diff --git a/front/package.json b/front/package.json index 4a85f6469..86540e149 100644 --- a/front/package.json +++ b/front/package.json @@ -20,6 +20,7 @@ "core-js": "^3.6.4", "diff": "^4.0.1", "django-channels": "^1.1.6", + "focus-trap": "^5.1.0", "fomantic-ui-css": "^2.8.3", "howler": "^2.0.14", "js-logger": "^1.4.1", diff --git a/front/src/components/Queue.vue b/front/src/components/Queue.vue index 4becb11f3..0813291af 100644 --- a/front/src/components/Queue.vue +++ b/front/src/components/Queue.vue @@ -209,7 +209,7 @@ import $ from 'jquery' import moment from "moment" import lodash from '@/lodash' import time from "@/utils/time" - +import createFocusTrap from 'focus-trap' import store from "@/store" export default { @@ -224,11 +224,14 @@ export default { showVolume: false, isShuffling: false, tracksChangeBuffer: null, + focusTrap: null, time } }, mounted () { let self = this + this.focusTrap = createFocusTrap(this.$el, {allowOutsideClick: () => { return true }}) + this.focusTrap.activate() this.$nextTick(() => { setTimeout(() => { this.scrollToCurrent() diff --git a/front/src/components/SetInstanceModal.vue b/front/src/components/SetInstanceModal.vue index 618a16a20..2e5c24bc2 100644 --- a/front/src/components/SetInstanceModal.vue +++ b/front/src/components/SetInstanceModal.vue @@ -35,7 +35,7 @@ </form> </div> <div class="actions"> - <div class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></div> + <button class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></button> </div> </modal> </template> diff --git a/front/src/components/ShortcutsModal.vue b/front/src/components/ShortcutsModal.vue index 017cdc977..c88bfb715 100644 --- a/front/src/components/ShortcutsModal.vue +++ b/front/src/components/ShortcutsModal.vue @@ -36,7 +36,7 @@ </div> </section> <footer class="actions"> - <div class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Close</translate></div> + <button class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Close</translate></button> </footer> </modal> </template> diff --git a/front/src/components/auth/LoginForm.vue b/front/src/components/auth/LoginForm.vue index 3b18fbc29..f0591a538 100644 --- a/front/src/components/auth/LoginForm.vue +++ b/front/src/components/auth/LoginForm.vue @@ -25,7 +25,6 @@ </label> <input ref="username" - tabindex="1" required name="username" type="text" @@ -50,7 +49,7 @@ <translate translate-context="Contant/Auth/Paragraph" :translate-params="{domain: $store.getters['instance/domain']}">You will be redirected to %{ domain } to authenticate.</translate> </p> </template> - <button tabindex="3" :class="['ui', {'loading': isLoading}, 'right', 'floated', buttonClasses, 'button']" type="submit"> + <button :class="['ui', {'loading': isLoading}, 'right', 'floated', buttonClasses, 'button']" type="submit"> <translate translate-context="*/Login/*/Verb">Login</translate> </button> </form> diff --git a/front/src/components/common/DangerousButton.vue b/front/src/components/common/DangerousButton.vue index 353c8ecf3..763f3fa98 100644 --- a/front/src/components/common/DangerousButton.vue +++ b/front/src/components/common/DangerousButton.vue @@ -14,14 +14,14 @@ </div> </div> <div class="actions"> - <div class="ui basic cancel button"> + <button class="ui basic cancel button"> <translate translate-context="*/*/Button.Label/Verb">Cancel</translate> - </div> - <div :class="['ui', 'confirm', confirmButtonColor, 'button']" @click="confirm"> + </button> + <button :class="['ui', 'confirm', confirmButtonColor, 'button']" @click="confirm"> <slot name="modal-confirm"> <translate translate-context="Modal/*/Button.Label/Short, Verb">Confirm</translate> </slot> - </div> + </button> </div> </modal> </div> diff --git a/front/src/components/moderation/FilterModal.vue b/front/src/components/moderation/FilterModal.vue index 2c650014c..f83e69741 100644 --- a/front/src/components/moderation/FilterModal.vue +++ b/front/src/components/moderation/FilterModal.vue @@ -37,8 +37,8 @@ </div> </div> <div class="actions"> - <div class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></div> - <div :class="['ui', 'success', {loading: isLoading}, 'button']" @click="hide"><translate translate-context="Popup/*/Button.Label">Hide content</translate></div> + <button class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></button> + <button :class="['ui', 'success', {loading: isLoading}, 'button']" @click="hide"><translate translate-context="Popup/*/Button.Label">Hide content</translate></button> </div> </modal> </template> diff --git a/front/src/components/moderation/ReportModal.vue b/front/src/components/moderation/ReportModal.vue index db37960a2..f32f7b6f6 100644 --- a/front/src/components/moderation/ReportModal.vue +++ b/front/src/components/moderation/ReportModal.vue @@ -71,7 +71,7 @@ </div> </div> <div class="actions"> - <div class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></div> + <button class="ui basic cancel button"><translate translate-context="*/*/Button.Label/Verb">Cancel</translate></button> <button v-if="canSubmit" :class="['ui', 'success', {loading: isLoading}, 'button']" diff --git a/front/src/components/semantic/Modal.vue b/front/src/components/semantic/Modal.vue index 78f953421..0a93a053e 100644 --- a/front/src/components/semantic/Modal.vue +++ b/front/src/components/semantic/Modal.vue @@ -9,6 +9,7 @@ <script> import $ from 'jquery' +import createFocusTrap from 'focus-trap' export default { props: { @@ -17,9 +18,13 @@ export default { }, data () { return { - control: null + control: null, + focusTrap: null, } }, + mounted () { + this.focusTrap = createFocusTrap(this.$el) + }, beforeDestroy () { if (this.control) { $(this.$el).modal('hide') @@ -38,6 +43,11 @@ export default { }.bind(this), onHidden: function () { this.$emit('update:show', false) + this.focusTrap.pause() + }.bind(this), + onVisible: function () { + this.focusTrap.activate() + this.focusTrap.unpause() }.bind(this) }) } diff --git a/front/yarn.lock b/front/yarn.lock index 40aa5b9d0..4cab2dee1 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -4375,6 +4375,14 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +focus-trap@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-5.1.0.tgz#64a0bfabd95c382103397dbc96bfef3a3cf8e5ad" + integrity sha512-CkB/nrO55069QAUjWFBpX6oc+9V90Qhgpe6fBWApzruMq5gnlh90Oo7iSSDK7pKiV5ugG6OY2AXM5mxcmL3lwQ== + dependencies: + tabbable "^4.0.0" + xtend "^4.0.1" + follow-redirects@1.5.10: version "1.5.10" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" @@ -9206,6 +9214,11 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +tabbable@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261" + integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ== + table@^5.2.3: version "5.4.6" resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" -- GitLab