Skip to content

[eslint-issue-reporter] Fix @elastic/eui/require-table-caption lint violations across @elastic/appex-sharedux files #258288

@alexwizp

Description

@alexwizp

Note

Reserved for AI coding agents—please don’t take it manually.

Summary

This issue groups 1 ESLint violation for rule @elastic/eui/require-table-caption across 1 file for team @elastic/appex-sharedux.

Why this issue exists

This is a grouped ESLint backlog item generated from the latest monitoring snapshot. One issue per (team, rule) keeps the backlog stable and actionable.

What to fix

Implement fixes in full alignment with Constitution and Skill below. Expand those blocks and treat every paragraph as normative—scope, change boundaries, WCAG, i18n, and rule-specific guidance override any approach that would clear ESLint but contradict them.

Required: before opening a PR, verify that every item in the Acceptance criteria section is met.

Rule: @elastic/eui/require-table-caption

EuiInMemoryTable must include a `tableCaption` prop for accessibility.

Example:
 <EuiInMemoryTable tableCaption="Descriptive caption for the table" ... /> 

Places to fix

Requirement: Fix every location in the checklist below—do not merge or close this issue until all listed places are addressed (the list may be capped by the reporter; still treat each listed item as mandatory).

  • examples/files_example/public/components/app.tsx

