From a74b519d017e49db1fb1855e142534d552c05053 Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Wed, 23 Oct 2024 12:15:08 -0600 Subject: [PATCH 1/4] Set row label with data from highest order col --- src/components/search/SearchResultTable.jsx | 61 ++++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/components/search/SearchResultTable.jsx b/src/components/search/SearchResultTable.jsx index de8fdc72..8ef2eaf2 100644 --- a/src/components/search/SearchResultTable.jsx +++ b/src/components/search/SearchResultTable.jsx @@ -31,7 +31,7 @@ const isSortable = (column, searchDescriptor) => { return (sortBy && (!searchDescriptor.getIn(['searchQuery', 'rel']) || sortBy.indexOf('/0/') === -1)); }; -const rowRenderer = (params, location) => { +const rowRenderer = (params, location, primaryCol) => { // 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 +58,8 @@ const rowRenderer = (params, location) => { // onRowMouseOver || // onRowRightClick ) { - a11yProps['aria-label'] = 'row'; + const label = rowData.get(primaryCol); + a11yProps['aria-label'] = label || 'row'; a11yProps.tabIndex = 0; if (onRowClick) { @@ -156,6 +157,7 @@ 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); @@ -193,6 +195,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, @@ -271,12 +302,16 @@ export default class SearchResultTable extends Component { location = locationGetter(rowData); } - return rowRenderer(params, location); + const columnConfig = this.getColumnConfig(); + const primaryCol = Object.keys(columnConfig) + .filter((col) => col !== 'workflowState') + .at(0); + + return rowRenderer(params, location, primaryCol); } renderTable() { const { - columnSetName, config, formatCellData, formatColumnLabel, @@ -288,8 +323,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 +353,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) From 331345d32d2bf63bb936650611aca0b0d06df574 Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Wed, 23 Oct 2024 13:17:59 -0600 Subject: [PATCH 2/4] Improve aria label and push logic into its own function --- src/components/search/SearchResultTable.jsx | 50 ++++++++++++++++----- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/src/components/search/SearchResultTable.jsx b/src/components/search/SearchResultTable.jsx index 8ef2eaf2..ec1f1944 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, primaryCol) => { +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,8 +63,7 @@ const rowRenderer = (params, location, primaryCol) => { // onRowMouseOver || // onRowRightClick ) { - const label = rowData.get(primaryCol); - a11yProps['aria-label'] = label || 'row'; + a11yProps['aria-label'] = ariaLabel; a11yProps.tabIndex = 0; if (onRowClick) { @@ -123,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 @@ -163,6 +168,7 @@ export default class SearchResultTable extends Component { 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); } @@ -284,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, @@ -302,12 +332,9 @@ export default class SearchResultTable extends Component { location = locationGetter(rowData); } - const columnConfig = this.getColumnConfig(); - const primaryCol = Object.keys(columnConfig) - .filter((col) => col !== 'workflowState') - .at(0); + const ariaLabel = this.renderRowLabel(params, totalItems); - return rowRenderer(params, location, primaryCol); + return rowRenderer(params, location, ariaLabel); } renderTable() { @@ -421,6 +448,7 @@ export default class SearchResultTable extends Component { } const height = (heightBasis * rowHeight) + rowHeight; + const renderWithTotal = (params) => this.renderRow(params, totalItems); return (
@@ -435,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={renderWithTotal} />
); From d6886b0660d42797b2f9d7e9d7626689dc12b10d Mon Sep 17 00:00:00 2001 From: Michael Ritter Date: Wed, 23 Oct 2024 13:35:52 -0600 Subject: [PATCH 3/4] Add stub intl object to props --- .../search/SearchResultTable.spec.jsx | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) 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( { Date: Wed, 23 Oct 2024 13:46:12 -0600 Subject: [PATCH 4/4] Minor update to function name --- src/components/search/SearchResultTable.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/search/SearchResultTable.jsx b/src/components/search/SearchResultTable.jsx index ec1f1944..6f840cfc 100644 --- a/src/components/search/SearchResultTable.jsx +++ b/src/components/search/SearchResultTable.jsx @@ -448,7 +448,7 @@ export default class SearchResultTable extends Component { } const height = (heightBasis * rowHeight) + rowHeight; - const renderWithTotal = (params) => this.renderRow(params, totalItems); + const renderRowWithTotal = (params) => this.renderRow(params, totalItems); return (
@@ -463,7 +463,7 @@ export default class SearchResultTable extends Component { sortBy={sortColumnName} sortDirection={sortDir === 'desc' ? Table.SortDirection.DESC : Table.SortDirection.ASC} noRowsRenderer={this.renderNoItems} - rowRenderer={renderWithTotal} + rowRenderer={renderRowWithTotal} />
);