Skip to content

Commit fa06968

Browse files
committed
Merge branch 'main' into feat/sidenav-customization-001
2 parents c3e0d4b + aa589ec commit fa06968

55 files changed

Lines changed: 3063 additions & 56 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/accessibility/references/components/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ Open the guide that matches the component(s) you are writing or refactoring. Rea
1212
| `interactive_components.md` | Names for interactive controls | `EuiBetaBadge`, `EuiButtonIcon`, `EuiComboBox`, `EuiSelect`, `EuiSuperSelect`, `EuiPagination`, `EuiTreeView`, `EuiBreadcrumbs` |
1313
| `overlays.md` | Modals, flyouts, popovers | `EuiModal`, `EuiFlyout`, `EuiFlyoutResizable`, `EuiConfirmModal`, `EuiPopover` |
1414
| `radio_groups.md` | Radio groups (`name` grouping) | `EuiRadio`, `EuiRadioGroup` |
15-
| `tooltip_icon.md` | Tooltip on icon button (no duplicate SR text) | `EuiToolTip`, `EuiButtonIcon` |
15+
| `tooltip_content.md` | No interactive elements in tooltip `content` / `title` | `EuiToolTip`, `EuiIconTip` |
16+
| `tooltip_icon.md` | Tooltip on icon button (wrap + no duplicate SR text, no native `title`) | `EuiToolTip`, `EuiButtonIcon` |
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# EUI tooltip content: no interactive elements
2+
3+
**Applies to:** `EuiToolTip`, `EuiIconTip` — the `content` and `title` props
4+
5+
Tooltip `content` and `title` render inside a portal with `role="tooltip"`. The overlay only appears while the trigger is hovered or focused and is dismissed on blur, so any focusable element placed inside it is unreachable by keyboard and assistive-technology users. Use **`EuiPopover`** when the content needs to be interactive.
6+
7+
**Related guides:** **`overlays.md`** (`EuiPopover` for interactive content) · **`tooltip_icon.md`** (wrapping `EuiButtonIcon` with `EuiToolTip`) · **`icons_and_tooltips.md`** (`EuiIconTip` vs `EuiToolTip` + `EuiIcon`).
8+
9+
## Canonical usage
10+
11+
- `EuiToolTip` / `EuiIconTip` `content` and `title` may contain:
12+
- **Plain strings** (preferred — easy to localize and read).
13+
- **Non-interactive JSX** — text nodes, `<span>`, `<p>`, `EuiText`, `EuiIcon`, and the display-only badges/cards (`EuiBadge`, `EuiBetaBadge`, `EuiCard`) used **without** `onClick` / `href`.
14+
- They must **not** contain anything focusable:
15+
- Native `<a>`, `<button>`, `<input>`, `<select>`, `<textarea>`.
16+
- Interactive EUI components — `EuiLink`, `EuiButton`, `EuiButtonEmpty`, `EuiButtonIcon`, `EuiFieldText`, `EuiFieldNumber`, `EuiFieldSearch`, `EuiFieldPassword`, `EuiTextArea`, `EuiSelect`, `EuiSuperSelect`, `EuiComboBox`, `EuiSelectable`, `EuiSwitch`, `EuiCheckbox`, `EuiRadio`, `EuiRange`, `EuiDualRange`, `EuiColorPicker`, `EuiDatePicker`, `EuiSuperDatePicker`, `EuiFilterButton`, `EuiPagination`, `EuiTab`, `EuiTreeView`, `EuiContextMenuItem`, `EuiKeyPadMenuItem`, `EuiListGroupItem`, `EuiBreadcrumbs`, `EuiBasicTable`, `EuiInMemoryTable`, `EuiCheckableCard`, …
17+
- The rule searches recursively — interactive elements nested inside fragments, conditional renders (`cond && …`, `cond ? … : …`), or wrapper elements are reported too.
18+
- When users need to interact with the content (click a link, fill a field), switch the wrapper to **`EuiPopover`** triggered by an explicit click — never by hover.
19+
20+
### Manual-review cases (rule is silent)
21+
22+
- **Variable content**`content={tooltipContent}` / `title={titleNode}` is intentionally skipped because it cannot be statically analyzed. Trace the variable and verify it never holds focusable JSX.
23+
- **Conditionally-interactive components**`EuiBadge`, `EuiBetaBadge`, `EuiCard` are excluded from the rule because they render as a plain element without `onClick` / `href`. As soon as you add `onClick` or `href`, they become focusable and the same restriction applies — move the interaction out of the tooltip.
24+
25+
## Examples
26+
27+
```tsx
28+
<EuiToolTip content="Just text">
29+
<EuiButton>Hover me</EuiButton>
30+
</EuiToolTip>
31+
32+
<EuiToolTip content={<EuiText><p>Description</p></EuiText>}>
33+
<EuiButton>Hover me</EuiButton>
34+
</EuiToolTip>
35+
36+
<EuiIconTip content="Informational text" type="info" />
37+
38+
// Display-only badge is fine
39+
<EuiToolTip content={<EuiBadge>v2.0</EuiBadge>}>
40+
<EuiButton>Hover me</EuiButton>
41+
</EuiToolTip>
42+
```
43+
44+
## Common mistakes
45+
46+
```tsx
47+
// WRONG — link inside tooltip is not keyboard-reachable
48+
<EuiToolTip content={<EuiLink href="/docs">Learn more</EuiLink>}>
49+
<EuiButton>Hover me</EuiButton>
50+
</EuiToolTip>
51+
52+
// RIGHT — switch to EuiPopover so the link participates in the focus order
53+
const [isOpen, setIsOpen] = useState(false);
54+
const togglePopover = () => setIsOpen((open) => !open);
55+
const closePopover = () => setIsOpen(false);
56+
57+
<EuiPopover
58+
button={<EuiButton onClick={togglePopover}>More info</EuiButton>}
59+
isOpen={isOpen}
60+
closePopover={closePopover}
61+
>
62+
<EuiLink href="/docs">Learn more</EuiLink>
63+
</EuiPopover>
64+
65+
// WRONG — button inside `EuiIconTip` content
66+
<EuiIconTip content={<EuiButton>Click</EuiButton>} type="info" />
67+
68+
// RIGHT — keep the icon tip purely informational
69+
<EuiIconTip content="Informational text" type="info" />
70+
71+
// WRONG — interactive element inside `title` is also reported
72+
<EuiToolTip title={<EuiLink href="#">Learn more</EuiLink>} content="Info">
73+
<EuiButton>Hover</EuiButton>
74+
</EuiToolTip>
75+
76+
// WRONG — interactive child wrapped in a fragment is reported recursively
77+
<EuiToolTip content={<><span>Text</span><EuiLink href="#">Link</EuiLink></>}>
78+
<EuiButton>Hover</EuiButton>
79+
</EuiToolTip>
80+
81+
// WRONG — interactive child behind `cond && …` is reported
82+
<EuiToolTip content={<span>{cond && <EuiLink href="#">Link</EuiLink>}</span>}>
83+
<EuiButton>Hover</EuiButton>
84+
</EuiToolTip>
85+
86+
// WRONG — interactive child inside a ternary is reported
87+
<EuiToolTip content={cond ? <EuiLink href="#">Link</EuiLink> : null}>
88+
<EuiButton>Hover</EuiButton>
89+
</EuiToolTip>
90+
91+
// WRONG — conditionally-interactive badge with onClick becomes focusable
92+
<EuiToolTip content={<EuiBadge onClick={onClick}>Open</EuiBadge>}>
93+
<EuiButton>Hover</EuiButton>
94+
</EuiToolTip>
95+
```

