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

Onboarding fleet integration #8

Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
b2a2873
add fleet integration
angorayc Sep 17, 2024
b226a3c
add installation status
angorayc Sep 17, 2024
c6b1992
installation status
angorayc Sep 18, 2024
5053567
clean up
angorayc Sep 20, 2024
e64e0aa
integration badge
angorayc Sep 20, 2024
f66bbd8
add customised cancellation link
angorayc Sep 24, 2024
a3ffd1e
agent still required
angorayc Sep 24, 2024
25943e0
display only 9 cards in the first tab
angorayc Sep 24, 2024
172345f
cancellation link
angorayc Sep 25, 2024
94fd73f
types
angorayc Sep 26, 2024
1800b07
add empty state styles
angorayc Sep 26, 2024
ef9d5ad
update complete badge
angorayc Sep 26, 2024
b6857c3
unit tests
angorayc Sep 27, 2024
ea3266f
update installStatus in IntegrationCardItem
angorayc Sep 27, 2024
80b6ee3
agentless available callout
angorayc Sep 27, 2024
2487395
update routes state
angorayc Sep 30, 2024
0ff541b
update onSave link in fleet
angorayc Sep 30, 2024
9621486
update unit tests
angorayc Sep 30, 2024
0856719
fix apm link
angorayc Oct 1, 2024
ca0da36
fixup
angorayc Oct 1, 2024
69c71cc
clean up
angorayc Oct 1, 2024
8da2fc8
update installation label
angorayc Oct 1, 2024
3733369
types
angorayc Oct 1, 2024
49e182e
add comments in fleet
angorayc Oct 3, 2024
03adb15
tests
angorayc Oct 3, 2024
97ebde6
code review
angorayc Oct 4, 2024
1be3165
search term behaviour
angorayc Oct 7, 2024
e833767
unit tests
angorayc Oct 7, 2024
6712a29
mv withLazyHook
angorayc Oct 7, 2024
1c65dac
add static integration list
angorayc Oct 8, 2024
90e72ae
support custom sorting on integrations
angorayc Oct 8, 2024
506c929
add endpoint callout
angorayc Oct 8, 2024
c6d35d2
remove duplicated state
angorayc Oct 8, 2024
71edb9c
type casting
angorayc Oct 8, 2024
cfd190c
update network sub category
angorayc Oct 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { render, screen } from '@testing-library/react';

import { installationStatuses } from '../../../../../../common/constants';

import {
InstallationStatus,
getLineClampStyles,
shouldShowInstallationStatus,
} from './installation_status';

// Mock useEuiTheme to return a mock theme
jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),
useEuiTheme: () => ({
euiTheme: {
border: { radius: { medium: '4px' } },
size: { s: '8px', m: '16px' },
colors: { emptyShade: '#FFFFFF' },
},
}),
}));

describe('getLineClampStyles', () => {
it('returns the correct styles when lineClamp is provided', () => {
expect(getLineClampStyles(3)).toEqual(
'-webkit-line-clamp: 3;display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;'
);
});

it('returns an empty string when lineClamp is not provided', () => {
expect(getLineClampStyles()).toEqual('');
});
});

describe('shouldShowInstallationStatus', () => {
it('returns false when showInstallationStatus is false', () => {
expect(
shouldShowInstallationStatus({
installStatus: installationStatuses.Installed,
showInstallationStatus: false,
})
).toEqual(false);
});

it('returns true when showInstallationStatus is true and installStatus is installed', () => {
expect(
shouldShowInstallationStatus({
installStatus: installationStatuses.Installed,
showInstallationStatus: true,
})
).toEqual(true);
});

it('returns true when showInstallationStatus is true and installStatus is installFailed', () => {
expect(
shouldShowInstallationStatus({
installStatus: installationStatuses.InstallFailed,
showInstallationStatus: true,
})
).toEqual(true);
});
});

