From 3b5428d96606f731e7d059c54d99ff061aceeafe Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Mon, 11 May 2026 14:14:12 -0400 Subject: [PATCH 1/8] [Alerting v2] Add HorizontalMinimalStepper component MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A compact stepped-progress indicator for use in flyout headers, designed to match the rule authoring UX spec (see design screenshots in PR). The existing EuiStepsHorizontal renders numbered circles with labels under each step — too tall and visually heavy for the constrained flyout header context. This component takes a single row with: ● ● — ○ Current step title 2 / 4 [icon] Indicators: - Past steps: small filled circle (8 × 8px), EUI primary blue - Current step: wider pill (24 × 8px), EUI primary blue - Future steps: small filled circle (8 × 8px), EUI lightShade grey Animation: CSS-only spring transition (cubic-bezier(0.34, 1.56, 0.64, 1)) on width + border-radius as step changes. React keeps the same keyed
across renders so style updates trigger the transition automatically. Respects prefers-reduced-motion. API mirrors EuiStepsHorizontal (status-driven, not index-driven): steps: Array<{ title: string; status: 'current' | 'complete' | 'incomplete' }> animated?: boolean (default true) Layout note: placing controls (e.g. a Form/YAML icon toggle) alongside the stepper is intentionally the caller's responsibility via EuiFlexGroup — the component has no rightSlot API to keep it composable. Storybook: Alerting V2 / Compose Discover / HorizontalMinimalStepper Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../horizontal_minimal_stepper.stories.tsx | 216 ++++++++++++++++++ .../horizontal_minimal_stepper.tsx | 115 ++++++++++ 2 files changed, 331 insertions(+) create mode 100644 x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx create mode 100644 x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx new file mode 100644 index 0000000000000..cfa7bf1008554 --- /dev/null +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx @@ -0,0 +1,216 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { EuiButton, EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; +import { HorizontalMinimalStepper, type MinimalStep } from '../horizontal_minimal_stepper'; + +const meta: Meta = { + title: 'Alerting V2/Compose Discover/HorizontalMinimalStepper', + component: HorizontalMinimalStepper, + parameters: { + layout: 'padded', + }, +}; + +export default meta; +type Story = StoryObj; + +// --------------------------------------------------------------------------- +// Helper to build step arrays from a current index +// --------------------------------------------------------------------------- +const makeSteps = (titles: string[], currentIndex: number): MinimalStep[] => + titles.map((title, i) => ({ + title, + status: i < currentIndex ? 'complete' : i === currentIndex ? 'current' : 'incomplete', + })); + +const RULE_STEPS = ['Alert Condition', 'Recovery Condition', 'Details & Artifacts', 'Notifications']; +const RULE_STEPS_SHORT = ['Alert Condition', 'Details & Artifacts', 'Notifications']; + +// --------------------------------------------------------------------------- +// Interactive story — click through steps to see the animation +// --------------------------------------------------------------------------- +export const Interactive: Story = { + render: () => { + const [currentStep, setCurrentStep] = useState(0); + const steps = makeSteps(RULE_STEPS, currentStep); + + return ( +
+ + + + + setCurrentStep((s) => Math.max(0, s - 1))} + > + ← Back + + + + setCurrentStep((s) => Math.min(RULE_STEPS.length - 1, s + 1))} + > + Next → + + + + + + Click Next/Back to see the dot→pill animation on the indicators. + +
+ ); + }, +}; + +// --------------------------------------------------------------------------- +// All four states shown at once +// --------------------------------------------------------------------------- +export const AllStates: Story = { + render: () => ( +
+ {RULE_STEPS.map((_, i) => ( +
+ +
+ ))} +
+ ), +}; + +// --------------------------------------------------------------------------- +// With a right-side icon slot (Form/YAML toggle alongside the stepper) +// --------------------------------------------------------------------------- +const TOGGLE_OPTIONS = [ + { id: 'form', label: 'Form', iconType: 'tableDensityNormal' }, + { id: 'yaml', label: 'YAML', iconType: 'editorCodeBlock' }, +]; + +export const WithIconToggle: Story = { + render: () => { + const [currentStep, setCurrentStep] = useState(0); + const [mode, setMode] = useState<'form' | 'yaml'>('form'); + const steps = makeSteps(RULE_STEPS, currentStep); + + return ( +
+ + + + + + setMode(id as 'form' | 'yaml')} + buttonSize="compressed" + isIconOnly + /> + + + + + + setCurrentStep((s) => s - 1)}> + ← Back + + + + setCurrentStep((s) => s + 1)}> + Next → + + + + + + The Form/YAML toggle lives outside the stepper in a standard EuiFlexGroup — the stepper + itself has no slot API. + +
+ ); + }, +}; + +// --------------------------------------------------------------------------- +// Three-step variant (no Recovery Condition — tracking disabled) +// --------------------------------------------------------------------------- +export const ThreeSteps: Story = { + render: () => { + const [currentStep, setCurrentStep] = useState(0); + const steps = makeSteps(RULE_STEPS_SHORT, currentStep); + + return ( +
+ + + + + setCurrentStep((s) => s - 1)}> + ← Back + + + + setCurrentStep((s) => s + 1)}> + Next → + + + + + + Three-step variant shown when "Track active and recovered state" is disabled (no Recovery + Condition step). + +
+ ); + }, +}; + +// --------------------------------------------------------------------------- +// Animation disabled (respects prefers-reduced-motion equivalently) +// --------------------------------------------------------------------------- +export const AnimationDisabled: Story = { + render: () => { + const [currentStep, setCurrentStep] = useState(0); + const steps = makeSteps(RULE_STEPS, currentStep); + + return ( +
+ + + + + setCurrentStep((s) => s - 1)}> + ← Back + + + + setCurrentStep((s) => s + 1)}> + Next → + + + + + + animated=false — instant transitions, equivalent to{' '} + prefers-reduced-motion: reduce (which the component also detects automatically). + +
+ ); + }, +}; diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx new file mode 100644 index 0000000000000..bf3c247b37c00 --- /dev/null +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useEuiTheme, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; + +/** Mirrors the status subset used by EuiStepsHorizontal. */ +export type MinimalStepStatus = 'current' | 'complete' | 'incomplete'; + +export interface MinimalStep { + title: string; + status: MinimalStepStatus; +} + +export interface HorizontalMinimalStepperProps { + /** Steps with their current status — same shape as EuiStepsHorizontal steps (subset). */ + steps: MinimalStep[]; + /** + * When true, indicators animate on step change using a spring curve. + * Defaults to true. Pass false to disable for testing or reduced-motion contexts. + */ + animated?: boolean; +} + +/** + * Minimal horizontal stepper for compact flyout headers. + * + * Renders a row of small indicators (dots + pill for current step), a bold + * current-step title, and a muted N / N counter. + * + * Layout is intentionally self-contained — place alongside other elements + * using standard EuiFlexGroup/EuiFlexItem outside this component: + * + * + * + * + * + * + * + * + * + */ +export const HorizontalMinimalStepper: React.FC = ({ + steps, + animated = true, +}) => { + const { euiTheme } = useEuiTheme(); + + const DOT_SIZE = 8; + const BAR_WIDTH = 24; + const BAR_HEIGHT = DOT_SIZE; + const GAP = 4; + + const activeColor = euiTheme.colors.primary; + const futureColor = euiTheme.colors.lightShade; + + const currentIndex = steps.findIndex((s) => s.status === 'current'); + const currentTitle = currentIndex >= 0 ? steps[currentIndex].title : ''; + const total = steps.length; + + const transition = animated + ? 'width 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + + 'border-radius 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + + 'background-color 150ms ease' + : undefined; + + return ( + + {/* Step indicators */} + +
+ {steps.map((step, i) => { + const isCurrent = step.status === 'current'; + const isComplete = step.status === 'complete'; + + return ( +
+ ); + })} +
+ + + {/* Current step title */} + + + {currentTitle} + + + + {/* Spacer */} + + + {/* N / N counter */} + + + {currentIndex + 1} / {total} + + + + ); +}; From 0d7315e0b90d8ad23b730d8734a69acafc36dd9d Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Mon, 11 May 2026 14:49:49 -0400 Subject: [PATCH 2/8] Respect prefers-reduced-motion in HorizontalMinimalStepper --- .../horizontal_minimal_stepper.tsx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx index bf3c247b37c00..cdb42fd0f2ecc 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx @@ -62,11 +62,16 @@ export const HorizontalMinimalStepper: React.FC = const currentTitle = currentIndex >= 0 ? steps[currentIndex].title : ''; const total = steps.length; - const transition = animated - ? 'width 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + - 'border-radius 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + - 'background-color 150ms ease' - : undefined; + const prefersReducedMotion = + typeof window !== 'undefined' && + window.matchMedia?.('(prefers-reduced-motion: reduce)').matches; + + const transition = + animated && !prefersReducedMotion + ? 'width 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + + 'border-radius 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + + 'background-color 150ms ease' + : undefined; return ( From 75c6d9448c5b0bd0221a56c0337808cef2db525e Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Mon, 11 May 2026 15:04:37 -0400 Subject: [PATCH 3/8] Migrate HorizontalMinimalStepper to Emotion CSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces inline styles with Emotion css`` + euiCanAnimate from @elastic/eui, matching the pattern used by EuiAccordion, EuiProgress, EuiLoadingSpinner, and every other animated EUI component. - horizontal_minimal_stepper.styles.ts: indicator styles per status variant, spring transition wrapped in ${euiCanAnimate} so prefers-reduced-motion is handled automatically by the browser via CSS @media query — no JS check needed - horizontal_minimal_stepper.tsx: uses css prop, drops animated prop (the CSS media query is the only mechanism needed), uses useEuiTheme via styles hook - stories: AnimationDisabled → ReducedMotion with chromatic prefersReducedMotion parameter; removes animated={false} usage Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../horizontal_minimal_stepper.stories.tsx | 23 +++--- .../horizontal_minimal_stepper.styles.ts | 57 +++++++++++++ .../horizontal_minimal_stepper.tsx | 80 +++++++------------ 3 files changed, 98 insertions(+), 62 deletions(-) create mode 100644 x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx index cfa7bf1008554..c34ae4469eaaa 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx @@ -83,10 +83,7 @@ export const AllStates: Story = {
{RULE_STEPS.map((_, i) => (
- +
))}
@@ -182,16 +179,23 @@ export const ThreeSteps: Story = { }; // --------------------------------------------------------------------------- -// Animation disabled (respects prefers-reduced-motion equivalently) +// Reduced motion — simulate what users with prefers-reduced-motion see +// (The component handles this automatically via euiCanAnimate in its styles; +// this story documents the expected visual output for reviewers.) // --------------------------------------------------------------------------- -export const AnimationDisabled: Story = { +export const ReducedMotion: Story = { + parameters: { + backgrounds: { default: 'light' }, + // Chromatic / Storybook can simulate prefers-reduced-motion via this parameter + chromatic: { prefersReducedMotion: 'reduce' }, + }, render: () => { const [currentStep, setCurrentStep] = useState(0); const steps = makeSteps(RULE_STEPS, currentStep); return (
- + @@ -207,8 +211,9 @@ export const AnimationDisabled: Story = { - animated=false — instant transitions, equivalent to{' '} - prefers-reduced-motion: reduce (which the component also detects automatically). + Animation is handled via euiCanAnimate in the Emotion styles — the transition + is automatically absent when prefers-reduced-motion: reduce is set. No{' '} + animated prop needed.
); diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts new file mode 100644 index 0000000000000..fddf8e8791641 --- /dev/null +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { css } from '@emotion/react'; +import { euiCanAnimate } from '@elastic/eui'; +import type { UseEuiTheme } from '@elastic/eui'; +import type { MinimalStepStatus } from './horizontal_minimal_stepper'; + +const DOT_SIZE = 8; +const BAR_WIDTH = 24; +const SPRING = 'cubic-bezier(0.34, 1.56, 0.64, 1)'; + +export const useHorizontalMinimalStepperStyles = ({ euiTheme }: UseEuiTheme) => { + const baseIndicator = css` + height: ${DOT_SIZE}px; + flex-shrink: 0; + ${euiCanAnimate} { + transition: + width 220ms ${SPRING}, + border-radius 220ms ${SPRING}, + background-color 150ms ease; + } + `; + + const indicatorByStatus: Record> = { + current: css` + ${baseIndicator}; + width: ${BAR_WIDTH}px; + border-radius: ${DOT_SIZE / 2}px; + background-color: ${euiTheme.colors.primary}; + `, + complete: css` + ${baseIndicator}; + width: ${DOT_SIZE}px; + border-radius: 50%; + background-color: ${euiTheme.colors.primary}; + `, + incomplete: css` + ${baseIndicator}; + width: ${DOT_SIZE}px; + border-radius: 50%; + background-color: ${euiTheme.colors.lightShade}; + `, + }; + + const indicatorRow = css` + display: flex; + align-items: center; + gap: 4px; + `; + + return { indicatorByStatus, indicatorRow }; +}; diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx index cdb42fd0f2ecc..c5d2132e3da1c 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { useEuiTheme, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; +import { useHorizontalMinimalStepperStyles } from './horizontal_minimal_stepper.styles'; /** Mirrors the status subset used by EuiStepsHorizontal. */ export type MinimalStepStatus = 'current' | 'complete' | 'incomplete'; @@ -19,11 +20,6 @@ export interface MinimalStep { export interface HorizontalMinimalStepperProps { /** Steps with their current status — same shape as EuiStepsHorizontal steps (subset). */ steps: MinimalStep[]; - /** - * When true, indicators animate on step change using a spring curve. - * Defaults to true. Pass false to disable for testing or reduced-motion contexts. - */ - animated?: boolean; } /** @@ -32,6 +28,9 @@ export interface HorizontalMinimalStepperProps { * Renders a row of small indicators (dots + pill for current step), a bold * current-step title, and a muted N / N counter. * + * Animation respects `prefers-reduced-motion` automatically via the + * `euiCanAnimate` CSS media query in the styles file. + * * Layout is intentionally self-contained — place alongside other elements * using standard EuiFlexGroup/EuiFlexItem outside this component: * @@ -44,64 +43,35 @@ export interface HorizontalMinimalStepperProps { *
* */ -export const HorizontalMinimalStepper: React.FC = ({ - steps, - animated = true, -}) => { - const { euiTheme } = useEuiTheme(); - - const DOT_SIZE = 8; - const BAR_WIDTH = 24; - const BAR_HEIGHT = DOT_SIZE; - const GAP = 4; - - const activeColor = euiTheme.colors.primary; - const futureColor = euiTheme.colors.lightShade; +export const HorizontalMinimalStepper: React.FC = ({ steps }) => { + const euiThemeContext = useEuiTheme(); + const { indicatorByStatus, indicatorRow } = useHorizontalMinimalStepperStyles(euiThemeContext); const currentIndex = steps.findIndex((s) => s.status === 'current'); + const displayIndex = currentIndex >= 0 ? currentIndex : 0; const currentTitle = currentIndex >= 0 ? steps[currentIndex].title : ''; const total = steps.length; - const prefersReducedMotion = - typeof window !== 'undefined' && - window.matchMedia?.('(prefers-reduced-motion: reduce)').matches; - - const transition = - animated && !prefersReducedMotion - ? 'width 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + - 'border-radius 220ms cubic-bezier(0.34, 1.56, 0.64, 1), ' + - 'background-color 150ms ease' - : undefined; - return ( - - {/* Step indicators */} + + {/* Step indicators — decorative, described by the group aria-label */} -
- {steps.map((step, i) => { - const isCurrent = step.status === 'current'; - const isComplete = step.status === 'complete'; - - return ( -
- ); - })} +
+ {steps.map((step, i) => ( +
+ ))}
{/* Current step title */} - + {currentTitle} @@ -111,8 +81,12 @@ export const HorizontalMinimalStepper: React.FC = {/* N / N counter */} - - {currentIndex + 1} / {total} + + {displayIndex + 1} / {total} From f6dadf969be5e6c5318bef0ee571cc2a27f0d787 Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Mon, 11 May 2026 15:29:31 -0400 Subject: [PATCH 4/8] =?UTF-8?q?Remove=20ReducedMotion=20storybook=20story?= =?UTF-8?q?=20=E2=80=94=20not=20demonstrable=20without=20browser=20setting?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../horizontal_minimal_stepper.stories.tsx | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx index c34ae4469eaaa..8278625cff219 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx @@ -178,44 +178,3 @@ export const ThreeSteps: Story = { }, }; -// --------------------------------------------------------------------------- -// Reduced motion — simulate what users with prefers-reduced-motion see -// (The component handles this automatically via euiCanAnimate in its styles; -// this story documents the expected visual output for reviewers.) -// --------------------------------------------------------------------------- -export const ReducedMotion: Story = { - parameters: { - backgrounds: { default: 'light' }, - // Chromatic / Storybook can simulate prefers-reduced-motion via this parameter - chromatic: { prefersReducedMotion: 'reduce' }, - }, - render: () => { - const [currentStep, setCurrentStep] = useState(0); - const steps = makeSteps(RULE_STEPS, currentStep); - - return ( -
- - - - - setCurrentStep((s) => s - 1)}> - ← Back - - - - setCurrentStep((s) => s + 1)}> - Next → - - - - - - Animation is handled via euiCanAnimate in the Emotion styles — the transition - is automatically absent when prefers-reduced-motion: reduce is set. No{' '} - animated prop needed. - -
- ); - }, -}; From 2850910c93f01b63fae1abd842cd38e26640d3cc Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Mon, 11 May 2026 15:30:51 -0400 Subject: [PATCH 5/8] =?UTF-8?q?Remove=20WithIconToggle=20story=20=E2=80=94?= =?UTF-8?q?=20toggle=20placement=20is=20caller=20responsibility,=20not=20c?= =?UTF-8?q?omponent=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../horizontal_minimal_stepper.stories.tsx | 56 +------------------ 1 file changed, 1 insertion(+), 55 deletions(-) diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx index 8278625cff219..21fd0e20f4236 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx @@ -7,7 +7,7 @@ import React, { useState } from 'react'; import type { Meta, StoryObj } from '@storybook/react'; -import { EuiButton, EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui'; import { HorizontalMinimalStepper, type MinimalStep } from '../horizontal_minimal_stepper'; const meta: Meta = { @@ -90,60 +90,6 @@ export const AllStates: Story = { ), }; -// --------------------------------------------------------------------------- -// With a right-side icon slot (Form/YAML toggle alongside the stepper) -// --------------------------------------------------------------------------- -const TOGGLE_OPTIONS = [ - { id: 'form', label: 'Form', iconType: 'tableDensityNormal' }, - { id: 'yaml', label: 'YAML', iconType: 'editorCodeBlock' }, -]; - -export const WithIconToggle: Story = { - render: () => { - const [currentStep, setCurrentStep] = useState(0); - const [mode, setMode] = useState<'form' | 'yaml'>('form'); - const steps = makeSteps(RULE_STEPS, currentStep); - - return ( -
- - - - - - setMode(id as 'form' | 'yaml')} - buttonSize="compressed" - isIconOnly - /> - - - - - - setCurrentStep((s) => s - 1)}> - ← Back - - - - setCurrentStep((s) => s + 1)}> - Next → - - - - - - The Form/YAML toggle lives outside the stepper in a standard EuiFlexGroup — the stepper - itself has no slot API. - -
- ); - }, -}; - // --------------------------------------------------------------------------- // Three-step variant (no Recovery Condition — tracking disabled) // --------------------------------------------------------------------------- From 03b190e5386c5339207df063f39b0fae4614469b Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 11 May 2026 20:18:07 +0000 Subject: [PATCH 6/8] Changes from node scripts/eslint_all_files --no-cache --fix --- .../horizontal_minimal_stepper.stories.tsx | 21 +++++++++++++++---- .../horizontal_minimal_stepper.styles.ts | 5 +---- .../horizontal_minimal_stepper.tsx | 6 +----- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx index 21fd0e20f4236..4d62c6d03746f 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx @@ -30,7 +30,12 @@ const makeSteps = (titles: string[], currentIndex: number): MinimalStep[] => status: i < currentIndex ? 'complete' : i === currentIndex ? 'current' : 'incomplete', })); -const RULE_STEPS = ['Alert Condition', 'Recovery Condition', 'Details & Artifacts', 'Notifications']; +const RULE_STEPS = [ + 'Alert Condition', + 'Recovery Condition', + 'Details & Artifacts', + 'Notifications', +]; const RULE_STEPS_SHORT = ['Alert Condition', 'Details & Artifacts', 'Notifications']; // --------------------------------------------------------------------------- @@ -104,12 +109,21 @@ export const ThreeSteps: Story = { - setCurrentStep((s) => s - 1)}> + setCurrentStep((s) => s - 1)} + > ← Back - setCurrentStep((s) => s + 1)}> + setCurrentStep((s) => s + 1)} + > Next → @@ -123,4 +137,3 @@ export const ThreeSteps: Story = { ); }, }; - diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts index fddf8e8791641..cc2b953ea9a08 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.styles.ts @@ -19,10 +19,7 @@ export const useHorizontalMinimalStepperStyles = ({ euiTheme }: UseEuiTheme) => height: ${DOT_SIZE}px; flex-shrink: 0; ${euiCanAnimate} { - transition: - width 220ms ${SPRING}, - border-radius 220ms ${SPRING}, - background-color 150ms ease; + transition: width 220ms ${SPRING}, border-radius 220ms ${SPRING}, background-color 150ms ease; } `; diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx index c5d2132e3da1c..d796b41151231 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx @@ -81,11 +81,7 @@ export const HorizontalMinimalStepper: React.FC = {/* N / N counter */} - + {displayIndex + 1} / {total} From 2371c04150c75bf330aa4367debed111b6693985 Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Mon, 11 May 2026 23:24:44 -0400 Subject: [PATCH 7/8] Fix type, lint, and hook errors in HorizontalMinimalStepper - Add @emotion/react/types/css-prop to tsconfig types so the css prop typechecks on raw HTML elements (pattern from kbn-classic-stream-flyout) - Remove animated prop from stories (animation is CSS-only, no prop needed) - Extract useState hooks in story render functions into named components (InteractiveStory, ThreeStepsStory) to satisfy react-hooks/rules-of-hooks - Escape quotes in JSX text; reformat for prettier compliance Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../horizontal_minimal_stepper.stories.tsx | 150 +++++++++--------- .../alerting-v2-rule-form/tsconfig.json | 3 +- 2 files changed, 79 insertions(+), 74 deletions(-) diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx index 4d62c6d03746f..d3f6700ebfe4c 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/__stories__/horizontal_minimal_stepper.stories.tsx @@ -41,43 +41,45 @@ const RULE_STEPS_SHORT = ['Alert Condition', 'Details & Artifacts', 'Notificatio // --------------------------------------------------------------------------- // Interactive story — click through steps to see the animation // --------------------------------------------------------------------------- -export const Interactive: Story = { - render: () => { - const [currentStep, setCurrentStep] = useState(0); - const steps = makeSteps(RULE_STEPS, currentStep); +const InteractiveStory = () => { + const [currentStep, setCurrentStep] = useState(0); + const steps = makeSteps(RULE_STEPS, currentStep); - return ( -
- - - - - setCurrentStep((s) => Math.max(0, s - 1))} - > - ← Back - - - - setCurrentStep((s) => Math.min(RULE_STEPS.length - 1, s + 1))} - > - Next → - - - - - - Click Next/Back to see the dot→pill animation on the indicators. - -
- ); - }, + return ( +
+ + + + + setCurrentStep((s) => Math.max(0, s - 1))} + > + ← Back + + + + setCurrentStep((s) => Math.min(RULE_STEPS.length - 1, s + 1))} + > + Next → + + + + + + Click Next/Back to see the dot→pill animation on the indicators. + +
+ ); +}; + +export const Interactive: Story = { + render: () => , }; // --------------------------------------------------------------------------- @@ -98,42 +100,44 @@ export const AllStates: Story = { // --------------------------------------------------------------------------- // Three-step variant (no Recovery Condition — tracking disabled) // --------------------------------------------------------------------------- -export const ThreeSteps: Story = { - render: () => { - const [currentStep, setCurrentStep] = useState(0); - const steps = makeSteps(RULE_STEPS_SHORT, currentStep); +const ThreeStepsStory = () => { + const [currentStep, setCurrentStep] = useState(0); + const steps = makeSteps(RULE_STEPS_SHORT, currentStep); - return ( -
- - - - - setCurrentStep((s) => s - 1)} - > - ← Back - - - - setCurrentStep((s) => s + 1)} - > - Next → - - - - - - Three-step variant shown when "Track active and recovered state" is disabled (no Recovery - Condition step). - -
- ); - }, + return ( +
+ + + + + setCurrentStep((s) => s - 1)} + > + ← Back + + + + setCurrentStep((s) => s + 1)} + > + Next → + + + + + + Three-step variant shown when "Track active and recovered state" is disabled (no + Recovery Condition step). + +
+ ); +}; + +export const ThreeSteps: Story = { + render: () => , }; diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json index 050bdd7d321d7..f3d01debeca57 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json @@ -6,7 +6,8 @@ "jest", "node", "react", - "@testing-library/jest-dom" + "@testing-library/jest-dom", + "@emotion/react/types/css-prop" ] }, "include": [ From 5d6ce534ac0ccc99ae43b3ad4b1b5dec36bfece4 Mon Sep 17 00:00:00 2001 From: Jason Rhodes Date: Tue, 12 May 2026 00:26:52 -0400 Subject: [PATCH 8/8] Use ClassNames instead of css prop on raw divs for type safety The CI project-references build (tsc -b tsconfig.refs.json) does not pick up the @emotion/react/types/css-prop augmentation from package tsconfig, so the css prop on plain HTML elements fails type check there. Use @emotion/react's ClassNames render-prop instead: it converts SerializedStyles to real CSS class names applied via className, which is always valid on HTML elements and keeps the euiCanAnimate transitions working exactly as before. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../horizontal_minimal_stepper.tsx | 19 +++++++++++++------ .../alerting-v2-rule-form/tsconfig.json | 3 +-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx index d796b41151231..18f7f014b5358 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/flyout/compose_discover/horizontal_minimal_stepper.tsx @@ -6,6 +6,7 @@ */ import React from 'react'; +import { ClassNames } from '@emotion/react'; import { useEuiTheme, EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; import { useHorizontalMinimalStepperStyles } from './horizontal_minimal_stepper.styles'; @@ -60,13 +61,19 @@ export const HorizontalMinimalStepper: React.FC = role="group" aria-label={`Step ${displayIndex + 1} of ${total}: ${currentTitle}`} > - {/* Step indicators — decorative, described by the group aria-label */} + {/* Step indicators — decorative, described by the group aria-label. + ClassNames converts SerializedStyles → real CSS class names so we can + use className on plain divs without needing the Emotion JSX transform. */} -
- {steps.map((step, i) => ( -
- ))} -
+ + {({ css }) => ( +
+ {steps.map((step, i) => ( +
+ ))} +
+ )} + {/* Current step title */} diff --git a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json index f3d01debeca57..050bdd7d321d7 100644 --- a/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json +++ b/x-pack/platform/packages/shared/response-ops/alerting-v2-rule-form/tsconfig.json @@ -6,8 +6,7 @@ "jest", "node", "react", - "@testing-library/jest-dom", - "@emotion/react/types/css-prop" + "@testing-library/jest-dom" ] }, "include": [