.agents/skills/accessibility/references/components/tooltip_icon.md

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,58 @@
22

33
**Applies to:** `EuiToolTip`, `EuiButtonIcon`
44

5-
When **`EuiToolTip`** wraps **`EuiButtonIcon`** and the tooltip **`content`** matches the button's **`aria-label`**, assistive technology can announce the same text twice. Use **`disableScreenReaderOutput`** so the tooltip stays available to sighted users while screen readers hear the name once.
5+
Every **`EuiButtonIcon`** needs two things:
66

7-
**Related guides:** **`focus_and_keyboard.md`** (tooltip anchors / `tabIndex`) · **`icons_and_tooltips.md`** (`EuiIconTip` vs `EuiToolTip` + `EuiIcon`).
7+
1. **A visible tooltip for sighted users** — wrap the button with **`EuiToolTip`**. Do **not** use the native **`title`** prop on `EuiButtonIcon`; browser tooltips are unstyled, have no delay control, and are not reliably announced by screen readers across browser / AT combinations.
8+
2. **An accessible name for assistive technology** — keep **`aria-label`** on the button.
89

9-
## Canonical usage
10+
When the tooltip **`content`** and the button's **`aria-label`** match (same string, same variable, or same `i18n` call), also set **`disableScreenReaderOutput`** on `EuiToolTip` so screen readers announce the name once instead of twice.
1011

