Accessibility in React ContextMenu component
23 Feb 2021 / 2 minutes to read
ARIA attributes
The web accessibility makes web content and web applications more accessible for people
with disabilities. It especially helps in dynamic content change and development of advanced user interface
controls with AJAX, HTML, JavaScript, and related technologies.
ContextMenu provides built-in compliance with WAI-ARIA
specifications. WAI-ARIA
support is
achieved through the attributes like aria-expanded
, aria-haspopup
applied for menu item in
ContextMenu. It helps the people with disabilities by providing information about the widget for assistive
technology in the screen readers. ContextMenu component contains the menu
role and menuItem
role.
Properties
Functionality
menu
This role will be specified for an item which have sub menu.
menuItem
This role will be specified for an item which do not have sub menus.
aria-haspopup
Indicates the availability and type of interactive popup element.
aria-expanded
Indicates whether the subtree can be expanded or collapsed, as well as whether its current state is expanded or collapsed.
Keyboard interaction
Keyboard shortcuts
Actions
Esc
Closes the opened ContextMenu.
Enter
Selects the focused item.
Up
Navigates up or to the previous menu item.
Down
Navigates down or to the next menu item.
Left
Close the current sub menu and navigates to parent menu..
Right
Navigates and open the next sub menu.
import { enableRipple } from '@syncfusion/ej2-base' ;
import { ContextMenuComponent, MenuItemModel } from '@syncfusion/ej2-react-navigations' ;
import * as React from 'react' ;
import * as ReactDom from 'react-dom' ;
enableRipple ( true ) ;
class App extends React . Component< { } , { } > {
public cmenu: ContextMenuComponent;
public menuItems: MenuItemModel[ ] = [
{
iconCss: 'e-menu-icons e-cut' ,
text: 'Cut'
} ,
{
iconCss: 'e-icons e-copy' ,
text: 'Copy'
} ,
{
iconCss: 'e-menu-icons e-paste' ,
items: [
{
iconCss: 'e-cm-icons e-pastetext' ,
text: 'Paste Text' ,
} ,
{
iconCss: 'e-cm-icons e-pastespecial' ,
text: 'Paste Special'
}
] ,
text: 'Paste'
} ] ;
constructor ( props: any ) {
super ( props) ;
this . onCreated = this . onCreated . bind ( this ) ;
}
public onCreated ( ) {
this . cmenu. open ( 40 , 20 ) ;
}
public render ( ) {
return (
< div className = " container" >
< div id = ' target' > Right click / Touch hold to open the ContextMenu </ div>
<ContextMenuComponent ref= { ( scope ) => this . cmenu = scope as ContextMenuComponent} id='contextmenu' items= { this . menuItems} created= { this . onCreated} />
</ div>
) ;
}
}
ReactDom. render ( < App /> , document. getElementById ( 'element' ) ) ;
<! DOCTYPE html >
< html lang = " en" >
< head>
< title> Syncfusion React ContextMenu</ title>
< meta charset = " utf-8" />
< meta name = " viewport" content = " width=device-width, initial-scale=1.0" />
< meta name = " description" content = " Essential JS 2 for React Components" />
< meta name = " author" content = " Syncfusion" />
< link href = " //cdn.syncfusion.com/ej2/ej2-base/styles/material.css" rel = " stylesheet" />
< link href = " //cdn.syncfusion.com/ej2/ej2-buttons/styles/material.css" rel = " stylesheet" />
< link href = " //cdn.syncfusion.com/ej2/ej2-lists/styles/material.css" rel = " stylesheet" />
< link href = " //cdn.syncfusion.com/ej2/ej2-inputs/styles/material.css" rel = " stylesheet" />
< link href = " //cdn.syncfusion.com/ej2/ej2-popups/styles/material.css" rel = " stylesheet" />
< link href = " //cdn.syncfusion.com/ej2/ej2-navigations/styles/material.css" rel = " stylesheet" />
< link href = " index.css" rel = " stylesheet" />
< script src = " https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.38/system.js" > </ script>
< script src = " systemjs.config.js" > </ script>
</ head>
< body>
< div id = ' element' >
< div id = ' loader' > Loading....</ div>
</ div>
</ body>
</ html>
@font-face {
font-family : 'e-context-menu' ;
src :
url ( data:application/x-font-ttf;charset=utf-8;base64,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) format ( 'truetype' ) ;
font-weight : normal;
font-style : normal;
}
.e-cm-icons {
font-family : 'e-context-menu' ;
font-style : normal;
font-variant : normal;
font-weight : normal;
line-height : 1;
text-transform : none;
}
#loader {
color : #008cff;
height : 40px;
left : 45%;
position : absolute;
top : 45%;
width : 30%;
}
@font-face {
font-family : 'ddb-icons' ;
src :
url ( data:application/x-font-ttf;charset=utf-8;base64,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) format ( 'truetype' ) ;
font-weight : normal;
font-style : normal;
}
.e-menu-icons {
font-family : 'ddb-icons' !important ;
speak : none;
font-size : 55px;
font-style : normal;
font-weight : normal;
font-variant : normal;
text-transform : none;
line-height : 1;
-webkit-font-smoothing : antialiased;
-moz-osx-font-smoothing : grayscale;
}
.e-cut::before {
content : '\e700' ;
}
.e-copy::before {
content : '\e70a' ;
}
.e-paste::before {
content : '\e701' ;
}
.e-pastespecial::before {
content : '\e260' ;
}
.e-pastetext::before {
content : '\e261' ;
}
#target {
border : 1px dashed;
height : 150px;
padding : 10px;
position : relative;
text-align : justify;
color : gray;
user-select : none;
}
import { enableRipple } from '@syncfusion/ej2-base' ;
import { ContextMenuComponent } from '@syncfusion/ej2-react-navigations' ;
import * as React from 'react' ;
import * as ReactDom from 'react-dom' ;
enableRipple ( true ) ;
class App extends React. Component {
constructor ( props ) {
super ( props) ;
this . menuItems = [
{
iconCss: 'e-menu-icons e-cut' ,
text: 'Cut'
} ,
{
iconCss: 'e-icons e-copy' ,
text: 'Copy'
} ,
{
iconCss: 'e-menu-icons e-paste' ,
items: [
{
iconCss: 'e-cm-icons e-pastetext' ,
text: 'Paste Text' ,
} ,
{
iconCss: 'e-cm-icons e-pastespecial' ,
text: 'Paste Special'
}
] ,
text: 'Paste'
}
] ;
this . onCreated = this . onCreated . bind ( this ) ;
}
onCreated ( ) {
this . cmenu. open ( 40 , 20 ) ;
}
render ( ) {
return ( < div className = " container" >
< div id = ' target' > Right click / Touch hold to open the ContextMenu </ div>
< ContextMenuComponent ref = { ( scope ) => this . cmenu = scope} id = ' contextmenu' items = { this . menuItems} created = { this . onCreated} />
</ div> ) ;
}
}
ReactDom. render ( < App /> , document. getElementById ( 'element' ) ) ;