Skip to content

Conversation

@damian-polewski
Copy link
Contributor

@damian-polewski damian-polewski commented Dec 2, 2025

Summary

Closes #241644

This PR adds the step to name and confirm step to the create classic stream flyout.

Changes

  • Creates NameAndConfirmStep and NameStreamSection components.
  • Removes selectedTemplate and onTemplateSelect and moves the state inside CreateClassicStreamFlyout.
  • Adds new state (for handling selected index pattern, new stream name and validation) and validation logic to CreateClassicStreamFlyout.
  • Creates StreamNameInput component

Important

The Confirm template details section will be added in a separate PR to make it easier for review.

How to test

View the component in Storybook:

yarn storybook classic_stream_flyout
Design Implementation
509460451-d7a44513-c959-4a40-800d-7c9b1a1e9b49 Screenshot 2025-12-03 at 22 36 18
509460566-0c38f9f7-d4af-4cdd-850e-9a3142a150a0 Screenshot 2025-12-03 at 22 37 37
509461037-14f8ef37-ccc3-438b-b6b0-5c1c2bc53a3e Screenshot 2025-12-03 at 22 38 24
509461130-6de6854a-0f2b-4630-8a2d-1109b09857db Screenshot 2025-12-03 at 22 38 44
509461659-5be67d3b-b5f3-42fe-b5a7-743afc960f8f Screenshot 2025-12-03 at 23 06 47
509462012-a49a7f21-4d2a-4ff1-8fbd-5b27bb3a202b Screenshot 2025-12-03 at 22 39 44
509462190-9ab365ab-b6a0-4148-a8ca-ac53b26b76b9 Screenshot 2025-12-03 at 22 40 27

Checklist

Check the PR satisfies following conditions.

Reviewers should verify this PR satisfies this list as well.

  • Any text added follows EUI's writing guidelines, uses sentence case text and includes i18n support
  • Documentation was added for features that require explanation or tutorials
  • Unit or functional tests were updated or added to match the most common scenarios
  • If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the docker list
  • This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The release_note:breaking label should be applied in these situations.
  • Flaky Test Runner was used on any tests changed
  • The PR description includes the appropriate Release Notes section, and the correct release_note:* label is applied per the guidelines
  • Review the backport guidelines and apply applicable backport:* labels.

@damian-polewski damian-polewski self-assigned this Dec 2, 2025
@damian-polewski damian-polewski added Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more t// release_note:skip Skip the PR/issue when compiling release notes backport:skip This PR does not require backporting Feature:Streams This is the label for the Streams Project labels Dec 2, 2025
@damian-polewski damian-polewski changed the title [Streams] feat: add name and confirm step [Streams] Add name and confirm step to create classic stream flyout Dec 2, 2025
@damian-polewski damian-polewski marked this pull request as ready for review December 3, 2025 22:22
@damian-polewski damian-polewski requested a review from a team as a code owner December 3, 2025 22:22
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-management (Team:Kibana Management)

@damian-polewski damian-polewski requested review from a team and MichaelMarcialis December 3, 2025 22:23
@kapral18 kapral18 requested a review from Copilot December 3, 2025 23:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements the name and confirm step for the create classic stream flyout, allowing users to specify a stream name by filling in wildcards from selected index patterns.

Key changes:

  • Adds stream name validation (empty wildcards, duplicates, higher priority conflicts)
  • Implements dynamic input generation based on index pattern wildcards
  • Moves template selection state management into the flyout component

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
utils/utils.ts New utility functions for data retention formatting, wildcard validation, and stream name validation
stream_name_input/stream_name_input.tsx Component that dynamically generates inputs for index pattern wildcards with validation state
stream_name_input/stream_name_input.test.tsx Comprehensive tests for the StreamNameInput component
stream_name_input/stream_name_input.stories.tsx Storybook stories demonstrating various input patterns and validation states
name_and_confirm/name_stream_section.tsx Section component for naming streams with index pattern selection and validation messages
name_and_confirm/name_and_confirm_step.tsx Step component that orchestrates the name and confirm UI
create_classic_stream_flyout.tsx Updated flyout to manage template selection state, validation, and stream creation flow
create_classic_stream_flyout.test.tsx Updated tests covering navigation, validation, and callback behavior
create_classic_stream_flyout.stories.tsx Updated stories with validation scenarios and mock data
select_template_step.tsx Updated to use extracted formatDataRetention utility