11-
- **`content`** equals **`aria-label`** (same string or same variable / same `i18n` call) → set **`disableScreenReaderOutput`** on **`EuiToolTip`**.
12-
- **`content`** differs from **`aria-label`** → no extra prop; both will be announced as intended.
13-
- Child is not **`EuiButtonIcon`** → this pattern doesn't apply; check the related guides above.
12+
**Related guides:** **`focus_and_keyboard.md`** (tooltip anchors / `tabIndex`) · **`icons_and_tooltips.md`** (`EuiIconTip` vs `EuiToolTip` + `EuiIcon`) · **`tooltip_content.md`** (no interactive elements inside tooltip `content` / `title`).
1413

15-
Prefer a single **`i18n.translate`** call (same id + `defaultMessage`) for both **`content`** and **`aria-label`** so the strings can't drift apart.
14+
## Canonical usage
1615

17-
For `{...tooltipProps}` spreads, merge **`disableScreenReaderOutput`** at the callsite or in the spread source.
16+
1. Wrap every `EuiButtonIcon` with `EuiToolTip`. Remove any `title` prop from the button.
17+
2. Pass the same localized string to `EuiToolTip` `content` and the button's `aria-label`. Prefer a single **`i18n.translate`** call (same id + `defaultMessage`) referenced from both places so the strings cannot drift apart.
18+
3. When `content` and `aria-label` match → add **`disableScreenReaderOutput`** on `EuiToolTip`.
19+
4. When `content` and `aria-label` intentionally differ (the tooltip elaborates beyond the name) → do **not** add `disableScreenReaderOutput`; both will be announced as intended.
1820

1921
## Examples
2022

2123
```tsx
22-
<EuiToolTip
23-
content={i18n.translate('filter.add', { defaultMessage: 'Add filter' })}
24-
disableScreenReaderOutput
25-
>
24+
const editLabel = i18n.translate('myFeature.editItem', {
25+
defaultMessage: 'Edit item',
26+
});
27+
28+
<EuiToolTip content={editLabel} disableScreenReaderOutput>
2629
<EuiButtonIcon
27-
iconType="plusInCircle"
28-
aria-label={i18n.translate('filter.add', { defaultMessage: 'Add filter' })}
29-
onClick={onAdd}
30+
iconType="pencil"
31+
aria-label={editLabel}
32+
onClick={onEdit}
3033
/>
3134
</EuiToolTip>
3235
```
3336

3437
## Common mistakes
3538

