Skip to content

Commit

Permalink
feat: add the downloading sub-step (#1221)
Browse files Browse the repository at this point in the history
* feat: add the downloading substep

Signed-off-by: Oleksii Orel <[email protected]>
  • Loading branch information
olexii4 committed Oct 14, 2024
1 parent 7d646f5 commit e31485c
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@

import React from 'react';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';

import Head from '@/components/Head';
import getComponentRenderer from '@/services/__mocks__/getComponentRenderer';
import { BrandingData } from '@/services/bootstrap/branding.constant';
import { FakeStoreBuilder } from '@/store/__mocks__/storeBuilder';

import Head from '..';
const { createSnapshot } = getComponentRenderer(getComponent);

jest.mock('react-helmet', () => {
const Helmet = (props: { children: React.ReactElement[] }) => {
Expand All @@ -28,27 +29,29 @@ jest.mock('react-helmet', () => {
});

describe('The head component for setting document title', () => {
const store = new FakeStoreBuilder()
.withBranding({ title: 'Dummy product title' } as BrandingData)
.build();
afterEach(() => {
jest.clearAllMocks();
});

it('should render default title correctly', () => {
const element = (
<Provider store={store}>
<Head />
</Provider>
);

expect(renderer.create(element).toJSON()).toMatchSnapshot();
const snapshot = createSnapshot();
expect(snapshot.toJSON()).toMatchSnapshot();
});

it('should render Quick Add page title correctly', () => {
const element = (
<Provider store={store}>
<Head pageName="Quick Add" />
</Provider>
);

expect(renderer.create(element).toJSON()).toMatchSnapshot();
const snapshot = createSnapshot('Quick Add');
expect(snapshot.toJSON()).toMatchSnapshot();
});
});

function getComponent(pageName?: string): React.ReactElement {
const store = new FakeStoreBuilder()
.withBranding({ title: 'Dummy product title' } as BrandingData)
.build();

return (
<Provider store={store}>
<Head pageName={pageName} />
</Provider>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2024 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import React from 'react';

import { ProgressStepTitle } from '@/components/WorkspaceProgress/StepTitle';

export type Props = {
distance: -1 | 0 | 1 | undefined;
title: string | undefined;
};

export class PureSubCondition extends React.PureComponent<Props> {
public render(): React.ReactElement {
const { distance, title } = this.props;

if (!title) {
return <></>;
}

return (
<ol className="pf-c-wizard__nav-list">
<li className="pf-c-wizard__nav-item">
<div className="pf-c-wizard__nav-link">
<ProgressStepTitle distance={distance}>{title}</ProgressStepTitle>
</div>
</li>
</ol>
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2018-2024 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
*/

import React from 'react';

import { PureSubCondition } from '@/components/WorkspaceProgress/StartingSteps/WorkspaceConditions/PureSubCondition';
import getComponentRenderer, { screen } from '@/services/__mocks__/getComponentRenderer';

const { createSnapshot, renderComponent } = getComponentRenderer(getComponent);

jest.mock('@/components/WorkspaceProgress/StepTitle');

describe('Starting sub-steps, checking rendering', () => {
afterEach(() => {
jest.clearAllMocks();
});

test('snapshot with no title', () => {
expect(createSnapshot(undefined)).toMatchSnapshot();
});

test('snapshot with a title', () => {
expect(createSnapshot('sub-step test', 1)).toMatchSnapshot();
});

it('should show the title and a proper icon(depends on the distance value)', () => {
let distanceElement: HTMLElement | null;
let stepTitle: HTMLElement | null;

const { reRenderComponent } = renderComponent('sub-step test 1', -1);

distanceElement = screen.queryByTestId('distance');
expect(distanceElement).not.toBeNull();
expect(distanceElement).toHaveTextContent('-1');

stepTitle = screen.queryByTestId('step-title');
expect(stepTitle).not.toBeNull();
expect(stepTitle).toHaveTextContent('sub-step test 1');

reRenderComponent('sub-step test 2', 0);

distanceElement = screen.queryByTestId('distance');
expect(distanceElement).not.toBeNull();
expect(distanceElement).toHaveTextContent('0');

stepTitle = screen.queryByTestId('step-title');
expect(stepTitle).not.toBeNull();
expect(stepTitle).toHaveTextContent('sub-step test 2');

reRenderComponent('sub-step test 3', 1);

distanceElement = screen.queryByTestId('distance');
expect(distanceElement).not.toBeNull();
expect(distanceElement).toHaveTextContent('1');

stepTitle = screen.queryByTestId('step-title');
expect(stepTitle).not.toBeNull();
expect(stepTitle).toHaveTextContent('sub-step test 3');

reRenderComponent(undefined, 1);

distanceElement = screen.queryByTestId('distance');
expect(distanceElement).toBeNull();

stepTitle = screen.queryByTestId('step-title');
expect(stepTitle).toBeNull();
});
});

function getComponent(title: string | undefined, distance?: -1 | 0 | 1): React.ReactElement {
return <PureSubCondition title={title} distance={distance} />;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Starting sub-steps, checking rendering snapshot with a title 1`] = `
<ol
className="pf-c-wizard__nav-list"
>
<li
className="pf-c-wizard__nav-item"
>
<div
className="pf-c-wizard__nav-link"
>
<div>
<div
data-testid="distance"
>
1
</div>
<span
className="done"
data-testid="step-title"
>
sub-step test
</span>
</div>
</div>
</li>
</ol>
`;

exports[`Starting sub-steps, checking rendering snapshot with no title 1`] = `null`;
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
ProgressStepState,
} from '@/components/WorkspaceProgress/ProgressStep';
import styles from '@/components/WorkspaceProgress/StartingSteps/WorkspaceConditions/index.module.css';
import { PureSubCondition } from '@/components/WorkspaceProgress/StartingSteps/WorkspaceConditions/PureSubCondition';
import { ProgressStepTitle } from '@/components/WorkspaceProgress/StepTitle';
import {
ConditionType,
Expand All @@ -38,9 +39,12 @@ export type State = ProgressStepState & {
isWarning: boolean;
isReady: boolean;
condition: ConditionType;
subConditionTitle: string;
};

export default class StartingStepWorkspaceConditions extends ProgressStep<Props, State> {
private timerId: number | undefined;

constructor(props: Props) {
super(props);

Expand All @@ -56,36 +60,67 @@ export default class StartingStepWorkspaceConditions extends ProgressStep<Props,
const prevCondition = prevProps?.condition;

let name: string;
let subConditionTitle: string;
if (state === undefined) {
name = condition.message || condition.type;
subConditionTitle = '';
} else {
name = condition.message || state.name;
subConditionTitle = state.subConditionTitle;
}

return {
isReady: isConditionReady(condition, prevCondition),
isWarning: isConditionError(condition, prevCondition),
name,
condition,
subConditionTitle,
};
}

private checkForSubCondition(condition: ConditionType): void {
if (this.timerId !== undefined || condition.status !== 'False') {
return;
}

// Show sub-condition only for DeploymentReady
if (condition.type === 'DeploymentReady') {
// Show sub-condition after 20 seconds
this.timerId = window.setTimeout(() => {
this.setState({
subConditionTitle:
'Downloading IDE binaries... (it can take a few minutes depending on your internet connection)',
});
}, 20000);
}
}

public componentDidMount() {
const state = this.buildState(this.props, undefined, this.state);
this.setState(state);

this.checkForSubCondition(state.condition);
}

public async componentDidUpdate(prevProps: Props) {
const state = this.buildState(this.props, prevProps, this.state);
this.setState(state);

if (state.subConditionTitle === '') {
this.checkForSubCondition(state.condition);
}
}

public shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
if (isEqual(this.props.condition, nextProps.condition) === false) {
if (!isEqual(this.props.condition, nextProps.condition)) {
return true;
}

if (!isEqual(this.state.condition, nextState.condition)) {
return true;
}

if (isEqual(this.state.condition, nextState.condition) === false) {
if (this.state.subConditionTitle !== nextState.subConditionTitle) {
return true;
}

Expand Down Expand Up @@ -123,8 +158,8 @@ export default class StartingStepWorkspaceConditions extends ProgressStep<Props,
}

render() {
const { hasChildren } = this.props;
const { isWarning: isWarning, isReady } = this.state;
const { isWarning: isWarning, isReady, subConditionTitle } = this.state;
const hasChildren = this.props.hasChildren || !!subConditionTitle;

const distance = isReady ? 1 : 0;
const isError = false;
Expand All @@ -139,6 +174,7 @@ export default class StartingStepWorkspaceConditions extends ProgressStep<Props,
isWarning={isWarning}
>
{this.name}
<PureSubCondition distance={distance} title={subConditionTitle} />
</ProgressStepTitle>
</React.Fragment>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import styles from '@/components/WorkspaceProgress/StepTitle/index.module.css';
export type Props = PropsWithChildren<{
className?: string;
distance: -1 | 0 | 1 | undefined;
hasChildren: boolean;
isError: boolean;
isWarning: boolean;
hasChildren?: boolean;
isError?: boolean;
isWarning?: boolean;
}>;

export class ProgressStepTitle extends React.Component<Props> {
Expand All @@ -41,7 +41,11 @@ export class ProgressStepTitle extends React.Component<Props> {

return (
<>
<ProgressStepTitleIcon distance={dist} isError={isError} isWarning={isWarning} />
<ProgressStepTitleIcon
distance={dist}
isError={isError === true}
isWarning={isWarning === true}
/>
<span data-testid="step-title" className={fullClassName}>
{children}
</span>
Expand Down
2 changes: 1 addition & 1 deletion run/local-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ if [ ! -d $DASHBOARD_FRONTEND/lib/public/dashboard/devfile-registry ]; then
-i "packages/devfile-registry/air-gap/index.json"

if [ -s "$DASHBOARD_FRONTEND/lib/public/dashboard/devfile-registry/air-gap/index.json" ]; then
scripts/sed_in_place 's|CHE_DASHBOARD_INTERNAL_URL|http://localhost:8080|g' "$DASHBOARD_FRONTEND/lib/public/dashboard/devfile-registry/air-gap/index.json"
$(pwd)/scripts/sed_in_place.sh 's|CHE_DASHBOARD_INTERNAL_URL|http://localhost:8080|g' "$DASHBOARD_FRONTEND/lib/public/dashboard/devfile-registry/air-gap/index.json"
fi
fi

Expand Down

0 comments on commit e31485c

Please sign in to comment.