Skip to content

Commit

Permalink
Add pins
Browse files Browse the repository at this point in the history
Test add/persist/remove pin
  • Loading branch information
jennypavlova committed Apr 26, 2023
1 parent f1fe2c4 commit ce499af
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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, { Dispatch } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiToolTip, EuiButtonIcon } from '@elastic/eui';
import type { Field } from './utils';

interface AddMetadataPinToRowProps {
fieldName: Field['name'];
pinnedItems: Array<Field['name']>;
setPinnedItems: Dispatch<React.SetStateAction<Array<Field['name']> | undefined>>;
}

const PIN_FIELD = i18n.translate('xpack.infra.hostsViewPage.flyout.metadata.pinField', {
defaultMessage: 'Pin Field',
});

export const AddMetadataPinToRow = ({
fieldName,
pinnedItems,
setPinnedItems,
}: AddMetadataPinToRowProps) => {
const handleAddPin = (pin: Field['name']) => {
setPinnedItems([...pinnedItems, pin]);
};

const handleRemovePin = (pin: Field['name']) => {
if (pinnedItems && pinnedItems.includes(pin)) {
setPinnedItems((pinnedItems ?? []).filter((pinName: string) => pin !== pinName));
}
};

if (pinnedItems?.includes(fieldName)) {
return (
<span>
<EuiToolTip
content={i18n.translate('xpack.infra.hostsViewPage.flyout.metadata.unpinField', {
defaultMessage: 'Unpin field',
})}
>
<EuiButtonIcon
size="s"
color="primary"
iconType="pinFilled"
data-test-subj="hostsView-flyout-metadata-remove-pin"
aria-label={i18n.translate('xpack.infra.hostsViewPage.flyout.metadata.pinAriaLabel', {
defaultMessage: 'Pinned field',
})}
onClick={() => handleRemovePin(fieldName)}
/>
</EuiToolTip>
</span>
);
}

return (
<span className="euiTableCellContent__hoverItem expandedItemActions__completelyHide">
<EuiToolTip content={PIN_FIELD}>
<EuiButtonIcon
color="primary"
size="s"
iconType="pin"
data-test-subj="hostsView-flyout-metadata-add-pin"
aria-label={PIN_FIELD}
onClick={() => handleAddPin(fieldName)}
/>
</EuiToolTip>
</span>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,25 @@ import {
EuiLink,
EuiInMemoryTable,
EuiSearchBarProps,
EuiIcon,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useCallback, useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import useToggle from 'react-use/lib/useToggle';
import { debounce } from 'lodash';
import { Query } from '@elastic/eui';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import type { HorizontalAlignment } from '@elastic/eui';
import { useHostFlyoutOpen } from '../../../hooks/use_host_flyout_open_url_state';
import { AddMetadataFilterButton } from './add_metadata_filter_button';

interface Row {
name: string;
value: string | string[] | undefined;
}
import { AddMetadataPinToRow } from './add_pin_to_row';
import { LOCAL_STORAGE_PINNED_METADATA_ROWS } from '../../../constants';
import { getRowsWithPins } from './utils';
import type { Field } from './utils';

interface Props {
rows: Row[];
rows: Field[];
loading: boolean;
}

Expand Down Expand Up @@ -72,6 +74,18 @@ export const Table = (props: Props) => {
const { rows, loading } = props;
const [searchError, setSearchError] = useState<SearchErrorType | null>(null);
const [hostFlyoutOpen, setHostFlyoutOpen] = useHostFlyoutOpen();
const [fieldsWithPins, setFieldsWithPins] = useState(rows);

const [pinnedItems, setPinnedItems] = useLocalStorage<Array<Field['name']>>(
LOCAL_STORAGE_PINNED_METADATA_ROWS,
[]
);

useMemo(() => {
if (pinnedItems) {
setFieldsWithPins(getRowsWithPins(rows, pinnedItems) ?? rows);
}
}, [rows, pinnedItems]);

const debouncedSearchOnChange = useMemo(
() =>
Expand Down Expand Up @@ -108,6 +122,23 @@ export const Table = (props: Props) => {

const columns = useMemo(
() => [
{
field: 'value',
name: <EuiIcon type="pin" />,
align: 'center' as HorizontalAlignment,
width: '5%',
sortable: false,
showOnHover: true,
render: (_name: string, item: Field) => {
return (
<AddMetadataPinToRow
fieldName={item.name}
pinnedItems={pinnedItems ?? []}
setPinnedItems={setPinnedItems}
/>
);
},
},
{
field: 'name',
name: FIELD_LABEL,
Expand All @@ -118,21 +149,22 @@ export const Table = (props: Props) => {
{
field: 'value',
name: VALUE_LABEL,
width: '55%',
width: '50%',
sortable: false,
render: (_name: string, item: Row) => <ExpandableContent values={item.value} />,
render: (_name: string, item: Field) => <ExpandableContent values={item.value} />,
},
{
field: 'value',
name: 'Actions',
width: '10%',
sortable: false,
showOnHover: true,
render: (_name: string, item: Row) => {
render: (_name: string, item: Field) => {
return <AddMetadataFilterButton item={item} />;
},
},
],
[]
[pinnedItems, setPinnedItems]
);

return (
Expand All @@ -141,7 +173,7 @@ export const Table = (props: Props) => {
tableLayout={'fixed'}
responsive={false}
columns={columns}
items={rows}
items={fieldsWithPins}
rowProps={{ className: 'euiTableRow-hasActions' }}
search={search}
loading={loading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

import type { InfraMetadata } from '../../../../../../../common/http_api';

export interface Field {
name: string;
value: string | string[] | undefined;
}

export const getAllFields = (metadata: InfraMetadata | null) => {
if (!metadata?.info) return [];
return prune([
Expand Down Expand Up @@ -105,5 +110,17 @@ export const getAllFields = (metadata: InfraMetadata | null) => {
]);
};

const prune = (fields: Array<{ name: string; value: string | string[] | undefined }>) =>
fields.filter((f) => !!f.value);
const prune = (fields: Field[]) => fields.filter((f) => !!f.value);

export const getRowsWithPins = (rows: Field[], pinnedItems: Array<Field['name']>) => {
if (pinnedItems.length > 0) {
const { pinned, other } = rows.reduce(
(acc, row) => {
(pinnedItems.includes(row.name) ? acc.pinned : acc.other).push(row);
return acc;
},
{ pinned: [] as Field[], other: [] as Field[] }
);
return [...pinned, ...other];
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const DEFAULT_HOST_LIMIT: HostLimitOptions = 100;
export const DEFAULT_PAGE_SIZE = 10;
export const LOCAL_STORAGE_HOST_LIMIT_KEY = 'hostsView:hostLimitSelection';
export const LOCAL_STORAGE_PAGE_SIZE_KEY = 'hostsView:pageSizeSelection';
export const LOCAL_STORAGE_PINNED_METADATA_ROWS = 'hostsView:pinnedMetadataRows';

export const ALL_ALERTS: AlertStatusFilter = {
status: ALERT_STATUS_ALL,
Expand Down
20 changes: 19 additions & 1 deletion x-pack/test/functional/apps/infra/hosts_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
// Tests

// Failing: See https://github.com/elastic/kibana/issues/155429
describe.skip('Hosts View', function () {
describe('Hosts View', function () {
this.tags('includeFirefox');

before(async () => {
Expand Down Expand Up @@ -260,6 +260,24 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => {
expect(removeFilterShouldNotExist).to.be(false);
});

it('should render metadata tab, pin and unpin table row', async () => {
const metadataTab = await pageObjects.infraHostsView.getMetadataTabName();
expect(metadataTab).to.contain('Metadata');

// Add Pin
await pageObjects.infraHostsView.clickAddMetadataPin();
expect(await pageObjects.infraHostsView.getRemovePinExist()).to.be(true);

// Persist pin after refresh
await browser.refresh();
await pageObjects.infraHome.waitForLoading();
expect(await pageObjects.infraHostsView.getRemovePinExist()).to.be(true);

// Remove Pin
await pageObjects.infraHostsView.clickRemoveMetadataPin();
expect(await pageObjects.infraHostsView.getRemovePinExist()).to.be(false);
});

it('should navigate to Uptime after click', async () => {
await pageObjects.infraHostsView.clickFlyoutUptimeLink();
await pageObjects.infraHome.waitForLoading();
Expand Down
15 changes: 14 additions & 1 deletion x-pack/test/functional/page_objects/infra_hosts_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) {
return testSubjects.click('hostsView-flyout-metadata-remove-filter');
},

async clickAddMetadataPin() {
return testSubjects.click('hostsView-flyout-metadata-add-pin');
},

async clickRemoveMetadataPin() {
return testSubjects.click('hostsView-flyout-metadata-remove-pin');
},

async getHostsLandingPageDisabled() {
const container = await testSubjects.find('hostView-no-enable-access');
const containerText = await container.getVisibleText();
Expand Down Expand Up @@ -139,7 +147,7 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) {
return testSubjects.existOrFail('embeddablePanelAction-openInLens');
},

// Flyout Tabs
// Flyout Tabs: Metadata
getMetadataTab() {
return testSubjects.find('hostsView-flyout-tabs-metadata');
},
Expand All @@ -161,6 +169,11 @@ export function InfraHostsViewProvider({ getService }: FtrProviderContext) {
return testSubjects.exists('hostsView-flyout-metadata-remove-filter');
},

async getRemovePinExist() {
return testSubjects.exists('hostsView-flyout-metadata-remove-pin');
},

// Flyout Tabs: Processes
async getProcessesTabContentTitle(index: number) {
const processesListElements = await testSubjects.findAll('infraProcessesSummaryTableItem');
return processesListElements[index].findByCssSelector('dt');
Expand Down

0 comments on commit ce499af

Please sign in to comment.