Skip to content

Commit

Permalink
Merge pull request #3234 from primefaces/issue-3233
Browse files Browse the repository at this point in the history
Sidebar accessibility completed
  • Loading branch information
tugcekucukoglu authored Nov 11, 2022
2 parents a112b4c + 83ec528 commit 4b8dd3a
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 19 deletions.
6 changes: 0 additions & 6 deletions api-generator/components/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ const SidebarProps = [
type: 'boolean',
default: 'true',
description: 'Whether to a modal layer behind the sidebar.'
},
{
name: 'ariaCloseLabel',
type: 'string',
default: 'close',
description: 'Aria label of the close icon.'
}
];

Expand Down
5 changes: 0 additions & 5 deletions src/components/sidebar/Sidebar.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,6 @@ export interface SidebarProps {
* Default value is true.
*/
modal?: boolean | undefined;
/**
* Aria label of the close icon.
* Default value is 'close'.
*/
ariaCloseLabel?: string | undefined;
}

export interface SidebarSlots {
Expand Down
40 changes: 32 additions & 8 deletions src/components/sidebar/Sidebar.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<template>
<Portal>
<transition name="p-sidebar" @enter="onEnter" @leave="onLeave" @after-leave="onAfterLeave" appear>
<div v-if="visible" :ref="containerRef" :class="containerClass" role="complementary" :aria-modal="modal" v-bind="$attrs">
<div v-if="visible" :ref="containerRef" v-focustrap :class="containerClass" role="complementary" :aria-modal="modal" :aria-labelledby="ariaId" v-bind="$attrs">
<div class="p-sidebar-header">
<div v-if="$slots.header" class="p-sidebar-header-content">
<slot name="header"></slot>
</div>
<button v-if="showCloseIcon" v-ripple class="p-sidebar-close p-sidebar-icon p-link" @click="hide" :aria-label="ariaCloseLabel" type="button">
<button v-if="showCloseIcon" v-ripple class="p-sidebar-close p-sidebar-icon p-link" @click="hide" :aria-label="closeAriaLabel" type="button">
<span :class="['p-sidebar-close-icon', closeIcon]" />
</button>
</div>
Expand All @@ -19,9 +19,10 @@
</template>

<script>
import FocusTrap from 'primevue/focustrap';
import Portal from 'primevue/portal';
import Ripple from 'primevue/ripple';
import { DomHandler, ZIndexUtils } from 'primevue/utils';
import { DomHandler, UniqueComponentId, ZIndexUtils } from 'primevue/utils';
export default {
name: 'Sidebar',
Expand Down Expand Up @@ -59,17 +60,15 @@ export default {
modal: {
type: Boolean,
default: true
},
ariaCloseLabel: {
type: String,
default: 'close'
}
},
mask: null,
documentKeydownListener: null,
maskClickListener: null,
container: null,
beforeUnmount() {
this.destroyModal();
this.unbindDocumentKeyDownListener();
if (this.container && this.autoZIndex) {
ZIndexUtils.clear(this.container);
Expand All @@ -83,6 +82,7 @@ export default {
},
onEnter(el) {
this.$emit('show');
this.bindDocumentKeyDownListener();
if (this.autoZIndex) {
ZIndexUtils.set('modal', el, this.baseZIndex || this.$primevue.config.zIndex.modal);
Expand All @@ -101,6 +101,11 @@ export default {
this.disableModality();
}
},
onKeyDown(event) {
if (event.code === 'Escape') {
this.hide();
}
},
onAfterLeave(el) {
if (this.autoZIndex) {
ZIndexUtils.clear(el);
Expand Down Expand Up @@ -150,6 +155,18 @@ export default {
this.maskClickListener = null;
}
},
bindDocumentKeyDownListener() {
if (!this.documentKeydownListener) {
this.documentKeydownListener = this.onKeyDown.bind(this);
window.document.addEventListener('keydown', this.documentKeydownListener);
}
},
unbindDocumentKeyDownListener() {
if (this.documentKeydownListener) {
window.document.removeEventListener('keydown', this.documentKeydownListener);
this.documentKeydownListener = null;
}
},
destroyModal() {
if (this.mask) {
this.unbindMaskClickListener();
Expand All @@ -175,10 +192,17 @@ export default {
},
fullScreen() {
return this.position === 'full';
},
closeAriaLabel() {
return this.$primevue.config.locale.aria ? this.$primevue.config.locale.aria.close : undefined;
},
ariaId() {
return UniqueComponentId();
}
},
directives: {
ripple: Ripple
ripple: Ripple,
focustrap: FocusTrap
},
components: {
Portal: Portal
Expand Down
74 changes: 74 additions & 0 deletions src/views/sidebar/SidebarDoc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,80 @@ import Sidebar from 'primevue/sidebar';
</table>
</div>

<h5>Accessibility</h5>
<h6>Screen Reader</h6>
<p>
Sidebar component uses <i>complementary</i> role by default, since any attribute is passed to the root element aria role can be changed depending on your use case and additional attributes like <i>aria-labelledby</i> can be added. In
addition <i>aria-modal</i> is added since focus is kept within the sidebar when opened.
</p>
<p>It is recommended to use a trigger component that can be accessed with keyboard such as a button, if not adding <i>tabIndex</i> would be necessary.</p>
<p>Trigger element also requires <i>aria-expanded</i> and <i>aria-controls</i> to be handled explicitly.</p>

<pre v-code><code>
&lt;Button label="Show" icon="pi pi-external-link" @click="visible = true" :aria-controls="visible ? 'sbar' : null" :aria-expanded="visible ? true : false" /&gt;

&lt;Sidebar id="sbar" v-model="visible" role="region"&gt;
&lt;p&gt;Content&lt;/p&gt;
&lt;/Sidebar&gt;

</code></pre>

<h6>Overlay Keyboard Support</h6>
<div className="doc-tablewrapper">
<table className="doc-table">
<thead>
<tr>
<th>Key</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<i>tab</i>
</td>
<td>Moves focus to the next the focusable element within the sidebar.</td>
</tr>
<tr>
<td><i>shift</i> + <i>tab</i></td>
<td>Moves focus to the previous the focusable element within the sidebar.</td>
</tr>
<tr>
<td>
<i>escape</i>
</td>
<td>Closes the dialog if <i>closeOnEscape</i> is true.</td>
</tr>
</tbody>
</table>
</div>

<h6>Close Button Keyboard Support</h6>
<div className="doc-tablewrapper">
<table className="doc-table">
<thead>
<tr>
<th>Key</th>
<th>Function</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<i>enter</i>
</td>
<td>Closes the sidebar.</td>
</tr>
<tr>
<td>
<i>space</i>
</td>
<td>Closes the sidebar.</td>
</tr>
</tbody>
</table>
</div>

<h5>Dependencies</h5>
<p>None.</p>
</AppDoc>
Expand Down

0 comments on commit 4b8dd3a

Please sign in to comment.