Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tooltip above Start Execution button #1802

Merged
merged 10 commits into from
Sep 14, 2023
24 changes: 9 additions & 15 deletions assets/js/components/ClusterDetails/HanaClusterDetails.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import React from 'react';

import { groupBy } from 'lodash';
import classNames from 'classnames';
import PageHeader from '@components/PageHeader';
import BackButton from '@components/BackButton';
import Button from '@components/Button';

import ListView from '@components/ListView';
import Table from '@components/Table';
import Tooltip from '@components/Tooltip';
import TriggerChecksExecutionRequest from '@components/TriggerChecksExecutionRequest';
import ClusterNodeLink from '@components/ClusterDetails/ClusterNodeLink';
import ChecksResultOverview from '@components/ClusterDetails/ChecksResultOverview';
import ProviderLabel from '@components/ProviderLabel';
Expand Down Expand Up @@ -133,21 +131,17 @@ function HanaClusterDetails({
content="Select some Checks first!"
place="bottom"
>
<TriggerChecksExecutionRequest
cssClasses="flex rounded relative ml-0.5 disabled:bg-slate-50 disabled:text-slate-500 disabled:border-gray-400"
targetID={clusterID}
<Button
type="primary"
className="mx-1"
onClick={() => {
onStartExecution(clusterID, hosts, selectedChecks, navigate);
}}
disabled={startExecutionDisabled}
hosts={hosts.map(({ id }) => id)}
checks={selectedChecks}
onStartExecution={onStartExecution}
>
<EOS_PLAY_CIRCLE
className={classNames('inline-block fill-jungle-green-500', {
'fill-slate-500': startExecutionDisabled,
})}
/>{' '}
<span>Start Execution</span>
</TriggerChecksExecutionRequest>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
</Tooltip>
</div>
</div>
Expand Down
63 changes: 60 additions & 3 deletions assets/js/components/ClusterDetails/HanaClusterDetails.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,7 @@ describe('HanaClusterDetails component', () => {
/>
);

expect(
screen.getByText(`Start Execution`).closest('button')
).toBeDisabled();
expect(screen.getByText('Start Execution')).toBeDisabled();
}
);

Expand Down Expand Up @@ -285,4 +283,63 @@ describe('HanaClusterDetails component', () => {
});
});
});

const suggestionScenarios = [
{
selectedChecks: [],
hasSelectedChecks: false,
suggestionExpectation: (tooltipSuggestion) => {
tooltipSuggestion.toBeVisible();
},
},
{
selectedChecks: [faker.datatype.uuid()],
hasSelectedChecks: true,
suggestionExpectation: (tooltipSuggestion) => {
tooltipSuggestion.not.toBeInTheDocument();
},
},
];

it.each(suggestionScenarios)(
'should suggest to the user to select some checks only when the selection is empty',
async ({ selectedChecks, hasSelectedChecks, suggestionExpectation }) => {
const user = userEvent.setup();

const {
clusterID,
clusterName,
cib_last_written: cibLastWritten,
type: clusterType,
sid,
provider,
details,
} = clusterFactory.build();

const hosts = hostFactory.buildList(2, { cluster_id: clusterID });

renderWithRouter(
<HanaClusterDetails
clusterID={clusterID}
clusterName={clusterName}
selectedChecks={selectedChecks}
hasSelectedChecks={hasSelectedChecks}
hosts={hosts}
clusterType={clusterType}
cibLastWritten={cibLastWritten}
sid={sid}
provider={provider}
sapSystems={[]}
details={details}
lastExecution={null}
/>
);

const startExecutionButton = screen.getByText('Start Execution');
await user.hover(startExecutionButton);
suggestionExpectation(
expect(screen.queryByText('Select some Checks first!'))
);
}
);
});
23 changes: 15 additions & 8 deletions assets/js/components/ClusterSettingsPage/ClusterSettingsPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import PageHeader from '@components/PageHeader';
import BackButton from '@components/BackButton';
import LoadingBox from '@components/LoadingBox';
import WarningBanner from '@components/Banners/WarningBanner';
import Tooltip from '@components/Tooltip';

import { UNKNOWN_PROVIDER, VMWARE_PROVIDER, TARGET_CLUSTER } from '@lib/model';

Expand Down Expand Up @@ -124,15 +125,21 @@ function ClusterSettingsPage() {
>
Save Checks Selection
</Button>
<Button
type="primary"
className="mx-1"
onClick={requestExecution}
disabled={!canStartExecution(selectedChecks, saving)}
<Tooltip
className="w-56"
content="Click Start Execution or wait for Trento to periodically run checks."
visible={canStartExecution(selectedChecks, saving)}
>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
<Button
type="primary"
className="mx-1"
onClick={requestExecution}
disabled={!canStartExecution(selectedChecks, saving)}
>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
</Tooltip>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,47 @@ describe('ClusterDetails ClusterSettings component', () => {
)
).toBeVisible();
});

const suggestionScenarios = [
{
cluster: clusterFactory.build({
selected_checks: [],
}),
suggestionExpectation: (tooltipSuggestion) => {
tooltipSuggestion.not.toBeInTheDocument();
},
},
{
cluster: clusterFactory.build({
selected_checks: [faker.datatype.uuid()],
}),
suggestionExpectation: (tooltipSuggestion) => {
tooltipSuggestion.toBeVisible();
},
},
];