3639
```tsx
37-
// WRONG — screen reader announces "Add filter" twice
38-
<EuiToolTip content={label}>
39-
<EuiButtonIcon iconType="plusInCircle" aria-label={label} onClick={onAdd} />
40+
// WRONG — no visible tooltip for sighted users
41+
<EuiButtonIcon iconType="trash" aria-label="Delete" onClick={onDelete} />
42+
43+
// WRONG — native `title` is not reliably announced by screen readers and has no consistent visual styling
44+
<EuiButtonIcon title="Delete" aria-label="Delete" iconType="trash" onClick={onDelete} />
45+
46+
// WRONG — tooltip and `aria-label` without `disableScreenReaderOutput` will lead to SR announcement duplication
47+
<EuiToolTip content="Delete">
48+
<EuiButtonIcon iconType="trash" aria-label="Delete" onClick={onDelete} />
4049
</EuiToolTip>
4150

42-
// RIGHT
43-
<EuiToolTip content={label} disableScreenReaderOutput>
44-
<EuiButtonIcon iconType="plusInCircle" aria-label={label} onClick={onAdd} />
51+
// RIGHT — wrap, keep `aria-label`, and add `disableScreenReaderOutput` when `content` matches
52+
<EuiToolTip content="Delete" disableScreenReaderOutput>
53+
<EuiButtonIcon iconType="trash" aria-label="Delete" onClick={onDelete} />
4554
</EuiToolTip>
4655

47-
// WRONG — different ids, strings may drift apart
56+
// WRONG — different i18n ids may drift apart
4857
content={i18n.translate('a.tooltip', { defaultMessage: 'Add' })}
4958
aria-label={i18n.translate('a.button', { defaultMessage: 'Add' })}
5059

.agents/skills/accessibility/references/eslint.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ Secondary path: when starting from a rule id, jump to the canonical **component
1515
| `@elastic/eui/require-aria-label-for-modals` | [`components/overlays.md`](components/overlays.md) | `{...props}` hides wiring; no visible title without UX change — escalate. |
1616
| `@elastic/eui/require-table-caption` | [`components/data_tables.md`](components/data_tables.md) | `tableCaption` only via `{...tableProps}` — fix at source; no duplicate conflicting captions. |
1717
| `@elastic/eui/sr-output-disabled-tooltip` | [`components/tooltip_icon.md`](components/tooltip_icon.md) | `EuiToolTip` props from spread; child not `EuiButtonIcon`. |
18+
| `@elastic/eui/tooltip-button-icon-wrap` | [`components/tooltip_icon.md`](components/tooltip_icon.md) | `{...props}` without an explicit `title` is silently skipped — verify the spread does not omit a tooltip. No `aria-label` on the button → autofix can't run; supply both `aria-label` and `EuiToolTip` `content`. |
1819
| `@elastic/eui/tooltip-focusable-anchor` | [`components/focus_and_keyboard.md`](components/focus_and_keyboard.md) | `{...anchorProps}` or unknown custom anchor. |
20+
| `@elastic/eui/tooltip-no-interactive-content` | [`components/tooltip_content.md`](components/tooltip_content.md) | Variable content (`content={var}` / `title={var}`) is silently skipped — trace and verify. `EuiBadge` / `EuiBetaBadge` / `EuiCard` with `onClick` / `href` become focusable and need migration to `EuiPopover`. |

.buildkite/pipelines/evals/evals.suites.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,25 @@
232232
"configPath": "x-pack/solutions/security/packages/kbn-evals-suite-security-esql-generation-regression/playwright.config.ts",
233233
"tags": ["security", "esql-generation"],
234234
"ciLabels": ["evals:security-esql-generation-regression"]
235+
},
236+
{
237+
"id": "agent-builder-dashboards",
238+
"name": "Agent Builder Dashboards",
239+
"slackChannel": "#kibana-presentation-reminders",
240+
"configPath": "x-pack/platform/packages/shared/agent-builder-dashboards/kbn-evals-suite-agent-builder-dashboards/playwright.config.ts",
241+
"tags": [
242+
"platform",
243+
"agent-builder-dashboards"
244+
],
245+
"ciLabels": [
246+
"evals:agent-builder-dashboards"
247+
],
248+
"weeklyEisModelGroups": [
249+
"eis/anthropic-claude-4.6-sonnet",
250+
"eis/anthropic-claude-4.6-opus",
251+
"eis/openai-gpt-5.2",
252+
"eis/openai-gpt-5.4"
253+
]
235254
}
236255
]
237256
}

.buildkite/pipelines/evals/llm_evals.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,28 @@ steps:
8686
- exit_status: '-1'
8787
limit: 3
8888

89+
- label: 'Evals: Agent Builder Dashboards'
90+
key: kbn-evals-weekly-agent-builder-dashboards
91+
command: bash .buildkite/scripts/steps/evals/run_suite.sh
92+
env:
93+
KBN_EVALS: '1'
94+
FTR_EIS_CCM: '1'
95+
EVAL_SUITE_ID: 'agent-builder-dashboards'
96+
EVAL_FANOUT: '1'
97+
EVAL_INCLUDE_EIS_MODELS: '1'
98+
EVAL_MODEL_GROUPS: *weekly_eis_core_models
99+
timeout_in_minutes: 60
100+
agents:
101+
image: family/kibana-ubuntu-2404
102+
imageProject: elastic-images-prod
103+
provider: gcp
104+
machineType: n2-standard-8
105+
preemptible: true
106+
retry:
107+
automatic:
108+
- exit_status: '-1'
109+
limit: 3
110+
89111
- label: 'Evals: ES|QL Generation Evaluations'
90112
key: kbn-evals-weekly-esql-generation
91113
command: bash .buildkite/scripts/steps/evals/run_suite.sh

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,7 @@ x-pack/platform/packages/private/upgrade-assistant/common @elastic/kibana-manage
995995
x-pack/platform/packages/private/upgrade-assistant/public @elastic/kibana-management
996996
x-pack/platform/packages/private/upgrade-assistant/server @elastic/kibana-management
997997
x-pack/platform/packages/shared/agent-builder-dashboards/agent-builder-dashboards-common @elastic/appex-ai-infra
998+
x-pack/platform/packages/shared/agent-builder-dashboards/kbn-evals-suite-agent-builder-dashboards @elastic/appex-ai-infra
998999
x-pack/platform/packages/shared/agent-builder/agent-builder-browser @elastic/workchat-eng
9991000
x-pack/platform/packages/shared/agent-builder/agent-builder-common @elastic/workchat-eng
10001001
x-pack/platform/packages/shared/agent-builder/agent-builder-genai-utils @elastic/workchat-eng

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,7 @@
17441744
"@kbn/evals-extensions": "link:x-pack/platform/packages/shared/kbn-evals-extensions",
17451745
"@kbn/evals-phoenix-executor": "link:x-pack/platform/packages/shared/kbn-evals-phoenix-executor",
17461746
"@kbn/evals-suite-agent-builder": "link:x-pack/platform/packages/shared/agent-builder/kbn-evals-suite-agent-builder",
1747+
"@kbn/evals-suite-agent-builder-dashboards": "link:x-pack/platform/packages/shared/agent-builder-dashboards/kbn-evals-suite-agent-builder-dashboards",
17471748
"@kbn/evals-suite-alerts-rag": "link:x-pack/solutions/security/packages/kbn-evals-suite-alerts-rag",
17481749
"@kbn/evals-suite-attack-discovery": "link:x-pack/solutions/security/packages/kbn-evals-suite-attack-discovery",
17491750
"@kbn/evals-suite-endpoint": "link:x-pack/solutions/security/packages/kbn-evals-suite-endpoint",

src/platform/packages/shared/kbn-doc-links/src/get_doc_links.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
941941
agentPolicy: `${ELASTIC_DOCS}reference/fleet/agent-policy`,
942942
agentlessIntegrations: `${ELASTIC_DOCS}solutions/security/get-started/agentless-integrations`,
943943
api: `${ELASTIC_DOCS}reference/fleet/fleet-api-docs`,
944+
managedOtlp: `${ELASTIC_DOCS}reference/opentelemetry/motlp`,
944945
uninstallAgent: `${ELASTIC_DOCS}solutions/security/configure-elastic-defend/uninstall-elastic-agent`,
945946
installAndUninstallIntegrationAssets: `${ELASTIC_DOCS}reference/fleet/install-uninstall-integration-assets`,
946947
elasticAgentInputConfiguration: `${ELASTIC_DOCS}reference/fleet/elastic-agent-input-configuration`,

src/platform/packages/shared/kbn-doc-links/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,7 @@ export interface DocLinks {
578578
agentPolicy: string;
579579
agentlessIntegrations: string;
580580
api: string;
581+
managedOtlp: string;
581582
uninstallAgent: string;
582583
installAndUninstallIntegrationAssets: string;
583584
elasticAgentInputConfiguration: string;

0 commit comments

Comments
 (0)