Comment on lines +62 to +65
export const validateStreamName = async (
streamName: string,
onValidate?: StreamNameValidator
): Promise<StreamNameValidationResult> => {
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

The function lacks a JSDoc comment explaining its purpose, parameters, and return value. Add documentation describing the validation flow and how empty wildcards are checked before the external validator runs.

Copilot uses AI. Check for mistakes.
Comment on lines +105 to +113
const buildStreamName = (pattern: string, parts: string[]): string => {
let partIndex = 0;
return pattern.replace(/\*/g, () => {
// Keep * if the part is empty, so validation can detect unfilled wildcards
const part = parts[partIndex] || '*';
partIndex++;
return part;
});
};
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc documentation explaining the purpose of this function, its parameters (pattern and parts), and the return value. The comment on line 108 is helpful but insufficient for understanding the function's role in the component.

Copilot uses AI. Check for mistakes.
isFirst: boolean;
isLast: boolean;
}

Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc documentation explaining that this function parses an index pattern string into segments of static text and wildcards, and describe the returned PatternSegment array structure.

Suggested change
/**
* Parses an index pattern string into an array of segments, where each segment is either
* a static text segment or a wildcard segment.
*
* A static segment represents a sequence of characters between wildcards.
* A wildcard segment represents a '*' character in the pattern.
*
* @param pattern - The index pattern string to parse (e.g., 'foo-*-bar-*').
* @returns {PatternSegment[]} An array of PatternSegment objects, where each object has:
* - type: 'static' | 'wildcard'
* - value: the string value of the segment (static text or '*')
* - index: the zero-based index of the wildcard (only present for wildcards)
*/

Copilot uses AI. Check for mistakes.

return segments;
};

Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc documentation explaining how this function groups segments into input groups for rendering, including how prepend/append text is assigned to each group.

Suggested change
/**
* Groups pattern segments into input groups for rendering.
*
* Each input group corresponds to a wildcard segment in the pattern, and may have
* static text prepended or appended to it:
* - Static text immediately preceding a wildcard becomes the `prepend` for that group.
* - Static text immediately following the last wildcard becomes the `append` for the last group.
* - If there is no static text before a wildcard, `prepend` is undefined.
* - If there is no static text after the last wildcard, `append` is undefined.
* - The `isFirst` and `isLast` flags indicate whether the group is the first or last wildcard group.
*
* Example:
* For pattern "foo-*-bar-*", segments would be:
* [static "foo-", wildcard, static "bar-", wildcard]
* This produces two groups:
* 1. prepend: "foo-", wildcardIndex: 0, isFirst: true, isLast: false
* 2. prepend: "bar-", wildcardIndex: 1, isFirst: false, isLast: true
*
* @param segments Array of pattern segments (static and wildcard)
* @returns Array of input groups for rendering, with prepend/append text assigned
*/

Copilot uses AI. Check for mistakes.

return groups;
};

Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc documentation explaining that this function counts the number of wildcard characters (*) in the pattern string.

Suggested change
/**
* Counts the number of wildcard characters (*) in the given pattern string.
*
* @param pattern - The pattern string to search for wildcards.
* @returns The number of wildcard (*) characters in the pattern.
*/

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +170
const handleCreate = useCallback(async () => {
setHasAttemptedSubmit(true);
setIsValidating(true);

try {
const result = await validateStreamName(streamName, onValidate);
setValidationError(result.errorType);
setConflictingIndexPattern(result.conflictingIndexPattern);

if (result.errorType === null) {
onCreate(streamName);
}
} finally {
setIsValidating(false);
}
}, [streamName, onValidate, onCreate]);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc documentation explaining that this callback validates the stream name before creation, sets validation state, and only calls onCreate if validation passes.

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +112
const runValidation = useCallback(
async (name: string): Promise<boolean> => {
setIsValidating(true);
try {
const result = await validateStreamName(name, onValidate);
setValidationError(result.errorType);
setConflictingIndexPattern(result.conflictingIndexPattern);
return result.errorType === null;
} finally {
setIsValidating(false);
}
},
[onValidate]
);
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc documentation explaining that this function runs validation, updates state with the results, and returns true if validation passes.