describe('InstallationStatus', () => {
it('renders null when showInstallationStatus is false', () => {
const { container } = render(
<InstallationStatus
installStatus={installationStatuses.Installed}
showInstallationStatus={false}
/>
);
expect(container.firstChild).toBeNull();
});

it('renders the Installed status correctly', () => {
render(
<InstallationStatus
installStatus={installationStatuses.Installed}
showInstallationStatus={true}
/>
);
expect(screen.getByText('Installed')).toBeInTheDocument();
});

it('renders the Install Failed status correctly', () => {
render(
<InstallationStatus
installStatus={installationStatuses.InstallFailed}
showInstallationStatus={true}
/>
);
expect(screen.getByText('Installed')).toBeInTheDocument();
});

it('renders null when installStatus is null or undefined', () => {
const { container } = render(
<InstallationStatus installStatus={null} showInstallationStatus={true} />
);
expect(container.firstChild).toBeNull();

const { container: undefinedContainer } = render(
<InstallationStatus installStatus={undefined} showInstallationStatus={true} />
);
expect(undefinedContainer.firstChild).toBeNull();
});

it('applies the correct styles for the component', () => {
const { getByTestId } = render(
<InstallationStatus
installStatus={installationStatuses.Installed}
showInstallationStatus={true}
/>
);

const spacer = getByTestId('installation-status-spacer');
const callout = getByTestId('installation-status-callout');

expect(spacer).toHaveStyle('background: #FFFFFF');
expect(callout).toHaveStyle('padding: 8px 16px');
expect(callout).toHaveTextContent('Installed');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';

import { EuiCallOut, EuiSpacer, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { css } from '@emotion/react';

import { installationStatuses } from '../../../../../../common/constants';
import type { EpmPackageInstallStatus } from '../../../../../../common/types';

const installedLabel = i18n.translate('xpack.fleet.packageCard.installedLabel', {
defaultMessage: 'Installed',
});

const installStatusMapToColor: Readonly<
Record<string, { color: 'success' | 'warning'; iconType: string; title: string }>
> = {
installed: {
color: 'success',
iconType: 'check',
title: installedLabel,
},
install_failed: {
color: 'warning',
iconType: 'warning',
title: installedLabel,
},
};

interface InstallationStatusProps {
installStatus: EpmPackageInstallStatus | null | undefined;
showInstallationStatus?: boolean;
}

export const getLineClampStyles = (lineClamp?: number) =>
lineClamp
? `-webkit-line-clamp: ${lineClamp};display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;`
: '';

export const shouldShowInstallationStatus = ({
installStatus,
showInstallationStatus,
}: InstallationStatusProps) =>
showInstallationStatus &&
(installStatus === installationStatuses.Installed ||
installStatus === installationStatuses.InstallFailed);

export const InstallationStatus: React.FC<InstallationStatusProps> = React.memo(
({ installStatus, showInstallationStatus }) => {
const { euiTheme } = useEuiTheme();
return shouldShowInstallationStatus({ installStatus, showInstallationStatus }) ? (
<div
css={css`
position: absolute;
border-radius: 0 0 ${euiTheme.border.radius.medium} ${euiTheme.border.radius.medium};
bottom: 0;
left: 0;
width: 100%;
overflow: hidden;
`}
>
<EuiSpacer
data-test-subj="installation-status-spacer"
size="m"
css={css`
background: ${euiTheme.colors.emptyShade};
`}
/>
<EuiCallOut
data-test-subj="installation-status-callout"
css={css`
padding: ${euiTheme.size.s} ${euiTheme.size.m};
text-align: center;
`}
{...(installStatus ? installStatusMapToColor[installStatus] : {})}
/>
</div>
) : null;
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useStartServices } from '../../../hooks';

import type { PackageCardProps } from './package_card';
import { PackageCard } from './package_card';
import { getLineClampStyles, shouldShowInstallationStatus } from './installation_status';

jest.mock('../../../hooks', () => {
return {
Expand All @@ -38,6 +39,16 @@ jest.mock('../../../components', () => {
};
});

jest.mock('./installation_status', () => {
return {
shouldShowInstallationStatus: jest.fn(),
getLineClampStyles: jest.fn(),
InstallationStatus: () => {
return <div data-test-subj="installation-status" />;
},
};
});

function cardProps(overrides: Partial<PackageCardProps> = {}): PackageCardProps {
return {
id: 'card-1',
Expand All @@ -60,8 +71,12 @@ function renderPackageCard(props: PackageCardProps) {
describe('package card', () => {
let mockNavigateToApp: jest.Mock;
let mockNavigateToUrl: jest.Mock;
const mockGetLineClamp = getLineClampStyles as jest.Mock;
const mockShouldShowInstallationStatus = shouldShowInstallationStatus as jest.Mock;

beforeEach(() => {
jest.clearAllMocks();

mockNavigateToApp = useStartServices().application.navigateToApp as jest.Mock;
mockNavigateToUrl = useStartServices().application.navigateToUrl as jest.Mock;
});
Expand Down Expand Up @@ -136,4 +151,83 @@ describe('package card', () => {
expect(!!collectionButton).toEqual(isCollectionCard);
}
);

describe('Installation status', () => {
it('should render installation status when showInstallationStatus is true', async () => {
const {
utils: { queryByTestId },
} = renderPackageCard(
cardProps({
showInstallationStatus: true,
})
);
const installationStatus = queryByTestId('installation-status');
expect(installationStatus).toBeInTheDocument();
});

it('should render max-height when maxCardHeight is provided', async () => {
const {
utils: { queryByTestId },
} = renderPackageCard(
cardProps({
maxCardHeight: 150,
})
);
const card = queryByTestId(`integration-card:card-1`);
expect(card).toHaveStyle('max-height: 150px');
});

it('should render 1 line of description when descriptionLineClamp is provided and shouldShowInstallationStatus returns true', async () => {
mockShouldShowInstallationStatus.mockReturnValue(true);
renderPackageCard(
cardProps({
showInstallationStatus: true,
installStatus: 'installed',
descriptionLineClamp: 3,
})
);
expect(mockShouldShowInstallationStatus).toHaveBeenCalledWith({
installStatus: 'installed',
showInstallationStatus: true,
});
expect(mockGetLineClamp).toHaveBeenCalledWith(1);
});

it('should render specific lines of description when descriptionLineClamp is provided and shouldShowInstallationStatus returns false', async () => {
mockShouldShowInstallationStatus.mockReturnValue(false);
renderPackageCard(
cardProps({
showInstallationStatus: false,
installStatus: 'installed',
descriptionLineClamp: 3,
})
);
expect(mockShouldShowInstallationStatus).toHaveBeenCalledWith({
installStatus: 'installed',
showInstallationStatus: false,
});
expect(mockGetLineClamp).toHaveBeenCalledWith(3);
});

it('should not render line clamp when descriptionLineClamp is not provided', async () => {
mockShouldShowInstallationStatus.mockReturnValue(false);
renderPackageCard(
cardProps({
showInstallationStatus: true,
installStatus: 'installed',
})
);
expect(mockShouldShowInstallationStatus).not.toHaveBeenCalled();
});

it('should render specific lines of title when titleLineClamp is provided and shouldShowInstallationStatus returns false', async () => {
mockShouldShowInstallationStatus.mockReturnValue(false);
renderPackageCard(
cardProps({
titleLineClamp: 1,
})
);
expect(mockGetLineClamp).toHaveBeenCalledWith(1);
});
});
});
Loading