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

[Fleet] Integration Policies List view #83634

Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
81bfb6e
Show table with list of Integration polices
paul-tavares Nov 12, 2020
4609b59
Date format for updatedAt
paul-tavares Nov 12, 2020
1182f4e
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 16, 2020
c79b575
hook to support list pagination that uses/persist to the URL
paul-tavares Nov 16, 2020
a26d96f
Add pagination to list of integration polices
paul-tavares Nov 16, 2020
5a0c3c0
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 16, 2020
d153745
Initial code for useGetEnrichedPackagePolicies hook
paul-tavares Nov 16, 2020
f488243
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 17, 2020
c5194a0
Show Agent Policy Name and count of agents
paul-tavares Nov 17, 2020
2befd92
Fix bug in useUrlPagination()
paul-tavares Nov 17, 2020
296d4d8
LinkAndRevision component + use it in Package Policy List
paul-tavares Nov 17, 2020
b3a728a
Refactor Agent Policies page to use `<LinkAndRevision>` component
paul-tavares Nov 17, 2020
e8e152f
Show last updated by using a persona component
paul-tavares Nov 17, 2020
a779846
Fix for small screens
paul-tavares Nov 18, 2020
6db69c3
Fix bug in create integration after creating a new Agent Policy
paul-tavares Nov 18, 2020
86f6eac
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 18, 2020
0791a19
Fix type and remove todo
paul-tavares Nov 18, 2020
2c80f9b
Fix jsdoc
paul-tavares Nov 18, 2020
d2e75c0
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 18, 2020
ff7cce0
fix i18n
paul-tavares Nov 18, 2020
825f9c6
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 18, 2020
2d044c7
Change format for list of integration policies with agent policies
paul-tavares Nov 19, 2020
5696283
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 19, 2020
8668097
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 23, 2020
7af206c
Fix eslint error?
paul-tavares Nov 23, 2020
43d965b
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 23, 2020
e9e1d8b
Fix format issue
paul-tavares Nov 23, 2020
67e63ae
Merge remote-tracking branch 'upstream/master' into task/fleet-82485-…
paul-tavares Nov 23, 2020
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
Expand Up @@ -11,3 +11,4 @@ export { PackageIcon } from './package_icon';
export { ContextMenuActions } from './context_menu_actions';
export { SearchBar } from './search_bar';
export * from './settings_flyout';
export * from './link_and_revision';
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { CSSProperties, memo } from 'react';
import { EuiLinkProps } from '@elastic/eui/src/components/link/link';

const MIN_WIDTH: CSSProperties = { minWidth: 0 };
const NO_WRAP_WHITE_SPACE: CSSProperties = { whiteSpace: 'nowrap' };

export type LinkAndRevisionProps = EuiLinkProps & {
revision?: string | number;
};