Copilot uses AI. Check for mistakes.
@kapral18 kapral18 requested a review from Copilot December 3, 2025 23:38
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Comment on lines +37 to +63
const parseIndexPattern = (pattern: string): PatternSegment[] => {
if (!pattern) return [];

const segments: PatternSegment[] = [];
let currentSegment = '';
let wildcardIndex = 0;

for (let i = 0; i < pattern.length; i++) {
const char = pattern[i];
if (char === '*') {
if (currentSegment) {
segments.push({ type: 'static', value: currentSegment });
currentSegment = '';
}
segments.push({ type: 'wildcard', value: '*', index: wildcardIndex });
wildcardIndex++;
} else {
currentSegment += char;
}
}

if (currentSegment) {
segments.push({ type: 'static', value: currentSegment });
}

return segments;
};
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Add JSDoc comment to parseIndexPattern explaining that it parses an index pattern into segments of static text and wildcards, with each wildcard receiving a sequential index starting from 0.

Copilot uses AI. Check for mistakes.
Comment on lines +98 to +128
// Run validation and update state, returns true if validation passes
const runValidation = useCallback(
async (name: string): Promise<boolean> => {
setIsValidating(true);
try {
const result = await validateStreamName(name, onValidate);
setValidationError(result.errorType);
setConflictingIndexPattern(result.conflictingIndexPattern);
return result.errorType === null;
} finally {
setIsValidating(false);
}
},
[onValidate]
);

// Debounced validation - only runs after first submit attempt with an error
// When validation passes, reset to "submit only" mode
useDebounce(
() => {
if (hasAttemptedSubmit && validationError !== null) {
runValidation(streamName).then((isValid) => {
if (isValid) {
// Validation passed, reset to "submit only" mode
setHasAttemptedSubmit(false);
}
});
}
},
VALIDATION_DEBOUNCE_MS,
[streamName, hasAttemptedSubmit, validationError, runValidation]
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

[nitpick] The runValidation function is included in the debounce dependencies array, which will cause the debounce effect to reset whenever onValidate changes. Since runValidation is a stable callback (only depends on onValidate), this is acceptable. However, to make the dependency array more explicit about what actually triggers re-execution, consider extracting the debounced validation logic to avoid including runValidation itself in the dependencies.

Suggested change
// Run validation and update state, returns true if validation passes
const runValidation = useCallback(
async (name: string): Promise<boolean> => {
setIsValidating(true);
try {
const result = await validateStreamName(name, onValidate);
setValidationError(result.errorType);
setConflictingIndexPattern(result.conflictingIndexPattern);
return result.errorType === null;
} finally {
setIsValidating(false);
}
},
[onValidate]
);
// Debounced validation - only runs after first submit attempt with an error
// When validation passes, reset to "submit only" mode
useDebounce(
() => {
if (hasAttemptedSubmit && validationError !== null) {
runValidation(streamName).then((isValid) => {
if (isValid) {
// Validation passed, reset to "submit only" mode
setHasAttemptedSubmit(false);
}
});
}
},
VALIDATION_DEBOUNCE_MS,
[streamName, hasAttemptedSubmit, validationError, runValidation]
// Debounced validation - only runs after first submit attempt with an error
// When validation passes, reset to "submit only" mode
useDebounce(
() => {
if (hasAttemptedSubmit && validationError !== null) {
(async () => {
setIsValidating(true);
try {
const result = await validateStreamName(streamName, onValidate);
setValidationError(result.errorType);
setConflictingIndexPattern(result.conflictingIndexPattern);
if (result.errorType === null) {
// Validation passed, reset to "submit only" mode
setHasAttemptedSubmit(false);
}
} finally {
setIsValidating(false);
}
})();
}
},
VALIDATION_DEBOUNCE_MS,
[streamName, hasAttemptedSubmit, validationError, onValidate]

Copilot uses AI. Check for mistakes.
@elasticmachine
Copy link
Contributor

⏳ Build in-progress, with failures

Failed CI Steps

Test Failures

  • [job] [logs] FTR Configs #130 / Alerting bulkDisable should bulk disable and untrack
  • [job] [logs] FTR Configs #84 / Maps endpoints apis search ES|QL should return getValues response in expected shape
  • [job] [logs] FTR Configs #84 / Maps endpoints apis search ES|QL should return getValues response in expected shape

cc @damian-polewski

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip This PR does not require backporting Feature:Streams This is the label for the Streams Project release_note:skip Skip the PR/issue when compiling release notes Team:Kibana Management Dev Tools, Index Management, Upgrade Assistant, ILM, Ingest Node Pipelines, and more t//

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Streams] Create Name and confirm step

2 participants