Skip to content

Commit

Permalink
Fix #3263: Datatable aria label fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Sep 9, 2022
1 parent 21282ee commit 5212b2b
Show file tree
Hide file tree
Showing 13 changed files with 164 additions and 136 deletions.
6 changes: 6 additions & 0 deletions api-generator/components/datatable.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ const DataTableProps = [
default: 'null',
description: 'Selected row in single mode or an array of values in multiple mode.'
},
{
name: 'selectionAriaLabel',
type: 'string',
default: 'null',
description: 'A field property from the row to add "Select {field}" and "Unselect {field}" ARIA labels to checkbox/radio buttons.'
},
{
name: 'contextMenuSelection',
type: 'any',
Expand Down
22 changes: 15 additions & 7 deletions components/doc/datatable/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { memo } from 'react';
import Link from 'next/link';
import { TabView, TabPanel } from '../../lib/tabview/TabView';
import { useLiveEditorTabs } from '../common/liveeditor';
import React, { memo } from 'react';
import { TabPanel, TabView } from '../../lib/tabview/TabView';
import { CodeHighlight } from '../common/codehighlight';
import { DevelopmentSection } from '../common/developmentsection';
import { useLiveEditorTabs } from '../common/liveeditor';

const DataTableDoc = memo(() => {
const sources = {
Expand Down Expand Up @@ -229,7 +229,7 @@ export class DataTableDemo extends Component {
filters={this.state.filters} filterDisplay="menu" loading={this.state.loading} responsiveLayout="scroll"
globalFilterFields={['name', 'country.name', 'representative.name', 'balance', 'status']} emptyMessage="No customers found."
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
<Column selectionMode="multiple" headerStyle={{ width: '3em' }}></Column>
<Column selectionMode="multiple" selectionAriaLabel="name" headerStyle={{ width: '3em' }}></Column>
<Column field="name" header="Name" sortable filter filterPlaceholder="Search by name" style={{ minWidth: '14rem' }} />
<Column field="country.name" header="Country" sortable filterField="country.name" style={{ minWidth: '14rem' }} body={this.countryBodyTemplate} filter filterPlaceholder="Search by country" />
<Column header="Agent" sortable sortField="representative.name" filterField="representative" showFilterMatchModes={false} filterMenuStyle={{ width: '14rem' }} style={{ minWidth: '14rem' }} body={this.representativeBodyTemplate}
Expand Down Expand Up @@ -448,7 +448,7 @@ const DataTableDemo = () => {
filters={filters} filterDisplay="menu" loading={loading} responsiveLayout="scroll"
globalFilterFields={['name', 'country.name', 'representative.name', 'balance', 'status']} emptyMessage="No customers found."
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
<Column selectionMode="multiple" headerStyle={{ width: '3em' }}></Column>
<Column selectionMode="multiple" selectionAriaLabel="name" headerStyle={{ width: '3em' }}></Column>
<Column field="name" header="Name" sortable filter filterPlaceholder="Search by name" style={{ minWidth: '14rem' }} />
<Column field="country.name" header="Country" sortable filterField="country.name" style={{ minWidth: '14rem' }} body={countryBodyTemplate} filter filterPlaceholder="Search by country" />
<Column header="Agent" sortable sortField="representative.name" filterField="representative" showFilterMatchModes={false} filterMenuStyle={{ width: '14rem' }} style={{ minWidth: '14rem' }} body={representativeBodyTemplate}
Expand Down Expand Up @@ -666,7 +666,7 @@ const DataTableDemo = () => {
filters={filters} filterDisplay="menu" loading={loading} responsiveLayout="scroll"
globalFilterFields={['name', 'country.name', 'representative.name', 'balance', 'status']} emptyMessage="No customers found."
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
<Column selectionMode="multiple" headerStyle={{ width: '3em' }}></Column>
<Column selectionMode="multiple" selectionAriaLabel="name" headerStyle={{ width: '3em' }}></Column>
<Column field="name" header="Name" sortable filter filterPlaceholder="Search by name" style={{ minWidth: '14rem' }} />
<Column field="country.name" header="Country" sortable filterField="country.name" style={{ minWidth: '14rem' }} body={countryBodyTemplate} filter filterPlaceholder="Search by country" />
<Column header="Agent" sortable sortField="representative.name" filterField="representative" showFilterMatchModes={false} filterMenuStyle={{ width: '14rem' }} style={{ minWidth: '14rem' }} body={representativeBodyTemplate}
Expand Down Expand Up @@ -892,7 +892,7 @@ const DataTableDemo = () => {
filters={filters} filterDisplay="menu" loading={loading} responsiveLayout="scroll"
globalFilterFields={['name', 'country.name', 'representative.name', 'balance', 'status']} emptyMessage="No customers found."
currentPageReportTemplate="Showing {first} to {last} of {totalRecords} entries">
<Column selectionMode="multiple" headerStyle={{ width: '3em' }}></Column>
<Column selectionMode="multiple" selectionAriaLabel="name" headerStyle={{ width: '3em' }}></Column>
<Column field="name" header="Name" sortable filter filterPlaceholder="Search by name" style={{ minWidth: '14rem' }} />
<Column field="country.name" header="Country" sortable filterField="country.name" style={{ minWidth: '14rem' }} body={countryBodyTemplate} filter filterPlaceholder="Search by country" />
<Column header="Agent" sortable sortField="representative.name" filterField="representative" showFilterMatchModes={false} filterMenuStyle={{ width: '14rem' }} style={{ minWidth: '14rem' }} body={representativeBodyTemplate}
Expand Down Expand Up @@ -3229,6 +3229,14 @@ export const DataTableStateDemo = () => {
<td>null</td>
<td>Selected row in single mode or an array of values in multiple mode.</td>
</tr>
<tr>
<td>selectionAriaLabel</td>
<td>string</td>
<td>null</td>
<td>
A field property from the row to add "Select {field}" and "Unselect {field}" ARIA labels to checkbox/radio buttons.
</td>
</tr>
<tr>
<td>contextMenuSelection</td>
<td>any</td>
Expand Down
5 changes: 4 additions & 1 deletion components/lib/api/Locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ let locales = {
equals: 'Equals',
notEquals: 'Not equals',
noFilter: 'No Filter',
filter: 'Filter',
lt: 'Less than',
lte: 'Less than or equal to',
gt: 'Greater than',
Expand Down Expand Up @@ -52,7 +53,9 @@ let locales = {
firstPageLabel: 'First Page',
lastPageLabel: 'Last Page',
nextPageLabel: 'Next Page',
previousPageLabel: 'Previous Page'
previousPageLabel: 'Previous Page',
selectLabel: 'Select',
unselectLabel: 'Unselect'
}
}
};
Expand Down
11 changes: 9 additions & 2 deletions components/lib/datatable/BodyCell.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as React from 'react';
import { ariaLabel } from '../api/Api';
import { useEventListener, useUnmountEffect, useUpdateEffect } from '../hooks/Hooks';
import { OverlayService } from '../overlayservice/OverlayService';
import { Ripple } from '../ripple/Ripple';
Expand Down Expand Up @@ -504,11 +505,17 @@ export const BodyCell = React.memo((props) => {

if (selectionMode) {
const showSelection = props.showSelectionElement ? props.showSelectionElement(props.rowData, { rowIndex: props.rowIndex, props: props.tableProps }) : true;
let label;
if (showSelection) {
const ariaLabelField = props.selectionAriaLabel || props.tableProps.dataKey;
const ariaLabelText = ObjectUtils.resolveFieldData(props.rowData, ariaLabelField);
label = `${props.selected ? ariaLabel('unselectLabel') : ariaLabel('selectLabel')} ${ariaLabelText}`;
}

content = showSelection && (
<>
{selectionMode === 'single' && <RowRadioButton checked={props.selected} onChange={onRadioChange} tabIndex={props.tabIndex} tableSelector={props.tableSelector} />}
{selectionMode === 'multiple' && <RowCheckbox checked={props.selected} onChange={onCheckboxChange} tabIndex={props.tabIndex} />}
{selectionMode === 'single' && <RowRadioButton checked={props.selected} onChange={onRadioChange} tabIndex={props.tabIndex} tableSelector={props.tableSelector} ariaLabel={label} />}
{selectionMode === 'multiple' && <RowCheckbox checked={props.selected} onChange={onCheckboxChange} tabIndex={props.tabIndex} ariaLabel={label} />}
</>
);
} else if (rowReorder) {
Expand Down
1 change: 1 addition & 0 deletions components/lib/datatable/BodyRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export const BodyRow = React.memo((props) => {
onEditingMetaChange={props.onEditingMetaChange}
onRowToggle={props.onRowToggle}
selection={props.selection}
selectionAriaLabel={props.tableProps.selectionAriaLabel}
allowCellSelection={props.allowCellSelection}
compareSelectionBy={props.compareSelectionBy}
selectOnEdit={props.selectOnEdit}
Expand Down
18 changes: 8 additions & 10 deletions components/lib/datatable/ColumnFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,10 @@ export const ColumnFilter = React.memo((props) => {
];
};

const filterLabel = () => {
return localeOption('filter');
};

const noFilterLabel = () => {
return localeOption('noFilter');
};
Expand Down Expand Up @@ -450,12 +454,9 @@ export const ColumnFilter = React.memo((props) => {
'p-column-filter-menu-button-open': overlayVisibleState,
'p-column-filter-menu-button-active': hasFilter()
});
const label = filterLabel();

return (
<button ref={iconRef} type="button" className={className} aria-haspopup aria-expanded={overlayVisibleState} onClick={toggleMenu} onKeyDown={onToggleButtonKeyDown}>
<span className="pi pi-filter-icon pi-filter"></span>
</button>
);
return <Button ref={iconRef} type="button" icon="pi pi-filter-icon pi-filter" className={className} aria-haspopup aria-expanded={overlayVisibleState} onClick={toggleMenu} onKeyDown={onToggleButtonKeyDown} aria-label={label} />;
}

return null;
Expand All @@ -466,12 +467,9 @@ export const ColumnFilter = React.memo((props) => {
const className = classNames('p-column-filter-clear-button p-link', {
'p-hidden-space': !hasRowFilter()
});
const clearLabel = clearButtonLabel();

return (
<button className={className} type="button" onClick={clearFilter}>
<span className="pi pi-filter-slash"></span>
</button>
);
return <Button ref={iconRef} type="button" icon="pi pi-filter-slash" className={className} onClick={clearFilter} aria-label={clearLabel} />;
}

return null;
Expand Down
1 change: 1 addition & 0 deletions components/lib/datatable/DataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -1756,6 +1756,7 @@ DataTable.defaultProps = {
dragSelection: false,
cellSelection: false,
selection: null,
selectionAriaLabel: null,
onSelectionChange: null,
contextMenuSelection: null,
onContextMenuSelectionChange: null,
Expand Down
2 changes: 1 addition & 1 deletion components/lib/datatable/RowCheckbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const RowCheckbox = React.memo((props) => {

return (
<div className={className} onClick={onClick}>
<div className={boxClassName} role="checkbox" aria-checked={props.checked} tabIndex={tabIndex} onKeyDown={onKeyDown} onFocus={onFocus} onBlur={onBlur}>
<div className={boxClassName} role="checkbox" aria-checked={props.checked} tabIndex={tabIndex} onKeyDown={onKeyDown} onFocus={onFocus} onBlur={onBlur} aria-label={props.ariaLabel}>
<span className={iconClassName}></span>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion components/lib/datatable/RowRadioButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const RowRadioButton = React.memo((props) => {
return (
<div className={className}>
<div className="p-hidden-accessible">
<input name={name} ref={inputRef} type="radio" checked={props.checked} onFocus={onFocus} onBlur={onBlur} onChange={onChange} onKeyDown={onKeyDown} />
<input name={name} ref={inputRef} type="radio" checked={props.checked} onFocus={onFocus} onBlur={onBlur} onChange={onChange} onKeyDown={onKeyDown} aria-label={props.ariaLabel} />
</div>
<div className={boxClassName} onClick={onClick} role="radio" aria-checked={props.checked}>
<div className="p-radiobutton-icon"></div>
Expand Down
Loading

0 comments on commit 5212b2b

Please sign in to comment.