/**
* Components shows a link for a given value along with a revision number to its right. The display
* value is truncated if it is longer than the width of where it is displayed, while the revision
* always remain visible
*/
export const LinkAndRevision = memo<LinkAndRevisionProps>(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI - I would like to add to this component a isOutdated: boolean prop and then refactor the Agents List view to also use this component. I have a feeling that the outdated indicator will also be wanted in other scenarios.

Copy link
Contributor

Choose a reason for hiding this comment

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

this would also be good for our Endpoint list

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes @kevinlog , we would likely have a similar component in our code base.

({ revision, className, ...euiLinkProps }) => {
return (
<EuiFlexGroup gutterSize="s" alignItems="baseline" style={MIN_WIDTH} responsive={false}>
<EuiFlexItem grow={false} className="eui-textTruncate">
<EuiLink className={`eui-textTruncate ${className ?? ''}`} {...euiLinkProps} />
</EuiFlexItem>
{revision && (
<EuiFlexItem grow={true}>
<EuiText color="subdued" size="xs" style={NO_WRAP_WHITE_SPACE}>
<FormattedMessage
id="xpack.fleet.policyNameLink.revisionNumber"
defaultMessage="rev. {revNumber}"
values={{ revNumber: revision }}
/>
</EuiText>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export { useBreadcrumbs } from './use_breadcrumbs';
export { useLink } from './use_link';
export { useKibanaLink } from './use_kibana_link';
export { usePackageIconType, UsePackageIconType } from './use_package_icon_type';
export { usePagination, Pagination } from './use_pagination';
export { usePagination, Pagination, PAGE_SIZE_OPTIONS } from './use_pagination';
export { useUrlPagination } from './use_url_pagination';
export { useSorting } from './use_sorting';
export { useDebounce } from './use_debounce';
export * from './use_request';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,27 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { useState } from 'react';
import { useMemo, useState } from 'react';

export const PAGE_SIZE_OPTIONS: readonly number[] = [5, 20, 50];

export interface Pagination {
currentPage: number;
pageSize: number;
}

export function usePagination() {
const [pagination, setPagination] = useState<Pagination>({
export function usePagination(
pageInfo: Pagination = {
currentPage: 1,
pageSize: 20,
});
}
) {
const [pagination, setPagination] = useState<Pagination>(pageInfo);
const pageSizeOptions = useMemo(() => [...PAGE_SIZE_OPTIONS], []);

return {
pagination,
setPagination,
pageSizeOptions: [5, 20, 50],
pageSizeOptions,
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { useCallback, useEffect, useMemo } from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { useUrlParams } from './use_url_params';
import { PAGE_SIZE_OPTIONS, Pagination, usePagination } from './use_pagination';

type SetUrlPagination = (pagination: Pagination) => void;
interface UrlPagination {
pagination: Pagination;
setPagination: SetUrlPagination;
pageSizeOptions: number[];
}

type UrlPaginationParams = Partial<Pagination>;

/**
* Uses URL params for pagination and also persists those to the URL as they are updated
*/
export const useUrlPagination = (): UrlPagination => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about changing the existing usePagination() to always persist the page/perPage to the URL, but that could have a large impact in testing to ensure it does not break anything. So for now, I went with a separate hook which could be merged and/or used if the use case calls for these parameters to be persisted in the URL.

const location = useLocation();
const history = useHistory();
const { urlParams, toUrlParams } = useUrlParams();
const urlPaginationParams = useMemo(() => {
return paginationFromUrlParams(urlParams);
}, [urlParams]);
const { pagination, pageSizeOptions, setPagination } = usePagination(urlPaginationParams);

const setUrlPagination = useCallback<SetUrlPagination>(
({ pageSize, currentPage }) => {
history.push({
...location,
search: toUrlParams({
...urlParams,
currentPage,
pageSize,
}),
});
},
[history, location, toUrlParams, urlParams]
);

useEffect(() => {
setPagination((prevState) => {
return {
...prevState,
...paginationFromUrlParams(urlParams),
};
});
}, [setPagination, urlParams]);

return {
pagination,
setPagination: setUrlPagination,
pageSizeOptions,
};
};

const paginationFromUrlParams = (urlParams: UrlPaginationParams): Pagination => {
const pagination: Pagination = {
pageSize: 20,
currentPage: 1,
};

// Search params can appear multiple times in the URL, in which case the value for them,
// once parsed, would be an array. In these case, we take the last value defined
pagination.currentPage = Number(
(Array.isArray(urlParams.currentPage)
? urlParams.currentPage[urlParams.currentPage.length - 1]
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe .pop() will give the same result. If so, I'd prefer it as it's less statements to read.

Suggested change
? urlParams.currentPage[urlParams.currentPage.length - 1]
? urlParams.currentPage.pop()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow - why did I not think of that 🤦
Thanks @jfsiii . going to merge this in, and will adjust this based on your comments in next PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

@paul-tavares no worries, It Depends ™️, right :) .pop() does alter the array. I didn't see it used again so seems it works for now, but there are trade-offs.

I really wish we could urlParams.currentPage[-1] like perl/python.

After writing this I went looking and found that we can't do arr[-2] but arr.at(-2) is coming eventually via .at() method. I don't see anything about it in any of the feature support sites but it is stage 3 now, so 🤞

: urlParams.currentPage) ?? pagination.currentPage
);
pagination.pageSize =
Number(
(Array.isArray(urlParams.pageSize)
? urlParams.pageSize[urlParams.pageSize.length - 1]
Copy link
Contributor

Choose a reason for hiding this comment

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

same question/comment re: .pop()

: urlParams.pageSize) ?? pagination.pageSize
) ?? pagination.pageSize;

// If Current Page is not a valid positive integer, set it to 1
if (!Number.isFinite(pagination.currentPage) || pagination.currentPage < 1) {
pagination.currentPage = 1;
}

// if pageSize is not one of the expected page sizes, reset it to 20 (default)
if (!PAGE_SIZE_OPTIONS.includes(pagination.pageSize)) {
pagination.pageSize = 20;
}

return pagination;
};
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
id="xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsDescriptionText"
defaultMessage="{count, plural, one {# agent} other {# agents}} are enrolled with the selected agent policy."
values={{
count: agentPoliciesById[selectedPolicyId].agents || 0,
count: agentPoliciesById[selectedPolicyId]?.agents ?? 0,
}}
/>
) : null
Expand Down Expand Up @@ -282,7 +282,7 @@ export const StepSelectAgentPolicy: React.FunctionComponent<{
id="xpack.fleet.createPackagePolicy.StepSelectPolicy.agentPolicyAgentsCountText"
defaultMessage="{count, plural, one {# agent} other {# agents}} enrolled"
values={{
count: agentPoliciesById[option.value!].agents || 0,
count: agentPoliciesById[option.value!]?.agents ?? 0,
}}
/>
</EuiTextColor>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import {
useUrlParams,
useBreadcrumbs,
} from '../../../hooks';
import { SearchBar } from '../../../components';
import { LinkAndRevision, SearchBar } from '../../../components';
import { LinkedAgentCount, AgentPolicyActionMenu } from '../components';
import { CreateAgentPolicyFlyout } from './components';

Expand Down Expand Up @@ -129,26 +129,13 @@ export const AgentPolicyListPage: React.FunctionComponent<{}> = () => {
}),
width: '20%',
render: (name: string, agentPolicy: AgentPolicy) => (
<EuiFlexGroup gutterSize="s" alignItems="baseline" style={{ minWidth: 0 }}>
<EuiFlexItem grow={false} className="eui-textTruncate">
<EuiLink
className="eui-textTruncate"
href={getHref('policy_details', { policyId: agentPolicy.id })}
title={name || agentPolicy.id}
>
{name || agentPolicy.id}
</EuiLink>
</EuiFlexItem>
<EuiFlexItem grow={true}>
<EuiText color="subdued" size="xs" style={{ whiteSpace: 'nowrap' }}>
<FormattedMessage
id="xpack.fleet.agentPolicyList.revisionNumber"
defaultMessage="rev. {revNumber}"
values={{ revNumber: agentPolicy.revision }}
/>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<LinkAndRevision
href={getHref('policy_details', { policyId: agentPolicy.id })}
title={name || agentPolicy.id}
revision={agentPolicy.revision}
>
{name || agentPolicy.id}
</LinkAndRevision>
),
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { SettingsPanel } from './settings_panel';

type ContentProps = PackageInfo & Pick<DetailParams, 'panel'>;

const SideNavColumn = styled(LeftColumn)`
const LeftSideColumn = styled(LeftColumn)`
/* 🤢🤷 https://www.styled-components.com/docs/faqs#how-can-i-override-styles-with-higher-specificity */
&&& {
margin-top: 77px;
Expand All @@ -30,15 +30,18 @@ const ContentFlexGroup = styled(EuiFlexGroup)`
`;

export function Content(props: ContentProps) {
const showRightColumn = props.panel !== 'policies';
return (
<ContentFlexGroup>
<SideNavColumn />
<CenterColumn>
<LeftSideColumn {...(!showRightColumn ? { columnGrow: 1 } : undefined)} />
<CenterColumn {...(!showRightColumn ? { columnGrow: 6 } : undefined)}>
<ContentPanel {...props} />
</CenterColumn>
<RightColumn>
<RightColumnContent {...props} />
</RightColumn>
{showRightColumn && (
<RightColumn>
<RightColumnContent {...props} />
</RightColumn>
)}
</ContentFlexGroup>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,8 +247,7 @@ export function Detail() {
return (entries(PanelDisplayNames)
.filter(([panelId]) => {
return (
panelId !== 'policies' ||
(packageInfoData?.response.status === InstallStatus.installed && false) // Remove `false` when ready to implement policies tab
panelId !== 'policies' || packageInfoData?.response.status === InstallStatus.installed
);
})
.map(([panelId, display]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,45 @@

import { EuiFlexItem } from '@elastic/eui';
import React, { FunctionComponent, ReactNode } from 'react';
import { FlexItemGrowSize } from '@elastic/eui/src/components/flex/flex_item';

interface ColumnProps {
children?: ReactNode;
className?: string;
columnGrow?: FlexItemGrowSize;
}

export const LeftColumn: FunctionComponent<ColumnProps> = ({ children, ...rest }) => {
export const LeftColumn: FunctionComponent<ColumnProps> = ({
columnGrow = 2,
children,
...rest
}) => {
return (
<EuiFlexItem grow={2} {...rest}>
<EuiFlexItem grow={columnGrow} {...rest}>
{children}
</EuiFlexItem>
);
};

export const CenterColumn: FunctionComponent<ColumnProps> = ({ children, ...rest }) => {
export const CenterColumn: FunctionComponent<ColumnProps> = ({
columnGrow = 9,
children,
...rest
}) => {
return (
<EuiFlexItem grow={9} {...rest}>
<EuiFlexItem grow={columnGrow} {...rest}>
{children}
</EuiFlexItem>
);
};

export const RightColumn: FunctionComponent<ColumnProps> = ({ children, ...rest }) => {
export const RightColumn: FunctionComponent<ColumnProps> = ({
columnGrow = 3,
children,
...rest
}) => {
return (
<EuiFlexItem grow={3} {...rest}>
<EuiFlexItem grow={columnGrow} {...rest}>
{children}
</EuiFlexItem>
);
Expand Down
Loading