it.each(suggestionScenarios)(
'should suggest to the user to start an execution when the selection is not empty',
({ cluster, suggestionExpectation }) => {
const { id: clusterID } = cluster;
const [StatefulClusterSettings] = withState(<ClusterSettingsPage />, {
...defaultInitialState,
clustersList: { clusters: [cluster] },
});

renderWithRouterMatch(StatefulClusterSettings, {
path: 'clusters/:clusterID/settings',
route: `/clusters/${clusterID}/settings`,
});

suggestionExpectation(
expect(
screen.queryByText(
'Click Start Execution or wait for Trento to periodically run checks.'
)
)
);
}
);
});
41 changes: 27 additions & 14 deletions assets/js/components/HostDetails/HostChecksSelection.jsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { EOS_PLAY_CIRCLE } from 'eos-icons-react';

import PageHeader from '@components/PageHeader';
import BackButton from '@components/BackButton';
import Button from '@components/Button';
import ChecksSelection from '@components/ChecksSelection';
import Tooltip from '@components/Tooltip';

import HostInfoBox from './HostInfoBox';

const defaultSavedSelection = [];

function HostChecksSelection({
hostID,
hostName,
provider,
agentVersion,
selectedChecks,
catalog,
catalogError,
catalogLoading,
onUpdateCatalog,
isSavingSelection,
onSaveSelection,
onSelectedChecksChange,
hostChecksExecutionEnabled,
onStartExecution = () => {},
savedHostSelection = defaultSavedSelection,
}) {
const [selection, setSelection] = useState([]);
useEffect(() => {
setSelection(savedHostSelection);
}, [savedHostSelection]);

return (
<div className="w-full px-2 sm:px-0">
<BackButton url={`/hosts/${hostID}`}>Back to Host Details</BackButton>
Expand All @@ -39,20 +46,26 @@ function HostChecksSelection({
<Button
type="primary"
className="mx-1"
onClick={() => onSaveSelection(selectedChecks, hostID, hostName)}
onClick={() => onSaveSelection(selection, hostID, hostName)}
disabled={isSavingSelection}
>
Save Checks Selection
</Button>
<Button
type="primary"
className="mx-1"
onClick={onStartExecution}
disabled={hostChecksExecutionEnabled}
<Tooltip
className="w-56"
content="Click Start Execution or wait for Trento to periodically run checks."
visible={savedHostSelection?.length > 0}
>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
<Button
type="primary"
className="mx-1"
onClick={onStartExecution}
disabled={hostChecksExecutionEnabled}
>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
</Tooltip>
</div>
</div>
</div>
Expand All @@ -62,9 +75,9 @@ function HostChecksSelection({
catalog={catalog}
catalogError={catalogError}
loading={catalogLoading}
selectedChecks={selectedChecks}
selectedChecks={selection}
onUpdateCatalog={() => onUpdateCatalog()}
onChange={onSelectedChecksChange}
onChange={setSelection}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ describe('HostChecksSelection component', () => {
hostName={hostName}
provider={provider}
agentVersion={agentVersion}
selectedChecks={selectedChecks}
catalog={catalog}
catalogError={null}
catalogLoading={false}
Expand All @@ -58,6 +57,11 @@ describe('HostChecksSelection component', () => {
expect(
screen.getByRole('button', { name: 'Save Checks Selection' })
).toBeVisible();
expect(
screen.queryByText(
'Click Start Execution or wait for Trento to periodically run checks.'
)
).not.toBeInTheDocument();
expect(onUpdateCatalog).toHaveBeenCalled();
});
});
23 changes: 15 additions & 8 deletions assets/js/components/HostDetails/HostDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import WarningBanner from '@components/Banners/WarningBanner';
import CleanUpButton from '@components/CleanUpButton';
import DeregistrationModal from '@components/DeregistrationModal';
import { canStartExecution } from '@components/ChecksSelection';
import Tooltip from '@components/Tooltip';

import SuseLogo from '@static/suse_logo.svg';
import ChecksComingSoon from '@static/checks-coming-soon.svg';
Expand Down Expand Up @@ -129,15 +130,21 @@ function HostDetails({
<span>Show Results</span>
</Button>

<Button
type="primary"
className="mx-1"
onClick={requestHostChecksExecution}
disabled={!canStartExecution(selectedChecks, savingChecks)}
<Tooltip
isEnabled={!canStartExecution(selectedChecks, savingChecks)}
content="Select some Checks first!"
place="bottom"
>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
<Button
type="primary"
className="mx-1"
onClick={requestHostChecksExecution}
disabled={!canStartExecution(selectedChecks, savingChecks)}
>
<EOS_PLAY_CIRCLE className="fill-white inline-block align-sub" />{' '}
Start Execution
</Button>
</Tooltip>
</div>
</div>
<div className="pb-3">
Expand Down
15 changes: 13 additions & 2 deletions assets/js/components/HostDetails/HostDetails.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,22 @@ describe('HostDetails component', () => {
).toBeVisible();
});

it('should disable start execution button when checks are not selected', () => {
it('should disable start execution button when checks are not selected', async () => {
const user = userEvent.setup();

renderWithRouter(
<HostDetails agentVersion="1.0.0" selectedChecks={[]} />
);

const startExecutionButton = screen.getByText('Start Execution');
expect(startExecutionButton).toBeDisabled();

await user.hover(startExecutionButton);
expect(screen.getByText('Select some Checks first!')).toBeInTheDocument();
});

it('should enable start execution button when checks are selected', () => {
it('should enable start execution button when checks are selected', async () => {
const user = userEvent.setup();
const selectedChecks = [faker.animal.bear(), faker.animal.bear()];

renderWithRouter(
Expand All @@ -44,6 +50,11 @@ describe('HostDetails component', () => {

const startExecutionButton = screen.getByText('Start Execution');
expect(startExecutionButton).toBeEnabled();

await user.hover(startExecutionButton);
expect(
screen.queryByText('Select some Checks first!')
).not.toBeInTheDocument();
});
});

Expand Down
Loading