From 8f221bfad00a1774eb3777a8e32e47d1cd57cc3a Mon Sep 17 00:00:00 2001
From: Alexey Antonov
Date: Mon, 4 Aug 2025 15:18:33 +0300
Subject: [PATCH] Fix violations of the
@elastic/eui/require-aria-label-for-modals ESLint rule (Step 3) (#229616)
## Summary
This PR manually fixes some remaining violations of the following rule
that, for some reason, were not addressed in the previous PR:
https://github.com/elastic/kibana/pull/227821
It applies the auto-fix for the newly introduced
`@elastic/eui/require-aria-label-for-modals` rule. This rule ensures
that `EUI` modal components (`EuiModal` and `EuiFlyout`) include either
an aria-label or `aria-labelledby` prop to support screen reader
accessibility. These attributes help users understand the purpose and
content of modal dialogs.
## Changes Made
1. **Identified the modal title/header element** used within `EuiModal`
or `EuiFlyout`.
Common elements include: `EuiFlyoutTitle`, `EuiModalTitle`, `EuiTitle`,
``, ``, or other heading tags.
2. **Ensured the title element has an `id`:**
If the header element lacked an `id`, one was programmatically
generated:
* **For function components:**
* Imported `useGeneratedHtmlId` from `@elastic/eui` if not already
present:
```ts
import { useGeneratedHtmlId } from '@elastic/eui';
```
* Defined a unique ID before the `return` statement:
```ts
const modalTitleId = useGeneratedHtmlId();
```
* Applied the ID to the title element:
```tsx
Title
```
* **For class components:**
* Imported `htmlIdGenerator` from `@elastic/eui` if not already present:
```ts
import { htmlIdGenerator } from '@elastic/eui';
```
* Defined a unique ID within the `render()` method:
```ts
const modalTitleId = htmlIdGenerator()('modalTitle');
```
* Applied the ID to the title element:
```tsx
Title
```
3. **Updated the `EuiModal` or `EuiFlyout` component** to include the
`aria-labelledby` attribute referencing the generated title ID:
```tsx
```
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
(cherry picked from commit ef05e61a64ff49080636578a37d6502492f9c947)
# Conflicts:
# src/platform/plugins/shared/workflows_management/public/features/run_workflow/ui/test_workflow_modal.tsx
# src/platform/plugins/shared/workflows_management/public/features/run_workflow/ui/workflow_event_modal.tsx
---
.../public/cluster_configuration_form.tsx | 10 +-
.../lib/external_url_error_modal.tsx | 71 +++++++-------
.../drilldowns_with_embeddable_example.tsx | 2 +-
.../drilldowns_without_embeddable_example.tsx | 2 +-
...thout_embeddable_single_button_example.tsx | 2 +-
.../datasource_preview/datasource_preview.js | 93 ++++++++++---------
.../auto_follow_pattern_request_flyout.js | 6 +-
.../follower_index_request_flyout.js | 6 +-
.../__snapshots__/detail_drawer.test.js.snap | 25 ++++-
.../pipeline_viewer/views/detail_drawer.js | 7 +-
...nowledge_base_edit_manual_entry_flyout.tsx | 7 +-
.../components/agent_policy_yaml_flyout.tsx | 9 +-
.../detail/settings/changelog_modal.tsx | 11 ++-
.../validate_job/validate_job_view.js | 33 ++++---
.../test_models/test_flyout.tsx | 60 +++++++-----
.../edit/import_modal/import_modal.js | 7 +-
.../components/forecasting_modal/modal.js | 12 ++-
.../import_content_pack_flyout.tsx | 7 +-
.../components/bulk_snooze_schedule_modal.tsx | 11 ++-
.../components/enablement_modal.tsx | 11 ++-
20 files changed, 246 insertions(+), 146 deletions(-)
diff --git a/src/platform/plugins/private/interactive_setup/public/cluster_configuration_form.tsx b/src/platform/plugins/private/interactive_setup/public/cluster_configuration_form.tsx
index bdeff4fb930b4..78112a6956a2f 100644
--- a/src/platform/plugins/private/interactive_setup/public/cluster_configuration_form.tsx
+++ b/src/platform/plugins/private/interactive_setup/public/cluster_configuration_form.tsx
@@ -32,6 +32,7 @@ import {
EuiSpacer,
EuiText,
EuiTitle,
+ useGeneratedHtmlId,
} from '@elastic/eui';
import type { FunctionComponent } from 'react';
import React, { useState } from 'react';
@@ -414,6 +415,7 @@ export interface CertificateChainProps {
}
const CertificateChain: FunctionComponent = ({ certificateChain }) => {
const [showModal, setShowModal] = useState(false);
+ const modalTitleId = useGeneratedHtmlId();
return (
<>
@@ -423,9 +425,13 @@ const CertificateChain: FunctionComponent = ({ certificat
compressed
/>
{showModal && (
- setShowModal(false)} maxWidth={euiThemeVars.euiBreakpoints.s}>
+ setShowModal(false)}
+ maxWidth={euiThemeVars.euiBreakpoints.s}
+ >
-
+
void;
}
-export const ExternalUrlErrorModal = ({ url, handleClose }: ExternalUrlErrorModalProps) => (
-
-
-
-
-
-
-
-
- {url}
-
- ),
- externalUrlPolicy: 'externalUrl.policy',
- kibanaConfigFileName: 'kibana.yml',
- }}
- />
-
-
-
+export const ExternalUrlErrorModal = ({ url, handleClose }: ExternalUrlErrorModalProps) => {
+ const externalUrlErrorModalTitleId = useGeneratedHtmlId();
+
+ return (
+
+
+
+
+
+
+
+ {url}
+
+ ),
+ externalUrlPolicy: 'externalUrl.policy',
+ kibanaConfigFileName: 'kibana.yml',
+ }}
/>
-
-
-
-);
+
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx b/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx
index f8b805fb8a21b..6a291b90f7781 100644
--- a/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/containers/drilldowns_with_embeddable_example/drilldowns_with_embeddable_example.tsx
@@ -107,7 +107,7 @@ export const DrilldownsWithEmbeddableExample: React.FC = () => {
{showManager && (
- setShowManager(false)} aria-labelledby="Drilldown Manager">
+ setShowManager(false)} aria-label="Drilldown Manager">
{
{showManager && (
- setShowManager(false)} aria-labelledby="Drilldown Manager">
+ setShowManager(false)} aria-label="Drilldown Manager">
{
{showManager && (
- setShowManager(false)} aria-labelledby="Drilldown Manager">
+ setShowManager(false)} aria-label="Drilldown Manager">
(
-
-
- {strings.getModalTitle()}
-
-
-
-
- {strings.getSaveButtonLabel()},
- }}
- />
-
-
-
- {datatable.type === 'error' ? (
-
- ) : (
-
- {datatable.rows.length > 0 ? (
-
- ) : (
- {strings.getEmptyTitle()}
}
- titleSize="s"
- body={
-
- {strings.getEmptyFirstLineDescription()}
-
- {strings.getEmptySecondLineDescription()}
-
- }
+export const DatasourcePreview = ({ done, datatable }) => {
+ const modalTitleId = useGeneratedHtmlId();
+
+ return (
+
+
+ {strings.getModalTitle()}
+
+
+
+
+ {strings.getSaveButtonLabel()},
+ }}
/>
- )}
-
- )}
-
-
-);
+
+
+
+ {datatable.type === 'error' ? (
+
+ ) : (
+
+ {datatable.rows.length > 0 ? (
+
+ ) : (
+ {strings.getEmptyTitle()}}
+ titleSize="s"
+ body={
+
+ {strings.getEmptyFirstLineDescription()}
+
+ {strings.getEmptySecondLineDescription()}
+
+ }
+ />
+ )}
+
+ )}
+
+
+ );
+};
DatasourcePreview.propTypes = {
datatable: PropTypes.object,
diff --git a/x-pack/platform/plugins/private/cross_cluster_replication/public/app/components/auto_follow_pattern_request_flyout.js b/x-pack/platform/plugins/private/cross_cluster_replication/public/app/components/auto_follow_pattern_request_flyout.js
index 115eb75c2d4f5..52d20c910549d 100644
--- a/x-pack/platform/plugins/private/cross_cluster_replication/public/app/components/auto_follow_pattern_request_flyout.js
+++ b/x-pack/platform/plugins/private/cross_cluster_replication/public/app/components/auto_follow_pattern_request_flyout.js
@@ -19,6 +19,7 @@ import {
EuiSpacer,
EuiText,
EuiTitle,
+ htmlIdGenerator,
} from '@elastic/eui';
import { serializeAutoFollowPattern } from '../../../common/services/auto_follow_pattern_serialization';
@@ -36,12 +37,13 @@ export class AutoFollowPatternRequestFlyout extends PureComponent {
const endpoint = `PUT /_ccr/auto_follow/${name ? name : ''}`;
const payload = JSON.stringify(serializeAutoFollowPattern(autoFollowPattern), null, 2);
const request = `${endpoint}\n${payload}`;
+ const modalTitleId = htmlIdGenerator()('autoFollowPatternRequestFlyoutTitle');
return (
-
+
-
+
{name ? (
'}/_ccr/follow`;
const payload = JSON.stringify(serializeFollowerIndex(followerIndex), null, 2);
const request = `${endpoint}\n${payload}`;
+ const flyoutTitleId = htmlIdGenerator()('flyoutTitle');
return (
-
+
-
+
@@ -17,7 +18,9 @@ exports[`DetailDrawer component If vertices shows basic info and no stats for if
-
+
if
@@ -53,6 +56,7 @@ exports[`DetailDrawer component If vertices shows basic info and no stats for if
exports[`DetailDrawer component Plugin vertices Plugin does not have explicit ID shows basic info and stats for plugin, suggesting that user set explicit ID 1`] = `
@@ -68,7 +72,9 @@ exports[`DetailDrawer component Plugin vertices Plugin does not have explicit ID
-
+
grok filter
@@ -300,6 +306,7 @@ exports[`DetailDrawer component Plugin vertices Plugin does not have explicit ID
exports[`DetailDrawer component Plugin vertices Plugin has explicit ID shows basic info and stats for plugin, including explicit ID 1`] = `
@@ -315,7 +322,9 @@ exports[`DetailDrawer component Plugin vertices Plugin has explicit ID shows bas
-
+
grok filter
@@ -541,6 +550,7 @@ exports[`DetailDrawer component Plugin vertices Plugin has explicit ID shows bas
exports[`DetailDrawer component Queue vertices shows basic info and no stats for queue 1`] = `
@@ -556,7 +566,9 @@ exports[`DetailDrawer component Queue vertices shows basic info and no stats for
-
+
queue
@@ -584,6 +596,7 @@ exports[`DetailDrawer component Queue vertices shows basic info and no stats for
exports[`DetailDrawer component shows vertex title 1`] = `
@@ -599,7 +612,9 @@ exports[`DetailDrawer component shows vertex title 1`] = `
-
+
diff --git a/x-pack/platform/plugins/private/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js b/x-pack/platform/plugins/private/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js
index e1048b93e2d77..c4d5d68fee617 100644
--- a/x-pack/platform/plugins/private/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js
+++ b/x-pack/platform/plugins/private/monitoring/public/components/logstash/pipeline_viewer/views/detail_drawer.js
@@ -24,6 +24,7 @@ import {
EuiTableRowCell,
EuiText,
EuiTitle,
+ useGeneratedHtmlId,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
@@ -301,14 +302,16 @@ function renderTitle(vertex) {
}
export function DetailDrawer({ vertex, onHide, timeseriesTooltipXValueFormatter }) {
+ const detailDrawerTitleId = useGeneratedHtmlId();
+
return (
-
+
{renderIcon(vertex)}
- {renderTitle(vertex)}
+ {renderTitle(vertex)}
diff --git a/x-pack/platform/plugins/private/observability_ai_assistant_management/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx b/x-pack/platform/plugins/private/observability_ai_assistant_management/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx
index a5f096788c75a..e697ca4fa7238 100644
--- a/x-pack/platform/plugins/private/observability_ai_assistant_management/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx
+++ b/x-pack/platform/plugins/private/observability_ai_assistant_management/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx
@@ -24,6 +24,7 @@ import {
EuiSpacer,
EuiText,
EuiTitle,
+ useGeneratedHtmlId,
} from '@elastic/eui';
import moment from 'moment';
import { KnowledgeBaseEntryRole } from '@kbn/observability-ai-assistant-plugin/public';
@@ -74,11 +75,13 @@ export function KnowledgeBaseEditManualEntryFlyout({
onClose();
};
+ const flyoutTitleId = useGeneratedHtmlId();
+
return (
-
+
-
+
{!entry
? i18n.translate(
'xpack.observabilityAiAssistantManagement.knowledgeBaseNewEntryFlyout.h2.newEntryLabel',
diff --git a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_yaml_flyout.tsx b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_yaml_flyout.tsx
index b9822329f2bf5..e088c9d576753 100644
--- a/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_yaml_flyout.tsx
+++ b/x-pack/platform/plugins/shared/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_yaml_flyout.tsx
@@ -22,6 +22,7 @@ import {
EuiButton,
EuiCallOut,
EuiSpacer,
+ useGeneratedHtmlId,
} from '@elastic/eui';
import { MAX_FLYOUT_WIDTH } from '../../../constants';
@@ -38,6 +39,8 @@ const FlyoutBody = styled(EuiFlyoutBody)`
export const AgentPolicyYamlFlyout = memo<{ policyId: string; onClose: () => void }>(
({ policyId, onClose }) => {
+ const flyoutTitleId = useGeneratedHtmlId();
+
const core = useStartServices();
const { isLoading: isLoadingYaml, data: yamlData, error } = useGetOneAgentPolicyFull(policyId);
const { data: agentPolicyData } = useGetOneAgentPolicy(policyId);
@@ -72,10 +75,10 @@ export const AgentPolicyYamlFlyout = memo<{ policyId: string; onClose: () => voi
`?apiVersion=${API_VERSIONS.public.v1}`;
return (
-
-
+
+
-
+
{agentPolicyData?.item ? (
= ({
onClose,
}) => {
const changelogText = formatChangelog(changelog);
+ const changelogModalTitleId = useGeneratedHtmlId();
return (
-
+
- {'Changelog'}
+ {'Changelog'}
(
);
-const Modal = ({ close, title, children }) => (
-
-
- {title}
-
+const Modal = ({ close, title, children }) => {
+ const modalTitleId = useGeneratedHtmlId();
+ return (
+
+
+ {title}
+
- {children}
+ {children}
-
-
-
-
-
-
-);
+
+
+
+
+
+
+ );
+};
Modal.propType = {
close: PropTypes.func.isRequired,
title: PropTypes.string,
diff --git a/x-pack/platform/plugins/shared/ml/public/application/model_management/test_models/test_flyout.tsx b/x-pack/platform/plugins/shared/ml/public/application/model_management/test_models/test_flyout.tsx
index 3b8d3cc7bdea9..f9c33676ab601 100644
--- a/x-pack/platform/plugins/shared/ml/public/application/model_management/test_models/test_flyout.tsx
+++ b/x-pack/platform/plugins/shared/ml/public/application/model_management/test_models/test_flyout.tsx
@@ -8,7 +8,14 @@
import type { FC } from 'react';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
-import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiSpacer, EuiTitle } from '@elastic/eui';
+import {
+ EuiFlyout,
+ EuiFlyoutBody,
+ EuiFlyoutHeader,
+ EuiSpacer,
+ EuiTitle,
+ useGeneratedHtmlId,
+} from '@elastic/eui';
import type { TrainedModelItem } from '../../../../common/types/trained_models';
import { TestTrainedModelContent } from './test_trained_model_content';
@@ -16,24 +23,33 @@ interface Props {
model: TrainedModelItem;
onClose: () => void;
}
-export const TestTrainedModelFlyout: FC = ({ model, onClose }) => (
-
-
-
-
-
-
-
-
-
- {model.model_id}
-
-
-
-
-
-
-);
+export const TestTrainedModelFlyout: FC = ({ model, onClose }) => {
+ const flyoutTitleId = useGeneratedHtmlId();
+
+ return (
+
+
+
+
+
+
+
+
+
+ {model.model_id}
+
+
+
+
+
+
+ );
+};
diff --git a/x-pack/platform/plugins/shared/ml/public/application/settings/calendars/edit/import_modal/import_modal.js b/x-pack/platform/plugins/shared/ml/public/application/settings/calendars/edit/import_modal/import_modal.js
index 2abec566b43d1..40133009655d0 100644
--- a/x-pack/platform/plugins/shared/ml/public/application/settings/calendars/edit/import_modal/import_modal.js
+++ b/x-pack/platform/plugins/shared/ml/public/application/settings/calendars/edit/import_modal/import_modal.js
@@ -19,6 +19,7 @@ import {
EuiModalFooter,
EuiFlexGroup,
EuiFlexItem,
+ htmlIdGenerator,
} from '@elastic/eui';
import { ImportedEvents } from '../imported_events';
@@ -127,6 +128,8 @@ export class ImportModal extends Component {
includePastEvents,
} = this.state;
+ const modalTitleId = htmlIdGenerator()('importModalTitle');
+
let showRecurringWarning = false;
let importedEvents;
@@ -142,11 +145,11 @@ export class ImportModal extends Component {
return (
-
+
-
+
+
-
+
(null);
const [contentPackObjects, setContentPackObjects] = useState([]);
@@ -50,10 +53,10 @@ export function ImportContentPackFlyout({
const [manifest, setManifest] = useState();
return (
-
+
-
+
{i18n.translate('xpack.streams.streamDetailDashboard.importContent', {
defaultMessage: 'Import content pack',
})}
diff --git a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx
index 4b1447e9bcbe6..bf31f66e2956a 100644
--- a/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx
+++ b/x-pack/platform/plugins/shared/triggers_actions_ui/public/application/sections/rules_list/components/bulk_snooze_schedule_modal.tsx
@@ -125,13 +125,14 @@ export const BulkSnoozeScheduleModal = (props: BulkSnoozeScheduleModalProps) =>
return deleteConfirmPlural(numberOfSelectedRules);
}, [rules, filter, numberOfSelectedRules]);
- const modalTitleId = useGeneratedHtmlId();
+ const confirmModalTitleId = useGeneratedHtmlId();
+ const modalHeaderTitleId = useGeneratedHtmlId();
if (bulkEditAction === 'unschedule' && (rules.length || filter)) {
return (
if (bulkEditAction === 'schedule' && (rules.length || filter)) {
return (
-
+
-
+