mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Automatic Import] Fix the enter bug (#199894)
## Release Notes Fixes the bug where pressing Enter reloaded the Automatic Import. ## Summary - Fixes #198238 - Adds/fixes telemetry for CEL events. - Refactors navigation functionality. - Adds extensive unit tests and a Cypress test for it. ## Details When the user presses the Enter inside our input field, the expected action is to send the form, in this case completing the step. However, previously the form submission would instead lead to reloading the whole Automatic Import page. In this PR we capture the form submission event and bubble it up as `completeStep` to the main component. We also move the implementation from the `Footer` up to this main component `CreateIntegrationAssistant`. This helps collect all the details about the step order in one place and refactor this logic. As a result, pressing `Enter` in any field now either - Is processed by the field itself (in case of multi-line fields); - Leads to the same action as pressing the "Next" button (desired result); or - Does nothing (e.g. in the inputs in the "Define data stream and upload logs" group – the reason for this is unclear). We add CEL-specific telemetry identifiers so that telemetry for step 5 is not always reported as `Deploy Step`. We also rename a bunch of stuff that was named `...StepReady` into `...StepReadyToComplete` as the previous name was ambiguous. To demonstrate this ambiguity we've enlisted the help of GPT 4o: <img width="832" alt="SCR-20241125-tiaa" src="https://github.com/user-attachments/assets/ad6bcf7c-7cb2-41c2-ac6b-38924ce990d3"> ## Testing We provide a Cypress test for Enter behavior: pressing it on the "integration title" input should let the flow proceed to the next step. This test fails on `main`. We also provide unit tests for all steps of navigation functionality in `x-pack/plugins/integration_assistant/public/components/create_integration/create_integration_assistant/create_integration_assistant.test.tsx`: Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
d4194ba5eb
commit
d8bb72ebfd
19 changed files with 976 additions and 495 deletions
|
@ -6,11 +6,13 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { render, act } from '@testing-library/react';
|
||||
import { TestProvider } from '../../../mocks/test_provider';
|
||||
import { CreateIntegrationAssistant } from './create_integration_assistant';
|
||||
import type { State } from './state';
|
||||
import { ExperimentalFeaturesService } from '../../../services';
|
||||
import { mockReportEvent } from '../../../services/telemetry/mocks/service';
|
||||
import { TelemetryEventType } from '../../../services/telemetry/types';
|
||||
|
||||
export const defaultInitialState: State = {
|
||||
step: 1,
|
||||
|
@ -20,6 +22,7 @@ export const defaultInitialState: State = {
|
|||
hasCelInput: false,
|
||||
result: undefined,
|
||||
};
|
||||
|
||||
const mockInitialState = jest.fn((): State => defaultInitialState);
|
||||
jest.mock('./state', () => ({
|
||||
...jest.requireActual('./state'),
|
||||
|
@ -39,39 +42,45 @@ const mockCelInputStep = jest.fn(() => <div data-test-subj="celInputStepMock" />
|
|||
const mockReviewCelStep = jest.fn(() => <div data-test-subj="reviewCelStepMock" />);
|
||||
const mockDeployStep = jest.fn(() => <div data-test-subj="deployStepMock" />);
|
||||
|
||||
const mockIsConnectorStepReady = jest.fn();
|
||||
const mockIsIntegrationStepReady = jest.fn();
|
||||
const mockIsDataStreamStepReady = jest.fn();
|
||||
const mockIsReviewStepReady = jest.fn();
|
||||
const mockIsCelInputStepReady = jest.fn();
|
||||
const mockIsCelReviewStepReady = jest.fn();
|
||||
const mockIsConnectorStepReadyToComplete = jest.fn();
|
||||
const mockIsIntegrationStepReadyToComplete = jest.fn();
|
||||
const mockIsDataStreamStepReadyToComplete = jest.fn();
|
||||
const mockIsReviewStepReadyToComplete = jest.fn();
|
||||
const mockIsCelInputStepReadyToComplete = jest.fn();
|
||||
const mockIsCelReviewStepReadyToComplete = jest.fn();
|
||||
|
||||
jest.mock('./steps/connector_step', () => ({
|
||||
ConnectorStep: () => mockConnectorStep(),
|
||||
isConnectorStepReady: () => mockIsConnectorStepReady(),
|
||||
isConnectorStepReadyToComplete: () => mockIsConnectorStepReadyToComplete(),
|
||||
}));
|
||||
jest.mock('./steps/integration_step', () => ({
|
||||
IntegrationStep: () => mockIntegrationStep(),
|
||||
isIntegrationStepReady: () => mockIsIntegrationStepReady(),
|
||||
isIntegrationStepReadyToComplete: () => mockIsIntegrationStepReadyToComplete(),
|
||||
}));
|
||||
jest.mock('./steps/data_stream_step', () => ({
|
||||
DataStreamStep: () => mockDataStreamStep(),
|
||||
isDataStreamStepReady: () => mockIsDataStreamStepReady(),
|
||||
isDataStreamStepReadyToComplete: () => mockIsDataStreamStepReadyToComplete(),
|
||||
}));
|
||||
jest.mock('./steps/review_step', () => ({
|
||||
ReviewStep: () => mockReviewStep(),
|
||||
isReviewStepReady: () => mockIsReviewStepReady(),
|
||||
isReviewStepReadyToComplete: () => mockIsReviewStepReadyToComplete(),
|
||||
}));
|
||||
jest.mock('./steps/cel_input_step', () => ({
|
||||
CelInputStep: () => mockCelInputStep(),
|
||||
isCelInputStepReady: () => mockIsCelInputStepReady(),
|
||||
isCelInputStepReadyToComplete: () => mockIsCelInputStepReadyToComplete(),
|
||||
}));
|
||||
jest.mock('./steps/review_cel_step', () => ({
|
||||
ReviewCelStep: () => mockReviewCelStep(),
|
||||
isCelReviewStepReady: () => mockIsCelReviewStepReady(),
|
||||
isCelReviewStepReadyToComplete: () => mockIsCelReviewStepReadyToComplete(),
|
||||
}));
|
||||
jest.mock('./steps/deploy_step', () => ({ DeployStep: () => mockDeployStep() }));
|
||||
|
||||
const mockNavigate = jest.fn();
|
||||
jest.mock('../../../common/hooks/use_navigate', () => ({
|
||||
...jest.requireActual('../../../common/hooks/use_navigate'),
|
||||
useNavigate: () => mockNavigate,
|
||||
}));
|
||||
|
||||
const renderIntegrationAssistant = () =>
|
||||
render(<CreateIntegrationAssistant />, { wrapper: TestProvider });
|
||||
|
||||
|
@ -89,19 +98,116 @@ describe('CreateIntegration', () => {
|
|||
mockInitialState.mockReturnValueOnce({ ...defaultInitialState, step: 1 });
|
||||
});
|
||||
|
||||
it('should render connector', () => {
|
||||
it('shoud report telemetry for assistant open', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(TelemetryEventType.IntegrationAssistantOpen, {
|
||||
sessionId: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should render connector step', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('connectorStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call isConnectorStepReady', () => {
|
||||
it('should call isConnectorStepReadyToComplete', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockIsConnectorStepReady).toHaveBeenCalled();
|
||||
expect(mockIsConnectorStepReadyToComplete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show "Next" on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toHaveTextContent('Next');
|
||||
});
|
||||
|
||||
describe('when connector step is not done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsConnectorStepReadyToComplete.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should still enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should still enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when connector step is done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsConnectorStepReadyToComplete.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
const result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report telemetry for connector step completion', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 1,
|
||||
stepName: 'Connector Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
let result: ReturnType<typeof renderIntegrationAssistant>;
|
||||
beforeEach(() => {
|
||||
result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not report telemetry', () => {
|
||||
expect(mockReportEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should navigate to the landing page', () => {
|
||||
expect(mockNavigate).toHaveBeenCalledWith('landing');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 2', () => {
|
||||
beforeEach(() => {
|
||||
mockIsConnectorStepReadyToComplete.mockReturnValue(true);
|
||||
mockInitialState.mockReturnValueOnce({ ...defaultInitialState, step: 2 });
|
||||
});
|
||||
|
||||
|
@ -110,14 +216,109 @@ describe('CreateIntegration', () => {
|
|||
expect(result.queryByTestId('integrationStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call isIntegrationStepReady', () => {
|
||||
it('should call isIntegrationStepReadyToComplete', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockIsIntegrationStepReady).toHaveBeenCalled();
|
||||
expect(mockIsIntegrationStepReadyToComplete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show "Next" on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toHaveTextContent('Next');
|
||||
});
|
||||
|
||||
describe('when integration step is not done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsIntegrationStepReadyToComplete.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should still enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should still enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when integration step is done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsIntegrationStepReadyToComplete.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
const result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report telemetry for integration step completion', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 2,
|
||||
stepName: 'Integration Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
let result: ReturnType<typeof renderIntegrationAssistant>;
|
||||
beforeEach(() => {
|
||||
result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not report telemetry', () => {
|
||||
expect(mockReportEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show connector step', () => {
|
||||
expect(result.queryByTestId('connectorStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 3', () => {
|
||||
beforeEach(() => {
|
||||
mockIsConnectorStepReadyToComplete.mockReturnValue(true);
|
||||
mockIsIntegrationStepReadyToComplete.mockReturnValue(true);
|
||||
mockInitialState.mockReturnValueOnce({ ...defaultInitialState, step: 3 });
|
||||
});
|
||||
|
||||
|
@ -126,9 +327,116 @@ describe('CreateIntegration', () => {
|
|||
expect(result.queryByTestId('dataStreamStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call isDataStreamStepReady', () => {
|
||||
it('should call isDataStreamStepReadyToComplete', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockIsDataStreamStepReady).toHaveBeenCalled();
|
||||
expect(mockIsDataStreamStepReadyToComplete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show "Analyze logs" on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toHaveTextContent('Analyze logs');
|
||||
});
|
||||
|
||||
describe('when data stream step is not done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsDataStreamStepReadyToComplete.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should still enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should still enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when data stream step is done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsDataStreamStepReadyToComplete.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
const result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report telemetry for data stream step completion', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 3,
|
||||
stepName: 'DataStream Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should show loader on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('generatingLoader')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
// Not sure why there are two buttons when testing.
|
||||
const nextButton = result
|
||||
.getAllByTestId('buttonsFooter-nextButton')
|
||||
.filter((button) => button.textContent !== 'Next')[0];
|
||||
expect(nextButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
let result: ReturnType<typeof renderIntegrationAssistant>;
|
||||
beforeEach(() => {
|
||||
result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not report telemetry', () => {
|
||||
expect(mockReportEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show integration step', () => {
|
||||
expect(result.queryByTestId('integrationStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -142,9 +450,89 @@ describe('CreateIntegration', () => {
|
|||
expect(result.queryByTestId('reviewStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call isReviewStepReady', () => {
|
||||
it('should call isReviewStepReadyToComplete', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockIsReviewStepReady).toHaveBeenCalled();
|
||||
expect(mockIsReviewStepReadyToComplete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show the "Add to Elastic" on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toHaveTextContent('Add to Elastic');
|
||||
});
|
||||
|
||||
describe('when review step is not done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsReviewStepReadyToComplete.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should still enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should still enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when review step is done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsReviewStepReadyToComplete.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
const result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report telemetry for review step completion', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 4,
|
||||
stepName: 'Review Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should show deploy step', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('deployStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -157,6 +545,26 @@ describe('CreateIntegration', () => {
|
|||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('deployStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('buttonsFooter-backButton')).toBe(null);
|
||||
});
|
||||
|
||||
it('should hide the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('buttonsFooter-backButton')).toBe(null);
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show "Close" on the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toHaveTextContent('Close');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -179,9 +587,107 @@ describe('CreateIntegration with generateCel enabled', () => {
|
|||
expect(result.queryByTestId('celInputStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call isCelInputStepReady', () => {
|
||||
it('should call isCelInputStepReadyToComplete', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockIsCelInputStepReady).toHaveBeenCalled();
|
||||
expect(mockIsCelInputStepReadyToComplete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show "Generate CEL input configuration" on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toHaveTextContent(
|
||||
'Generate CEL input configuration'
|
||||
);
|
||||
});
|
||||
|
||||
it('should enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when cel input step is not done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsCelInputStepReadyToComplete.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
// Not sure why there are two buttons when testing.
|
||||
const nextButton = result
|
||||
.getAllByTestId('buttonsFooter-nextButton')
|
||||
.filter((button) => button.textContent !== 'Next')[0];
|
||||
expect(nextButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cel input step is done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsCelInputStepReadyToComplete.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
const result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report telemetry for cel input step completion', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 5,
|
||||
stepName: 'CEL Input Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should show loader on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('generatingLoader')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
// Not sure why there are two buttons when testing.
|
||||
const nextButton = result
|
||||
.getAllByTestId('buttonsFooter-nextButton')
|
||||
.filter((button) => button.textContent !== 'Next')[0];
|
||||
expect(nextButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
let result: ReturnType<typeof renderIntegrationAssistant>;
|
||||
beforeEach(() => {
|
||||
result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not report telemetry', () => {
|
||||
expect(mockReportEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show review step', () => {
|
||||
expect(result.queryByTestId('reviewStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -194,6 +700,26 @@ describe('CreateIntegration with generateCel enabled', () => {
|
|||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('deployStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('buttonsFooter-backButton')).toBe(null);
|
||||
});
|
||||
|
||||
it('should hide the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('buttonsFooter-backButton')).toBe(null);
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show "Close" on the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toHaveTextContent('Close');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 6', () => {
|
||||
|
@ -210,9 +736,89 @@ describe('CreateIntegration with generateCel enabled', () => {
|
|||
expect(result.queryByTestId('reviewCelStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call isReviewCelStepReady', () => {
|
||||
it('should call isReviewCelStepReadyToComplete', () => {
|
||||
renderIntegrationAssistant();
|
||||
expect(mockIsCelReviewStepReady).toHaveBeenCalled();
|
||||
expect(mockIsCelReviewStepReadyToComplete).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show the "Add to Elastic" on the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toHaveTextContent('Add to Elastic');
|
||||
});
|
||||
|
||||
describe('when cel review step is not done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsCelReviewStepReadyToComplete.mockReturnValue(false);
|
||||
});
|
||||
|
||||
it('should disable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeDisabled();
|
||||
});
|
||||
|
||||
it('should still enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should still enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when cel review step is done', () => {
|
||||
beforeEach(() => {
|
||||
mockIsCelReviewStepReadyToComplete.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-backButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
const result = renderIntegrationAssistant();
|
||||
mockReportEvent.mockClear();
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should report telemetry for review step completion', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 6,
|
||||
stepName: 'CEL Review Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should show deploy step', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('deployStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should enable the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-nextButton')).toBeEnabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -225,5 +831,25 @@ describe('CreateIntegration with generateCel enabled', () => {
|
|||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('deployStepMock')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should hide the back button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('buttonsFooter-backButton')).toBe(null);
|
||||
});
|
||||
|
||||
it('should hide the next button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.queryByTestId('buttonsFooter-backButton')).toBe(null);
|
||||
});
|
||||
|
||||
it('should enable the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toBeEnabled();
|
||||
});
|
||||
|
||||
it('should show "Close" on the cancel button', () => {
|
||||
const result = renderIntegrationAssistant();
|
||||
expect(result.getByTestId('buttonsFooter-cancelButton')).toHaveTextContent('Close');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,31 +5,96 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useReducer, useMemo, useEffect } from 'react';
|
||||
import React, { useReducer, useMemo, useEffect, useCallback } from 'react';
|
||||
import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template';
|
||||
import { Header } from './header';
|
||||
import { Footer } from './footer';
|
||||
import { ConnectorStep, isConnectorStepReady } from './steps/connector_step';
|
||||
import { IntegrationStep, isIntegrationStepReady } from './steps/integration_step';
|
||||
import { DataStreamStep, isDataStreamStepReady } from './steps/data_stream_step';
|
||||
import { ReviewStep, isReviewStepReady } from './steps/review_step';
|
||||
import { CelInputStep, isCelInputStepReady } from './steps/cel_input_step';
|
||||
import { ReviewCelStep, isCelReviewStepReady } from './steps/review_cel_step';
|
||||
import { useNavigate, Page } from '../../../common/hooks/use_navigate';
|
||||
import { ConnectorStep, isConnectorStepReadyToComplete } from './steps/connector_step';
|
||||
import { IntegrationStep, isIntegrationStepReadyToComplete } from './steps/integration_step';
|
||||
import { DataStreamStep, isDataStreamStepReadyToComplete } from './steps/data_stream_step';
|
||||
import { ReviewStep, isReviewStepReadyToComplete } from './steps/review_step';
|
||||
import { CelInputStep, isCelInputStepReadyToComplete } from './steps/cel_input_step';
|
||||
import { ReviewCelStep, isCelReviewStepReadyToComplete } from './steps/review_cel_step';
|
||||
import { DeployStep } from './steps/deploy_step';
|
||||
import { reducer, initialState, ActionsProvider, type Actions } from './state';
|
||||
import { useTelemetry } from '../telemetry';
|
||||
import { ExperimentalFeaturesService } from '../../../services';
|
||||
|
||||
const stepNames: Record<number | string, string> = {
|
||||
1: 'Connector Step',
|
||||
2: 'Integration Step',
|
||||
3: 'DataStream Step',
|
||||
4: 'Review Step',
|
||||
cel_input: 'CEL Input Step',
|
||||
cel_review: 'CEL Review Step',
|
||||
deploy: 'Deploy Step',
|
||||
};
|
||||
|
||||
export const CreateIntegrationAssistant = React.memo(() => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { generateCel: isGenerateCelEnabled } = ExperimentalFeaturesService.get();
|
||||
|
||||
const celInputStepIndex = isGenerateCelEnabled && state.hasCelInput ? 5 : null;
|
||||
const celReviewStepIndex = isGenerateCelEnabled && state.celInputResult ? 6 : null;
|
||||
const deployStepIndex =
|
||||
celInputStepIndex !== null || celReviewStepIndex !== null || state.step === 7 ? 7 : 5;
|
||||
|
||||
const stepName =
|
||||
state.step === deployStepIndex
|
||||
? stepNames.deploy
|
||||
: state.step === celReviewStepIndex
|
||||
? stepNames.cel_review
|
||||
: state.step === celInputStepIndex
|
||||
? stepNames.cel_input
|
||||
: state.step in stepNames
|
||||
? stepNames[state.step]
|
||||
: 'Unknown Step';
|
||||
|
||||
const telemetry = useTelemetry();
|
||||
useEffect(() => {
|
||||
telemetry.reportAssistantOpen();
|
||||
}, [telemetry]);
|
||||
|
||||
const isThisStepReadyToComplete = useMemo(() => {
|
||||
if (state.step === 1) {
|
||||
return isConnectorStepReadyToComplete(state);
|
||||
} else if (state.step === 2) {
|
||||
return isIntegrationStepReadyToComplete(state);
|
||||
} else if (state.step === 3) {
|
||||
return isDataStreamStepReadyToComplete(state);
|
||||
} else if (state.step === 4) {
|
||||
return isReviewStepReadyToComplete(state);
|
||||
} else if (isGenerateCelEnabled && state.step === 5) {
|
||||
return isCelInputStepReadyToComplete(state);
|
||||
} else if (isGenerateCelEnabled && state.step === 6) {
|
||||
return isCelReviewStepReadyToComplete(state);
|
||||
}
|
||||
return false;
|
||||
}, [state, isGenerateCelEnabled]);
|
||||
|
||||
const goBackStep = useCallback(() => {
|
||||
if (state.step === 1) {
|
||||
navigate(Page.landing);
|
||||
} else {
|
||||
dispatch({ type: 'SET_STEP', payload: state.step - 1 });
|
||||
}
|
||||
}, [navigate, dispatch, state.step]);
|
||||
|
||||
const completeStep = useCallback(() => {
|
||||
if (!isThisStepReadyToComplete) {
|
||||
// If the user tries to navigate to the next step without completing the current step.
|
||||
return;
|
||||
}
|
||||
telemetry.reportAssistantStepComplete({ step: state.step, stepName });
|
||||
if (state.step === 3 || state.step === celInputStepIndex) {
|
||||
dispatch({ type: 'SET_IS_GENERATING', payload: true });
|
||||
} else {
|
||||
dispatch({ type: 'SET_STEP', payload: state.step + 1 });
|
||||
}
|
||||
}, [telemetry, state.step, stepName, celInputStepIndex, isThisStepReadyToComplete]);
|
||||
|
||||
const actions = useMemo<Actions>(
|
||||
() => ({
|
||||
setStep: (payload) => {
|
||||
|
@ -53,27 +118,11 @@ export const CreateIntegrationAssistant = React.memo(() => {
|
|||
setCelInputResult: (payload) => {
|
||||
dispatch({ type: 'SET_CEL_INPUT_RESULT', payload });
|
||||
},
|
||||
completeStep,
|
||||
}),
|
||||
[]
|
||||
[completeStep]
|
||||
);
|
||||
|
||||
const isNextStepEnabled = useMemo(() => {
|
||||
if (state.step === 1) {
|
||||
return isConnectorStepReady(state);
|
||||
} else if (state.step === 2) {
|
||||
return isIntegrationStepReady(state);
|
||||
} else if (state.step === 3) {
|
||||
return isDataStreamStepReady(state);
|
||||
} else if (state.step === 4) {
|
||||
return isReviewStepReady(state);
|
||||
} else if (isGenerateCelEnabled && state.step === 5) {
|
||||
return isCelInputStepReady(state);
|
||||
} else if (isGenerateCelEnabled && state.step === 6) {
|
||||
return isCelReviewStepReady(state);
|
||||
}
|
||||
return false;
|
||||
}, [state, isGenerateCelEnabled]);
|
||||
|
||||
return (
|
||||
<ActionsProvider value={actions}>
|
||||
<KibanaPageTemplate>
|
||||
|
@ -95,28 +144,21 @@ export const CreateIntegrationAssistant = React.memo(() => {
|
|||
result={state.result}
|
||||
/>
|
||||
)}
|
||||
{state.step === 5 &&
|
||||
(isGenerateCelEnabled && state.hasCelInput ? (
|
||||
<CelInputStep
|
||||
integrationSettings={state.integrationSettings}
|
||||
connector={state.connector}
|
||||
isGenerating={state.isGenerating}
|
||||
/>
|
||||
) : (
|
||||
<DeployStep
|
||||
integrationSettings={state.integrationSettings}
|
||||
result={state.result}
|
||||
connector={state.connector}
|
||||
/>
|
||||
))}
|
||||
|
||||
{isGenerateCelEnabled && state.celInputResult && state.step === 6 && (
|
||||
{state.step === celInputStepIndex && (
|
||||
<CelInputStep
|
||||
integrationSettings={state.integrationSettings}
|
||||
connector={state.connector}
|
||||
isGenerating={state.isGenerating}
|
||||
/>
|
||||
)}
|
||||
{state.step === celReviewStepIndex && (
|
||||
<ReviewCelStep
|
||||
isGenerating={state.isGenerating}
|
||||
celInputResult={state.celInputResult}
|
||||
/>
|
||||
)}
|
||||
{isGenerateCelEnabled && state.step === 7 && (
|
||||
|
||||
{state.step === deployStepIndex && (
|
||||
<DeployStep
|
||||
integrationSettings={state.integrationSettings}
|
||||
result={state.result}
|
||||
|
@ -126,10 +168,14 @@ export const CreateIntegrationAssistant = React.memo(() => {
|
|||
)}
|
||||
</KibanaPageTemplate.Section>
|
||||
<Footer
|
||||
currentStep={state.step}
|
||||
isGenerating={state.isGenerating}
|
||||
hasCelInput={state.hasCelInput}
|
||||
isNextStepEnabled={isNextStepEnabled}
|
||||
isAnalyzeStep={state.step === 3}
|
||||
isAnalyzeCelStep={state.step === celInputStepIndex}
|
||||
isLastStep={state.step === deployStepIndex}
|
||||
isNextStepEnabled={isThisStepReadyToComplete && !state.isGenerating}
|
||||
isNextAddingToElastic={state.step === deployStepIndex - 1}
|
||||
onBack={goBackStep}
|
||||
onNext={completeStep}
|
||||
/>
|
||||
</KibanaPageTemplate>
|
||||
</ActionsProvider>
|
||||
|
|
|
@ -6,13 +6,11 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, act, type RenderResult } from '@testing-library/react';
|
||||
import { render, type RenderResult } from '@testing-library/react';
|
||||
import { TestProvider } from '../../../../mocks/test_provider';
|
||||
import { Footer } from './footer';
|
||||
import { ActionsProvider } from '../state';
|
||||
import { mockActions } from '../mocks/state';
|
||||
import { mockReportEvent } from '../../../../services/telemetry/mocks/service';
|
||||
import { TelemetryEventType } from '../../../../services/telemetry/types';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
const mockNavigate = jest.fn();
|
||||
|
@ -39,15 +37,12 @@ describe('Footer', () => {
|
|||
} as never);
|
||||
});
|
||||
|
||||
describe('when rendered', () => {
|
||||
describe('when rendered for the most common case', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(
|
||||
<Footer currentStep={1} isGenerating={false} hasCelInput={false} isNextStepEnabled />,
|
||||
{
|
||||
wrapper,
|
||||
}
|
||||
);
|
||||
result = render(<Footer isNextStepEnabled />, {
|
||||
wrapper,
|
||||
});
|
||||
});
|
||||
it('should render footer buttons component', () => {
|
||||
expect(result.queryByTestId('buttonsFooter')).toBeInTheDocument();
|
||||
|
@ -65,230 +60,4 @@ describe('Footer', () => {
|
|||
expect(result.queryByTestId('buttonsFooter-nextButton')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 1', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(
|
||||
<Footer currentStep={1} isGenerating={false} hasCelInput={false} isNextStepEnabled />,
|
||||
{
|
||||
wrapper,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 2', () => {
|
||||
expect(mockActions.setStep).toHaveBeenCalledWith(2);
|
||||
});
|
||||
|
||||
it('should report telemetry', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 1,
|
||||
stepName: 'Connector Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should navigate to landing', () => {
|
||||
expect(mockNavigate).toHaveBeenCalledWith('landing');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 2', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(
|
||||
<Footer currentStep={2} isGenerating={false} hasCelInput={false} isNextStepEnabled />,
|
||||
{
|
||||
wrapper,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 3', () => {
|
||||
expect(mockActions.setStep).toHaveBeenCalledWith(3);
|
||||
});
|
||||
|
||||
it('should report telemetry', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 2,
|
||||
stepName: 'Integration Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 1', () => {
|
||||
expect(mockActions.setStep).toHaveBeenCalledWith(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 3', () => {
|
||||
describe('when it is not generating', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(
|
||||
<Footer currentStep={3} isGenerating={false} hasCelInput={false} isNextStepEnabled />,
|
||||
{
|
||||
wrapper,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 4', () => {
|
||||
expect(mockActions.setIsGenerating).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should report telemetry', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 3,
|
||||
stepName: 'DataStream Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 2', () => {
|
||||
expect(mockActions.setStep).toHaveBeenCalledWith(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when it is generating', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(
|
||||
<Footer currentStep={3} isGenerating={true} hasCelInput={false} isNextStepEnabled />,
|
||||
{
|
||||
wrapper,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should render the loader', () => {
|
||||
expect(result.queryByTestId('generatingLoader')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when step is 4', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(
|
||||
<Footer currentStep={4} isGenerating={false} hasCelInput={false} isNextStepEnabled />,
|
||||
{
|
||||
wrapper,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('when next button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-nextButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 5', () => {
|
||||
expect(mockActions.setStep).toHaveBeenCalledWith(5);
|
||||
});
|
||||
|
||||
it('should report telemetry', () => {
|
||||
expect(mockReportEvent).toHaveBeenCalledWith(
|
||||
TelemetryEventType.IntegrationAssistantStepComplete,
|
||||
{
|
||||
sessionId: expect.any(String),
|
||||
step: 4,
|
||||
stepName: 'Review Step',
|
||||
durationMs: expect.any(Number),
|
||||
sessionElapsedTime: expect.any(Number),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when back button is clicked', () => {
|
||||
beforeEach(() => {
|
||||
act(() => {
|
||||
result.getByTestId('buttonsFooter-backButton').click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should set step 3', () => {
|
||||
expect(mockActions.setStep).toHaveBeenCalledWith(3);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when next step is disabled', () => {
|
||||
let result: RenderResult;
|
||||
beforeEach(() => {
|
||||
result = render(<Footer currentStep={1} isGenerating={false} hasCelInput={false} />, {
|
||||
wrapper,
|
||||
});
|
||||
});
|
||||
it('should render next button disabled', () => {
|
||||
expect(result.queryByTestId('buttonsFooter-nextButton')).toBeDisabled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,13 +6,10 @@
|
|||
*/
|
||||
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { ButtonsFooter } from '../../../../common/components/buttons_footer';
|
||||
import { useNavigate, Page } from '../../../../common/hooks/use_navigate';
|
||||
import { useTelemetry } from '../../telemetry';
|
||||
import { useActions, type State } from '../state';
|
||||
import { type State } from '../state';
|
||||
import * as i18n from './translations';
|
||||
import { ExperimentalFeaturesService } from '../../../../services';
|
||||
|
||||
// Generation button for Step 3
|
||||
const AnalyzeButtonText = React.memo<{ isGenerating: boolean }>(({ isGenerating }) => {
|
||||
|
@ -43,61 +40,47 @@ const AnalyzeCelButtonText = React.memo<{ isGenerating: boolean }>(({ isGenerati
|
|||
AnalyzeCelButtonText.displayName = 'AnalyzeCelButtonText';
|
||||
|
||||
interface FooterProps {
|
||||
currentStep: State['step'];
|
||||
isGenerating: State['isGenerating'];
|
||||
hasCelInput: State['hasCelInput'];
|
||||
isGenerating?: State['isGenerating'];
|
||||
isAnalyzeStep?: boolean;
|
||||
isAnalyzeCelStep?: boolean;
|
||||
isLastStep?: boolean;
|
||||
isNextStepEnabled?: boolean;
|
||||
isNextAddingToElastic?: boolean;
|
||||
onBack?: () => void;
|
||||
onNext?: () => void;
|
||||
}
|
||||
|
||||
export const Footer = React.memo<FooterProps>(
|
||||
({ currentStep, isGenerating, hasCelInput, isNextStepEnabled = false }) => {
|
||||
const telemetry = useTelemetry();
|
||||
const { setStep, setIsGenerating } = useActions();
|
||||
const navigate = useNavigate();
|
||||
({
|
||||
isGenerating = false,
|
||||
isAnalyzeStep = false,
|
||||
isAnalyzeCelStep = false,
|
||||
isLastStep = false,
|
||||
isNextStepEnabled = false,
|
||||
isNextAddingToElastic = false,
|
||||
onBack = () => {},
|
||||
onNext = () => {},
|
||||
}) => {
|
||||
const nextButtonText = useMemo(
|
||||
() =>
|
||||
isNextAddingToElastic ? (
|
||||
i18n.ADD_TO_ELASTIC
|
||||
) : isAnalyzeStep ? (
|
||||
<AnalyzeButtonText isGenerating={isGenerating} />
|
||||
) : isAnalyzeCelStep ? (
|
||||
<AnalyzeCelButtonText isGenerating={isGenerating} />
|
||||
) : null,
|
||||
[isNextAddingToElastic, isAnalyzeStep, isGenerating, isAnalyzeCelStep]
|
||||
);
|
||||
|
||||
const { generateCel: isGenerateCelEnabled } = ExperimentalFeaturesService.get();
|
||||
|
||||
const onBack = useCallback(() => {
|
||||
if (currentStep === 1) {
|
||||
navigate(Page.landing);
|
||||
} else {
|
||||
setStep(currentStep - 1);
|
||||
}
|
||||
}, [currentStep, navigate, setStep]);
|
||||
|
||||
const onNext = useCallback(() => {
|
||||
telemetry.reportAssistantStepComplete({ step: currentStep });
|
||||
if (currentStep === 3 || currentStep === 5) {
|
||||
setIsGenerating(true);
|
||||
} else {
|
||||
setStep(currentStep + 1);
|
||||
}
|
||||
}, [currentStep, setIsGenerating, setStep, telemetry]);
|
||||
|
||||
const nextButtonText = useMemo(() => {
|
||||
if (currentStep === 3) {
|
||||
return <AnalyzeButtonText isGenerating={isGenerating} />;
|
||||
}
|
||||
if (currentStep === 4 && (!isGenerateCelEnabled || !hasCelInput)) {
|
||||
return i18n.ADD_TO_ELASTIC;
|
||||
}
|
||||
if (currentStep === 5 && isGenerateCelEnabled && hasCelInput) {
|
||||
return <AnalyzeCelButtonText isGenerating={isGenerating} />;
|
||||
}
|
||||
if (currentStep === 6 && isGenerateCelEnabled) {
|
||||
return i18n.ADD_TO_ELASTIC;
|
||||
}
|
||||
}, [currentStep, isGenerating, hasCelInput, isGenerateCelEnabled]);
|
||||
|
||||
if (currentStep === 7 || (currentStep === 5 && (!isGenerateCelEnabled || !hasCelInput))) {
|
||||
return <ButtonsFooter cancelButtonText={i18n.CLOSE} />;
|
||||
}
|
||||
return (
|
||||
return isLastStep ? (
|
||||
<ButtonsFooter cancelButtonText={i18n.CLOSE} />
|
||||
) : (
|
||||
<ButtonsFooter
|
||||
isNextDisabled={!isNextStepEnabled || isGenerating}
|
||||
nextButtonText={nextButtonText}
|
||||
isNextDisabled={!isNextStepEnabled}
|
||||
onBack={onBack}
|
||||
onNext={onNext}
|
||||
nextButtonText={nextButtonText}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -435,4 +435,5 @@ export const mockActions: Actions = {
|
|||
setHasCelInput: jest.fn(),
|
||||
setResult: jest.fn(),
|
||||
setCelInputResult: jest.fn(),
|
||||
completeStep: jest.fn(),
|
||||
};
|
||||
|
|
|
@ -78,6 +78,7 @@ export interface Actions {
|
|||
setHasCelInput: (payload: State['hasCelInput']) => void;
|
||||
setResult: (payload: State['result']) => void;
|
||||
setCelInputResult: (payload: State['celInputResult']) => void;
|
||||
completeStep: () => void;
|
||||
}
|
||||
|
||||
const ActionsContext = createContext<Actions | undefined>(undefined);
|
||||
|
|
|
@ -22,7 +22,7 @@ interface CelInputStepProps {
|
|||
|
||||
export const CelInputStep = React.memo<CelInputStepProps>(
|
||||
({ integrationSettings, connector, isGenerating }) => {
|
||||
const { setIsGenerating, setStep, setCelInputResult } = useActions();
|
||||
const { setIsGenerating, setStep, setCelInputResult, completeStep } = useActions();
|
||||
|
||||
const onGenerationCompleted = useCallback<OnComplete>(
|
||||
(result: State['celInputResult']) => {
|
||||
|
@ -43,7 +43,14 @@ export const CelInputStep = React.memo<CelInputStepProps>(
|
|||
<EuiFlexItem>
|
||||
<StepContentWrapper title={i18n.CEL_INPUT_TITLE} subtitle={i18n.CEL_INPUT_DESCRIPTION}>
|
||||
<EuiPanel hasShadow={false} hasBorder>
|
||||
<EuiForm component="form" fullWidth>
|
||||
<EuiForm
|
||||
component="form"
|
||||
fullWidth
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
completeStep();
|
||||
}}
|
||||
>
|
||||
<ApiDefinitionInput integrationSettings={integrationSettings} />
|
||||
</EuiForm>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import type { State } from '../../state';
|
||||
|
||||
export const isCelInputStepReady = ({ integrationSettings }: State) =>
|
||||
export const isCelInputStepReadyToComplete = ({ integrationSettings }: State) =>
|
||||
Boolean(
|
||||
integrationSettings?.name &&
|
||||
integrationSettings?.dataStreamTitle &&
|
||||
|
|
|
@ -6,12 +6,18 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useEuiTheme, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiRadio } from '@elastic/eui';
|
||||
import { noop } from 'lodash/fp';
|
||||
import {
|
||||
useEuiTheme,
|
||||
EuiBadge,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiRadio,
|
||||
EuiFormFieldset,
|
||||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { useKibana } from '../../../../../common/hooks/use_kibana';
|
||||
import type { AIConnector } from '../../types';
|
||||
import { useActions } from '../../state';
|
||||
|
||||
const useRowCss = () => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
@ -36,54 +42,60 @@ const useRowCss = () => {
|
|||
interface ConnectorSelectorProps {
|
||||
connectors: AIConnector[];
|
||||
selectedConnectorId: string | undefined;
|
||||
setConnector: (connector: AIConnector | undefined) => void;
|
||||
}
|
||||
export const ConnectorSelector = React.memo<ConnectorSelectorProps>(
|
||||
({ connectors, selectedConnectorId }) => {
|
||||
({ connectors, setConnector, selectedConnectorId }) => {
|
||||
const {
|
||||
triggersActionsUi: { actionTypeRegistry },
|
||||
} = useKibana().services;
|
||||
const { setConnector } = useActions();
|
||||
const rowCss = useRowCss();
|
||||
return (
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
direction="column"
|
||||
gutterSize="s"
|
||||
data-test-subj="connectorSelector"
|
||||
>
|
||||
{connectors.map((connector) => (
|
||||
<EuiFlexItem key={connector.id}>
|
||||
<EuiPanel
|
||||
key={connector.id}
|
||||
onClick={() => setConnector(connector)}
|
||||
hasShadow={false}
|
||||
hasBorder
|
||||
paddingSize="l"
|
||||
css={rowCss}
|
||||
data-test-subj={`connectorSelector-${connector.id}`}
|
||||
>
|
||||
<EuiFlexGroup direction="row" alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem>
|
||||
<EuiRadio
|
||||
label={connector.name}
|
||||
id={connector.id}
|
||||
checked={selectedConnectorId === connector.id}
|
||||
onChange={noop}
|
||||
data-test-subj={`connectorSelectorRadio-${connector.id}${
|
||||
selectedConnectorId === connector.id ? '-selected' : ''
|
||||
}`}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">
|
||||
{actionTypeRegistry.get(connector.actionTypeId).actionTypeTitle}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
<EuiFormFieldset>
|
||||
<EuiFlexGroup
|
||||
alignItems="stretch"
|
||||
direction="column"
|
||||
gutterSize="s"
|
||||
data-test-subj="connectorSelector"
|
||||
>
|
||||
{connectors.map((connector) => (
|
||||
<EuiFlexItem key={connector.id}>
|
||||
<EuiPanel
|
||||
element="button"
|
||||
type="button" // So that the enter button will not submit the form.
|
||||
role="radio"
|
||||
key={connector.id}
|
||||
onClick={() => setConnector(connector)}
|
||||
hasShadow={false}
|
||||
hasBorder
|
||||
paddingSize="l"
|
||||
css={rowCss}
|
||||
data-test-subj={`connectorSelector-${connector.id}`}
|
||||
>
|
||||
<EuiFlexGroup direction="row" alignItems="center" justifyContent="spaceBetween">
|
||||
<EuiFlexItem>
|
||||
<EuiRadio
|
||||
label={connector.name}
|
||||
id={connector.id}
|
||||
value={connector.id}
|
||||
checked={selectedConnectorId === connector.id}
|
||||
onChange={() => setConnector(connector)}
|
||||
data-test-subj={`connectorSelectorRadio-${connector.id}${
|
||||
selectedConnectorId === connector.id ? '-selected' : ''
|
||||
}`}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">
|
||||
{actionTypeRegistry.get(connector.actionTypeId).actionTypeTitle}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
</EuiFormFieldset>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { useLoadConnectors } from '@kbn/elastic-assistant';
|
||||
import {
|
||||
EuiForm,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLoadingSpinner,
|
||||
|
@ -42,7 +43,8 @@ interface ConnectorStepProps {
|
|||
export const ConnectorStep = React.memo<ConnectorStepProps>(({ connector }) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const { http, notifications } = useKibana().services;
|
||||
const { setConnector } = useActions();
|
||||
const { setConnector, completeStep } = useActions();
|
||||
|
||||
const [connectors, setConnectors] = useState<AIConnector[]>();
|
||||
const {
|
||||
isLoading,
|
||||
|
@ -69,41 +71,56 @@ export const ConnectorStep = React.memo<ConnectorStepProps>(({ connector }) => {
|
|||
const hasConnectors = !isLoading && connectors?.length;
|
||||
|
||||
return (
|
||||
<StepContentWrapper
|
||||
title={i18n.TITLE}
|
||||
subtitle={i18n.DESCRIPTION}
|
||||
right={hasConnectors ? <CreateConnectorPopover onConnectorSaved={onConnectorSaved} /> : null}
|
||||
<EuiForm
|
||||
component="form"
|
||||
fullWidth
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
completeStep();
|
||||
}}
|
||||
>
|
||||
<EuiFlexGroup direction="column" alignItems="stretch">
|
||||
<EuiFlexItem>
|
||||
{isLoading ? (
|
||||
<EuiLoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
{hasConnectors ? (
|
||||
<ConnectorSelector connectors={connectors} selectedConnectorId={connector?.id} />
|
||||
) : (
|
||||
<AuthorizationWrapper canCreateConnectors>
|
||||
<ConnectorSetup
|
||||
actionTypeIds={AllowedActionTypeIds}
|
||||
onConnectorSaved={onConnectorSaved}
|
||||
<StepContentWrapper
|
||||
title={i18n.TITLE}
|
||||
subtitle={i18n.DESCRIPTION}
|
||||
right={
|
||||
hasConnectors ? <CreateConnectorPopover onConnectorSaved={onConnectorSaved} /> : null
|
||||
}
|
||||
>
|
||||
<EuiFlexGroup direction="column" alignItems="stretch">
|
||||
<EuiFlexItem>
|
||||
{isLoading ? (
|
||||
<EuiLoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
{hasConnectors ? (
|
||||
<ConnectorSelector
|
||||
connectors={connectors}
|
||||
setConnector={setConnector}
|
||||
selectedConnectorId={connector?.id}
|
||||
/>
|
||||
</AuthorizationWrapper>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="s" color="subdued">
|
||||
<EuiFlexGroup direction="row" gutterSize="xs" alignItems="flexStart">
|
||||
<EuiFlexItem grow={false} css={{ margin: euiTheme.size.xxs }}>
|
||||
<EuiIcon type="iInCircle" />
|
||||
) : (
|
||||
<AuthorizationWrapper canCreateConnectors>
|
||||
<ConnectorSetup
|
||||
actionTypeIds={AllowedActionTypeIds}
|
||||
onConnectorSaved={onConnectorSaved}
|
||||
/>
|
||||
</AuthorizationWrapper>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{i18n.SUPPORTED_MODELS_INFO}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiText>
|
||||
</StepContentWrapper>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiText size="s" color="subdued">
|
||||
<EuiFlexGroup direction="row" gutterSize="xs" alignItems="flexStart">
|
||||
<EuiFlexItem grow={false} css={{ margin: euiTheme.size.xxs }}>
|
||||
<EuiIcon type="iInCircle" />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>{i18n.SUPPORTED_MODELS_INFO}</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiText>
|
||||
</StepContentWrapper>
|
||||
</EuiForm>
|
||||
);
|
||||
});
|
||||
ConnectorStep.displayName = 'ConnectorStep';
|
||||
|
|
|
@ -7,4 +7,4 @@
|
|||
|
||||
import type { State } from '../../state';
|
||||
|
||||
export const isConnectorStepReady = ({ connector }: State) => connector != null;
|
||||
export const isConnectorStepReadyToComplete = ({ connector }: State) => connector != null;
|
||||
|
|
|
@ -53,8 +53,14 @@ interface DataStreamStepProps {
|
|||
}
|
||||
export const DataStreamStep = React.memo<DataStreamStepProps>(
|
||||
({ integrationSettings, connector, isGenerating }) => {
|
||||
const { setIntegrationSettings, setIsGenerating, setHasCelInput, setStep, setResult } =
|
||||
useActions();
|
||||
const {
|
||||
setIntegrationSettings,
|
||||
setIsGenerating,
|
||||
setHasCelInput,
|
||||
setStep,
|
||||
setResult,
|
||||
completeStep,
|
||||
} = useActions();
|
||||
const { isLoading: isLoadingPackageNames, packageNames } = useLoadPackageNames(); // this is used to avoid duplicate names
|
||||
|
||||
const [name, setName] = useState<string>(integrationSettings?.name ?? '');
|
||||
|
@ -150,14 +156,21 @@ export const DataStreamStep = React.memo<DataStreamStepProps>(
|
|||
);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup direction="column" gutterSize="l" data-test-subj="dataStreamStep">
|
||||
<EuiFlexItem>
|
||||
<StepContentWrapper
|
||||
title={i18n.INTEGRATION_NAME_TITLE}
|
||||
subtitle={i18n.INTEGRATION_NAME_DESCRIPTION}
|
||||
>
|
||||
<EuiPanel hasShadow={false} hasBorder>
|
||||
<EuiForm component="form" fullWidth>
|
||||
<EuiForm
|
||||
component="form"
|
||||
fullWidth
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
completeStep();
|
||||
}}
|
||||
>
|
||||
<EuiFlexGroup direction="column" gutterSize="l" data-test-subj="dataStreamStep">
|
||||
<EuiFlexItem>
|
||||
<StepContentWrapper
|
||||
title={i18n.INTEGRATION_NAME_TITLE}
|
||||
subtitle={i18n.INTEGRATION_NAME_DESCRIPTION}
|
||||
>
|
||||
<EuiPanel hasShadow={false} hasBorder>
|
||||
<EuiFormRow
|
||||
label={i18n.INTEGRATION_NAME_LABEL}
|
||||
helpText={
|
||||
|
@ -176,18 +189,16 @@ export const DataStreamStep = React.memo<DataStreamStepProps>(
|
|||
disabled={isLoadingPackageNames}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</EuiPanel>
|
||||
</StepContentWrapper>
|
||||
</EuiFlexItem>
|
||||
</EuiPanel>
|
||||
</StepContentWrapper>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem>
|
||||
<StepContentWrapper
|
||||
title={i18n.DATA_STREAM_TITLE}
|
||||
subtitle={i18n.DATA_STREAM_DESCRIPTION}
|
||||
>
|
||||
<EuiPanel hasShadow={false} hasBorder>
|
||||
<EuiForm component="form" fullWidth>
|
||||
<EuiFlexItem>
|
||||
<StepContentWrapper
|
||||
title={i18n.DATA_STREAM_TITLE}
|
||||
subtitle={i18n.DATA_STREAM_DESCRIPTION}
|
||||
>
|
||||
<EuiPanel hasShadow={false} hasBorder>
|
||||
<EuiFormRow label={i18n.DATA_STREAM_TITLE_LABEL}>
|
||||
<EuiFieldText
|
||||
name="dataStreamTitle"
|
||||
|
@ -228,19 +239,19 @@ export const DataStreamStep = React.memo<DataStreamStepProps>(
|
|||
/>
|
||||
</EuiFormRow>
|
||||
<SampleLogsInput integrationSettings={integrationSettings} />
|
||||
</EuiForm>
|
||||
</EuiPanel>
|
||||
</StepContentWrapper>
|
||||
{isGenerating && (
|
||||
<GenerationModal
|
||||
integrationSettings={integrationSettings}
|
||||
connector={connector}
|
||||
onComplete={onGenerationCompleted}
|
||||
onClose={onGenerationClosed}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</StepContentWrapper>
|
||||
{isGenerating && (
|
||||
<GenerationModal
|
||||
integrationSettings={integrationSettings}
|
||||
connector={connector}
|
||||
onComplete={onGenerationCompleted}
|
||||
onClose={onGenerationClosed}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiForm>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
import type { State } from '../../state';
|
||||
|
||||
export const isDataStreamStepReady = ({ integrationSettings }: State) =>
|
||||
export const isDataStreamStepReadyToComplete = ({ integrationSettings }: State) =>
|
||||
Boolean(
|
||||
integrationSettings?.name &&
|
||||
integrationSettings?.dataStreamTitle &&
|
||||
|
|
|
@ -51,7 +51,7 @@ interface IntegrationStepProps {
|
|||
|
||||
export const IntegrationStep = React.memo<IntegrationStepProps>(({ integrationSettings }) => {
|
||||
const styles = useLayoutStyles();
|
||||
const { setIntegrationSettings } = useActions();
|
||||
const { setIntegrationSettings, completeStep } = useActions();
|
||||
const [logoError, setLogoError] = React.useState<string>();
|
||||
|
||||
const setIntegrationValues = useCallback(
|
||||
|
@ -95,7 +95,14 @@ export const IntegrationStep = React.memo<IntegrationStepProps>(({ integrationSe
|
|||
<EuiPanel paddingSize="none" hasShadow={false} hasBorder data-test-subj="integrationStep">
|
||||
<EuiFlexGroup direction="row" gutterSize="none">
|
||||
<EuiFlexItem css={styles.left}>
|
||||
<EuiForm component="form" fullWidth>
|
||||
<EuiForm
|
||||
component="form"
|
||||
fullWidth
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
completeStep();
|
||||
}}
|
||||
>
|
||||
<EuiFormRow label={i18n.TITLE_LABEL}>
|
||||
<EuiFieldText
|
||||
name="title"
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
|
||||
import type { State } from '../../state';
|
||||
|
||||
export const isIntegrationStepReady = ({ integrationSettings }: State) =>
|
||||
export const isIntegrationStepReadyToComplete = ({ integrationSettings }: State) =>
|
||||
Boolean(integrationSettings?.title && integrationSettings?.description);
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
|
||||
import type { State } from '../../state';
|
||||
|
||||
export const isCelReviewStepReady = ({ isGenerating, celInputResult }: State) =>
|
||||
export const isCelReviewStepReadyToComplete = ({ isGenerating, celInputResult }: State) =>
|
||||
isGenerating === false && celInputResult != null;
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiLoadingSpinner, EuiPanel } from '@elastic/eui';
|
||||
import { EuiForm, EuiLoadingSpinner, EuiPanel } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import type { State } from '../../state';
|
||||
import { useActions, type State } from '../../state';
|
||||
import { StepContentWrapper } from '../step_content_wrapper';
|
||||
import * as i18n from './translations';
|
||||
import { CelConfigResults } from './cel_config_results';
|
||||
|
@ -17,15 +17,24 @@ interface ReviewCelStepProps {
|
|||
}
|
||||
|
||||
export const ReviewCelStep = React.memo<ReviewCelStepProps>(({ isGenerating, celInputResult }) => {
|
||||
const { completeStep } = useActions();
|
||||
|
||||
return (
|
||||
<StepContentWrapper title={i18n.TITLE} subtitle={i18n.DESCRIPTION}>
|
||||
<EuiPanel hasShadow={false} hasBorder data-test-subj="reviewCelStep">
|
||||
{isGenerating ? (
|
||||
<EuiLoadingSpinner size="l" />
|
||||
) : (
|
||||
<>
|
||||
<EuiForm
|
||||
component="form"
|
||||
fullWidth
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
completeStep();
|
||||
}}
|
||||
>
|
||||
<CelConfigResults celInputResult={celInputResult} />
|
||||
</>
|
||||
</EuiForm>
|
||||
)}
|
||||
</EuiPanel>
|
||||
</StepContentWrapper>
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
|
||||
import type { State } from '../../state';
|
||||
|
||||
export const isReviewStepReady = ({ isGenerating, result }: State) =>
|
||||
export const isReviewStepReadyToComplete = ({ isGenerating, result }: State) =>
|
||||
isGenerating === false && result != null;
|
||||
|
|
|
@ -15,20 +15,12 @@ import type {
|
|||
IntegrationSettings,
|
||||
} from './create_integration_assistant/types';
|
||||
|
||||
const stepNames: Record<string, string> = {
|
||||
'1': 'Connector Step',
|
||||
'2': 'Integration Step',
|
||||
'3': 'DataStream Step',
|
||||
'4': 'Review Step',
|
||||
'5': 'Deploy Step',
|
||||
};
|
||||
|
||||
type ReportUploadZipIntegrationComplete = (params: {
|
||||
integrationName?: string;
|
||||
error?: string;
|
||||
}) => void;
|
||||
type ReportAssistantOpen = () => void;
|
||||
type ReportAssistantStepComplete = (params: { step: number }) => void;
|
||||
type ReportAssistantStepComplete = (params: { step: number; stepName: string }) => void;
|
||||
type ReportGenerationComplete = (params: {
|
||||
connector: AIConnector;
|
||||
integrationSettings: IntegrationSettings;
|
||||
|
@ -92,11 +84,11 @@ export const TelemetryContextProvider = React.memo<PropsWithChildren<{}>>(({ chi
|
|||
}, [telemetry]);
|
||||
|
||||
const reportAssistantStepComplete = useCallback<ReportAssistantStepComplete>(
|
||||
({ step }) => {
|
||||
({ step, stepName }) => {
|
||||
telemetry.reportEvent(TelemetryEventType.IntegrationAssistantStepComplete, {
|
||||
sessionId: sessionData.current.sessionId,
|
||||
step,
|
||||
stepName: stepNames[step.toString()] ?? 'Unknown Step',
|
||||
stepName,
|
||||
durationMs: Date.now() - stepsData.current.startedAt,
|
||||
sessionElapsedTime: Date.now() - sessionData.current.startedAt,
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue