Skip to content

Lookup - Screen readers do not pick up NoDataText when Lookup is empty - #35026

Open
dmlvr wants to merge 3 commits into
DevExpress:mainfrom
dmlvr:T1334729_26_2
Open

Lookup - Screen readers do not pick up NoDataText when Lookup is empty#35026
dmlvr wants to merge 3 commits into
DevExpress:mainfrom
dmlvr:T1334729_26_2

Conversation

@dmlvr

@dmlvr dmlvr commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@dmlvr dmlvr self-assigned this Sep 2, 2026
@dmlvr dmlvr added the 26_2 label Sep 2, 2026
@dmlvr
dmlvr marked this pull request as ready for review September 3, 2026 13:40
@dmlvr
dmlvr requested review from a team as code owners September 3, 2026 13:40
Copilot AI lite review requested due to automatic review settings September 3, 2026 13:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The change is narrowly scoped, includes targeted automated test coverage for the new accessibility behavior, and updates dependent assertions/snapshots accordingly.

Pull request overview

This PR improves List/Lookup accessibility by introducing a dedicated, visually hidden live region (role="status") in the List base implementation so screen readers reliably announce list state changes—particularly noDataText when a Lookup search yields no results.

Changes:

  • Added a persistent screen-reader-only live region to ListBase and updated its text based on loading state, item count, and noDataText.
  • Updated existing List QUnit tests that previously asserted element.text() (now affected by the live region text).
  • Added new QUnit coverage for announcements in both List and Lookup, and updated affected Jest snapshots.
File summaries
File Description
packages/devextreme/testing/tests/DevExpress.ui.widgets/listParts/commonTests.js Adjusts text assertions to ignore the new live-region text; adds QUnit accessibility tests for announcements.
packages/devextreme/testing/tests/DevExpress.ui.widgets.editors/lookup.tests.js Adds QUnit tests verifying Lookup’s internal list announces item count / noDataText on search without recreating the live region.
packages/devextreme/js/__internal/ui/list/list.base.ts Implements the live region container and announcement updates in ListBase.
packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/snapshots/view.integration.test.tsx.snap Updates snapshots to include the newly rendered live region in lists used by header filter UI.
packages/devextreme/js/__internal/grids/new/grid_core/filtering/header_filter/snapshots/options.integration.test.ts.snap Updates snapshots to include the newly rendered live region in lists used by header filter UI.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

const itemsCount = this._editStrategy.itemsGetter().length;
const itemsLabel = messageLocalization.format('dxList-listAriaLabel');

this._$a11yStatusContainer?.text(itemsCount ? `${itemsLabel}: ${itemsCount}` : noDataText ?? '');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check what we need to announce.

const itemsCount = this._editStrategy.itemsGetter().length;
const itemsLabel = messageLocalization.format('dxList-listAriaLabel');

this._$a11yStatusContainer?.text(itemsCount ? `${itemsLabel}: ${itemsCount}` : noDataText ?? '');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check ?? ''

@dmlvr dmlvr Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noDataText from options

const { noDataText } = this.option() 

can be undefined. But dxElementWrapper.text do not support undefined

dxElementWrapper.text(text: string | number | boolean). 

So this is the best way to support typing

Image

const LIST_SELECT_RADIOBUTTON = 'dx-list-select-radiobutton';
const WRAP_ITEM_TEXT_CLASS = 'dx-wrap-item-text';
const SELECT_ALL_ITEM_SELECTOR = '.dx-list-select-all';
export const SCREEN_READER_ONLY_CLASS = 'dx-screen-reader-only';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if it can be imported.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const LIST_SELECT_RADIOBUTTON = 'dx-list-select-radiobutton';
const WRAP_ITEM_TEXT_CLASS = 'dx-wrap-item-text';
const SELECT_ALL_ITEM_SELECTOR = '.dx-list-select-all';
export const SCREEN_READER_ONLY_CLASS = 'dx-screen-reader-only';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if we can reuse dx-gridbase-a11y-status-container

@dmlvr dmlvr Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_renderA11yStatusContainer(): void {
const isContainerExistingInDOM = this._$a11yStatusContainer?.parent().is(this.$element());

if (isContainerExistingInDOM) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if(!!this._$a11yStatusContainer.get(0))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -85,6 +85,7 @@ const LIST_SELECT_CHECKBOX = 'dx-list-select-checkbox';
const LIST_SELECT_RADIOBUTTON = 'dx-list-select-radiobutton';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check previous solution.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot AI review requested due to automatic review settings September 4, 2026 13:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The new live-region announcement label is inconsistent with existing ARIA labeling behavior for deletable lists (allowItemDeleting), which should be aligned before merging.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

packages/devextreme/js/__internal/ui/list/list.base.ts:1030

  • _updateA11yStatusText always uses dxList-listAriaLabel for the announcement label, but _setListAria() switches to dxList-listAriaLabel-deletable when allowItemDeleting is enabled. This makes the live-region announcement inconsistent with the list’s own ARIA label in deletable mode (and may produce partially untranslated output in that configuration). Consider selecting the same label key here based on allowItemDeleting.
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

Copilot AI review requested due to automatic review settings September 4, 2026 13:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The live-region element can be detached during widget refresh/clean but not recreated due to a stale cached reference, which can break announcements after rerender.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment on lines +1009 to +1018
_renderA11yStatusContainer(): void {
if (this._$a11yStatusContainer?.get(0)) {
return;
}

this._$a11yStatusContainer = $('<div>')
.addClass(SCREEN_READER_ONLY_CLASS)
.attr('role', 'status')
.appendTo(this.$element());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants