diff --git a/src/components/search/SearchResultTable.jsx b/src/components/search/SearchResultTable.jsx
index de8fdc72..6f840cfc 100644
--- a/src/components/search/SearchResultTable.jsx
+++ b/src/components/search/SearchResultTable.jsx
@@ -1,7 +1,7 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import Immutable from 'immutable';
-import { defineMessages, FormattedMessage } from 'react-intl';
+import { defineMessages, intlShape, FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import get from 'lodash/get';
import { Table } from 'cspace-layout';
@@ -16,6 +16,11 @@ const messages = defineMessages({
id: 'searchResultTable.searchPending',
defaultMessage: '⋯',
},
+ rowLabel: {
+ id: 'searchResultTable.rowLabel',
+ description: 'The aria-label for a row',
+ defaultMessage: '{primary} selected row {index} of {total}',
+ },
});
/**
@@ -31,7 +36,7 @@ const isSortable = (column, searchDescriptor) => {
return (sortBy && (!searchDescriptor.getIn(['searchQuery', 'rel']) || sortBy.indexOf('/0/') === -1));
};
-const rowRenderer = (params, location) => {
+const rowRenderer = (params, location, ariaLabel) => {
// This is a fork of react-virtualized's default row renderer:
// https://github.com/bvaughn/react-virtualized/blob/master/source/Table/defaultRowRenderer.js
@@ -58,7 +63,7 @@ const rowRenderer = (params, location) => {
// onRowMouseOver ||
// onRowRightClick
) {
- a11yProps['aria-label'] = 'row';
+ a11yProps['aria-label'] = ariaLabel;
a11yProps.tabIndex = 0;
if (onRowClick) {
@@ -122,6 +127,7 @@ const propTypes = {
}).isRequired,
formatCellData: PropTypes.func,
formatColumnLabel: PropTypes.func,
+ intl: intlShape,
isSearchPending: PropTypes.bool,
linkItems: PropTypes.bool,
// eslint-disable-next-line react/forbid-prop-types
@@ -156,11 +162,13 @@ export default class SearchResultTable extends Component {
constructor() {
super();
+ this.getColumnConfig = this.getColumnConfig.bind(this);
this.getItemLocation = this.getItemLocation.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleRowClick = this.handleRowClick.bind(this);
this.renderNoItems = this.renderNoItems.bind(this);
this.renderRow = this.renderRow.bind(this);
+ this.renderRowLabel = this.renderRowLabel.bind(this);
this.sort = this.sort.bind(this);
}
@@ -193,6 +201,35 @@ export default class SearchResultTable extends Component {
}
}
+ getColumnConfig() {
+ const {
+ columnSetName,
+ config,
+ searchDescriptor,
+ } = this.props;
+
+ const recordType = searchDescriptor.get('recordType');
+ const subresource = searchDescriptor.get('subresource');
+
+ const columnConfigurer = subresource
+ ? config.subresources[subresource]
+ : config.recordTypes[recordType];
+
+ let columnConfig = get(columnConfigurer, ['columns', columnSetName]);
+
+ if (!columnConfig && columnSetName !== defaultProps.columnSetName) {
+ // Fall back to the default column set if the named one doesn't exist.
+
+ columnConfig = get(columnConfigurer, ['columns', defaultProps.columnSetName]);
+ }
+
+ if (!columnConfig) {
+ columnConfig = [];
+ }
+
+ return columnConfig;
+ }
+
getItemLocation(item) {
const {
config,
@@ -253,7 +290,31 @@ export default class SearchResultTable extends Component {
return
{message}
;
}
- renderRow(params) {
+ renderRowLabel(params, totalItems) {
+ const {
+ intl,
+ } = this.props;
+
+ const {
+ index,
+ rowData,
+ } = params;
+
+ const columnConfig = this.getColumnConfig();
+ const primaryCol = Object.keys(columnConfig)
+ .filter((col) => col !== 'workflowState')
+ .at(0);
+
+ const primaryData = rowData.get(primaryCol);
+ const label = primaryData
+ ? intl.formatMessage(messages.rowLabel,
+ { primary: primaryData, index: index + 1, total: totalItems })
+ : 'row';
+
+ return label;
+ }
+
+ renderRow(params, totalItems) {
const {
getItemLocation,
linkItems,
@@ -271,12 +332,13 @@ export default class SearchResultTable extends Component {
location = locationGetter(rowData);
}
- return rowRenderer(params, location);
+ const ariaLabel = this.renderRowLabel(params, totalItems);
+
+ return rowRenderer(params, location, ariaLabel);
}
renderTable() {
const {
- columnSetName,
config,
formatCellData,
formatColumnLabel,
@@ -288,8 +350,6 @@ export default class SearchResultTable extends Component {
} = this.props;
if (searchResult) {
- const recordType = searchDescriptor.get('recordType');
- const subresource = searchDescriptor.get('subresource');
const searchQuery = searchDescriptor.get('searchQuery');
const listTypeConfig = config.listTypes[listType];
@@ -320,21 +380,7 @@ export default class SearchResultTable extends Component {
items = Immutable.List.of(items);
}
- const columnConfigurer = subresource
- ? config.subresources[subresource]
- : config.recordTypes[recordType];
-
- let columnConfig = get(columnConfigurer, ['columns', columnSetName]);
-
- if (!columnConfig && columnSetName !== defaultProps.columnSetName) {
- // Fall back to the default column set if the named one doesn't exist.
-
- columnConfig = get(columnConfigurer, ['columns', defaultProps.columnSetName]);
- }
-
- if (!columnConfig) {
- columnConfig = [];
- }
+ const columnConfig = this.getColumnConfig();
const columns = Object.keys(columnConfig)
.filter((name) => !columnConfig[name].disabled)
@@ -402,6 +448,7 @@ export default class SearchResultTable extends Component {
}
const height = (heightBasis * rowHeight) + rowHeight;
+ const renderRowWithTotal = (params) => this.renderRow(params, totalItems);
return (
@@ -416,7 +463,7 @@ export default class SearchResultTable extends Component {
sortBy={sortColumnName}
sortDirection={sortDir === 'desc' ? Table.SortDirection.DESC : Table.SortDirection.ASC}
noRowsRenderer={this.renderNoItems}
- rowRenderer={this.renderRow}
+ rowRenderer={renderRowWithTotal}
/>
);
diff --git a/test/specs/components/search/SearchResultTable.spec.jsx b/test/specs/components/search/SearchResultTable.spec.jsx
index 7f375361..693df9d1 100644
--- a/test/specs/components/search/SearchResultTable.spec.jsx
+++ b/test/specs/components/search/SearchResultTable.spec.jsx
@@ -145,6 +145,10 @@ const searchResult = Immutable.fromJS({
},
});
+const intl = {
+ formatMessage: (message) => `formatted ${message.id}`,
+};
+
describe('SearchResultTable', () => {
beforeEach(function before() {
this.container = createTestContainer(this);
@@ -153,7 +157,10 @@ describe('SearchResultTable', () => {
it('should render as a div', function test() {
render(
-
+
, this.container,
);
@@ -166,6 +173,7 @@ describe('SearchResultTable', () => {
@@ -183,6 +191,7 @@ describe('SearchResultTable', () => {
@@ -200,6 +209,7 @@ describe('SearchResultTable', () => {
@@ -216,6 +226,7 @@ describe('SearchResultTable', () => {
@@ -230,6 +241,7 @@ describe('SearchResultTable', () => {
@@ -244,6 +256,7 @@ describe('SearchResultTable', () => {
{
{
{
@@ -322,6 +338,7 @@ describe('SearchResultTable', () => {
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{
render(
{