PR metadata (required)

  • Target branch: main
  • Label: a11y:agent-pr
  • Close: reference this issue in the PR body (Closes #<issue number>) so it auto-closes when merged.

Acceptance criteria

The opened PR must satisfy every item below; it is not considered valid otherwise.

  • All items in PR metadata (required) above are satisfied.
  • PR has label a11y:agent-pr.
  • PR body contains Closes #<issue number> referencing this issue.
  • PR is opened against main.
  • All checklist locations above are fixed (or skips are documented per Skill).

PR checklist (copy into PR description)

  • Added label a11y:agent-pr
  • PR body includes Closes #<issue number>
  • Fixed all checklist locations (or documented skips per Skill)
Constitution

Constitution for Skills

Scope and precedence

  • This constitution applies to every skill and every skill-driven fix in this repo.
  • When a SKILL adds narrower rules, follow both documents.
  • If there is a conflict, use this precedence order:
    1. task-specific user or system instruction
    2. this constitution
    3. the individual SKILL
  • Skill-specific guidance may narrow the allowed change set, but it must not weaken this constitution.

Core principles

Standards compliance

  • All changes must meet WCAG 2.2 AA at minimum.
  • Follow the WAI-ARIA Authoring Practices Guide (APG) for widget patterns (e.g. dialogs, tabs, menus, radio groups).
  • Prefer semantic HTML over ARIA where possible.
  • Use WAI-ARIA only when native semantics are insufficient.

Minimal changes

  • Apply the smallest change that fully resolves the accessibility issue.
  • Avoid unnecessary refactoring.
  • Preserve existing behavior, layout, and intent unless the accessibility fix requires a change.

Deterministic behavior

  • Apply deterministic fixes: the same code pattern should produce the same result.
  • Avoid subjective or stylistic changes unless they are directly tied to accessibility.

Type safety

  • Preserve existing TypeScript types and interfaces.
  • Do not widen types (e.g. stringany) or suppress type errors (@ts-ignore, as any) to make a fix compile.
  • When a fix adds a new prop, ensure its type is compatible with the component's type definition.

Accessibility rules

  • Prefer existing visible text for accessible names whenever possible.
  • Reuse nearby labels, headings, button text, or other visible copy before introducing hidden text such as aria-label.
  • Do not remove existing accessibility attributes such as title, alt, aria-label, or aria-labelledby unless they are being replaced with a stronger or more correct alternative.
  • Prefer relationships to visible content, such as aria-labelledby, when that produces a clearer and more maintainable result.
  • When multiple rules apply to the same element, resolve all of them in a single pass where possible; never let one fix introduce a new violation of another rule.

i18n rules

  • Any new or replacement user-facing string, or any new or replacement string exposed to assistive technology as an accessible name, must use i18n.translate('message.id', { defaultMessage: 'English fallback' }).
  • Do not introduce that copy as a raw string literal.
  • If the file already uses a shared i18n object such as i18nTexts.modalTitle, keep that pattern for new strings in that file.
  • Do not localize programmatic identifiers that are not user-visible, such as radio group name values.
  • Add an i18n import only when you introduce a new i18n.translate(...) call.
  • Place new imports at the top of the file and merge them with existing imports from the same package.
  • Reuse existing translation IDs when the same message already exists in the file.
  • Follow the local translation ID naming convention when one exists.
  • If no local naming convention exists, use <fileOrComponent>.<attributeName>, for example myTable.tableCaption or myButton.ariaLabel.
  • Keep defaultMessage short, user-facing, and consistent with the surrounding UI.
  • Do not create duplicate translation IDs with different defaultMessage values in the same file.

Example

<EuiButtonIcon
  aria-label={i18n.translate('myButton.ariaLabel', {
    defaultMessage: 'Refresh results',
  })}
/>

Generating HTML IDs

When a fix needs a new id (for aria-labelledby, titleProps, label references, etc.), use the EUI ID utilities.

Function components

Import useGeneratedHtmlId from @elastic/eui. Call it once before the first return and store the result in a descriptive variable.

import { useGeneratedHtmlId } from '@elastic/eui';

const labelId = useGeneratedHtmlId();

Class components

Import htmlIdGenerator from @elastic/eui. Call it inside render() with a stable suffix.

import { htmlIdGenerator } from '@elastic/eui';

render() {
  const labelId = htmlIdGenerator()('myLabel');
}

Naming

  • Use descriptive variable names that reflect the element being identified (e.g. modalTitleId, fieldLabelId, popoverTitleId).
  • Ensure variable names are unique within the component scope.
  • Reuse an existing valid ID variable when one already targets the same element.

Change boundaries

  • Do not change unrelated components, logic, behavior, or layout.
  • Do not broaden the scope of a fix beyond the rule, component, or pattern described by the skill.
  • Do not remove or modify license headers.
  • Do not add comments to updated lines.
  • Do not delete existing comments unless the skill explicitly requires it.
  • Do not break existing tests. If a fix causes a test assertion to fail because the DOM changed, update only the affected assertion — never delete or skip the test.

Output constraints

  • Return only the requested code changes.
  • Do not include explanations in the output.
  • Keep the final result concise and implementation-focused.
Skill

Skill fix-no-table-captions

Description

This skill fixes violations of the @elastic/eui/require-table-caption rule.
Ensure EuiInMemoryTable and EuiBasicTable components have a meaningful tableCaption property for accessibility.

Task

Refactor the provided React TypeScript file so EuiInMemoryTable and EuiBasicTable use an accessible tableCaption.

Scope

Only modify:

  • EuiInMemoryTable
  • EuiBasicTable

Decision rules

  1. Each target has exactly one tableCaption prop.
  2. Keep existing meaningful captions unchanged.
  3. Replace captions that are missing, empty, or not meaningful.
  4. Prefer nearby visible text when it can serve as the caption (Constitution Accessibility rules).
  5. Otherwise set tableCaption with i18n.translate (Constitution i18n rules).
  6. Caption text should describe the table's purpose or content, not repeat the page title verbatim.

Skip / defer

  • If the table component uses a spread prop that already provides tableCaption, do not add another. Instead add an eslint-disable comment (see example below).
  • If the spread source is not inspectable in the current file, flag for manual review rather than guessing.

Examples

EuiBasicTable

<EuiBasicTable
  tableCaption={i18n.translate('usersList.tableCaption', {
    defaultMessage: 'User accounts list',
  })}
  items={items}
  columns={columns}
/>

EuiInMemoryTable

<EuiInMemoryTable
  tableCaption={i18n.translate('discover.gridCaption', {
    defaultMessage: 'Documents in this view',
  })}
  items={items}
  columns={columns}
/>

Spread prop already contains caption

{/* eslint-disable-next-line @elastic/eui/require-table-caption */}
<EuiBasicTable {...tableProps} items={items} columns={columns} />

Metadata

Metadata

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions