Skip to content

Commit

Permalink
side panel styling (#214)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpinsonneau authored Nov 15, 2022
1 parent e72a0b7 commit dce1518
Show file tree
Hide file tree
Showing 8 changed files with 402 additions and 149 deletions.
12 changes: 7 additions & 5 deletions web/locales/en/plugin__netobserv-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,21 @@
"Egress": "Egress",
"Remove {{name}} filter": "Remove {{name}} filter",
"Filter on {{name}}": "Filter on {{name}}",
"Flow Details": "Flow Details",
"Flow information": "Flow information",
"Details": "Details",
"Raw": "Raw",
"JSON": "JSON",
"Kind not managed": "Kind not managed",
"Unable to get flows": "Unable to get flows",
"Name": "Name",
"Node Name": "Node Name",
"IP": "IP",
"No information available for this content. Change scope to get more details.": "No information available for this content. Change scope to get more details.",
"Source to destination:": "Source to destination:",
"In:": "In:",
"Destination to source:": "Destination to source:",
"Out:": "Out:",
"Both:": "Both:",
"Infos": "Infos",
"{{type}} rate": "{{type}} rate",
"Edge": "Edge",
"Unknown": "Unknown",
Expand All @@ -169,10 +173,9 @@
"Badges are hidden": "Badges are hidden",
"Truncate labels": "Truncate labels",
"Kind": "Kind",
"Name": "Name",
"Query is slow": "Query is slow",
"Overview": "Overview",
"Flow Table": "Flow Table",
"Traffic flows": "Traffic flows",
"Topology": "Topology",
"Collapse": "Collapse",
"Expand": "Expand",
Expand Down Expand Up @@ -212,7 +215,6 @@
"Ports": "Ports",
"MAC": "MAC",
"Node IP": "Node IP",
"Node Name": "Node Name",
"Kubernetes Objects": "Kubernetes Objects",
"Owner Kubernetes Objects": "Owner Kubernetes Objects",
"IPs & Ports": "IPs & Ports",
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/metrics/metrics-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const MetricsContent: React.FC<MetricsContentProps> = ({
return (
<TextContent id="metrics" className="metrics-content-div">
{showTitle && (
<Text id="metrics-title" component={TextVariants.h3}>
<Text id="metrics-title" component={TextVariants.h4}>
{title}
</Text>
)}
Expand Down
63 changes: 60 additions & 3 deletions web/src/components/netflow-record/record-panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,66 @@
}

.record-field-container.grouped {
padding-left: 15px;
padding-left: 2em;
}

.record-group-container {
margin-bottom: 15px;
.borderless-accordion>.pf-c-accordion__expanded-content-body::after,
.borderless-accordion::after {
display: none;
}

.borderless-accordion>.pf-c-accordion__toggle-text {
padding-left: 0.8em;
font-size: 14px;
font-weight: 400 !important;
line-height: 1.5;
}

.borderless-accordion>.pf-c-accordion__toggle-icon {
position: absolute;
left: 0;
}

button.borderless-accordion {
padding-top: 1em;
padding-bottom: 1em;
}

.borderless-accordion>.pf-c-accordion__expanded-content-body,
.drawer-panel-content>.pf-c-drawer__panel-main>.pf-c-drawer__body {
padding: 0 !important;
}

#drawer-tabs {
padding-left: 0;
}

.drawer-tab>button {
font-size: 15px;
}

.drawer-panel-content>.pf-c-drawer__panel-main {
overflow: hidden;
}

.drawer-head {
font-size: 1.25rem !important;
margin: 0 !important;
padding: 1em !important;
align-items: baseline;
}

.drawer-body {
display: flex;
flex-direction: column;
}

.drawer-tab {
flex: 1;
overflow-y: auto;
}

#pf-tab-section-details-drawer-tabs,
#pf-tab-section-raw-drawer-tabs {
padding: var(--pf-c-drawer--child--PaddingTop) var(--pf-c-drawer--child--PaddingRight) var(--pf-c-drawer--child--PaddingBottom) var(--pf-c-drawer--child--PaddingLeft);
}
129 changes: 100 additions & 29 deletions web/src/components/netflow-record/record-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import {
Accordion,
AccordionContent,
AccordionItem,
AccordionToggle,
ClipboardCopy,
ClipboardCopyVariant,
Divider,
DrawerActions,
DrawerCloseButton,
DrawerHead,
DrawerPanelBody,
DrawerPanelContent,
Tab,
Tabs,
TabTitleText,
Text,
TextContent,
TextVariants
Expand All @@ -16,7 +24,7 @@ import { useTranslation } from 'react-i18next';
import { defaultSize, maxSize, minSize } from '../../utils/panel';
import { defaultTimeRange, flowdirToReporter } from '../../utils/router';
import { Record } from '../../api/ipfix';
import { Column, ColumnsId, getColumnGroups } from '../../utils/columns';
import { Column, ColumnGroup, ColumnsId, getColumnGroups } from '../../utils/columns';
import { TimeRange } from '../../utils/datetime';
import { doesIncludeFilter, Filter, findFromFilters, removeFromFilters } from '../../model/filters';
import { findFilter } from '../../utils/filter-definitions';
Expand All @@ -25,7 +33,7 @@ import { Reporter } from '../../model/flow-query';
import './record-panel.css';

export type RecordDrawerProps = {
record?: Record;
record: Record;
columns: Column[];
filters: Filter[];
range: number | TimeRange;
Expand All @@ -50,6 +58,18 @@ export const RecordPanel: React.FC<RecordDrawerProps> = ({
onClose
}) => {
const { t } = useTranslation('plugin__netobserv-plugin');
const [hidden, setHidden] = React.useState<string[]>([]);
const [activeTab, setActiveTab] = React.useState<string>('details');

const toggle = React.useCallback(
(id: string) => {
const index = hidden.indexOf(id);
const newExpanded: string[] =
index >= 0 ? [...hidden.slice(0, index), ...hidden.slice(index + 1, hidden.length)] : [...hidden, id];
setHidden(newExpanded);
},
[hidden]
);

const getFilter = (col: Column) => {
if (record) {
Expand Down Expand Up @@ -133,6 +153,42 @@ export const RecordPanel: React.FC<RecordDrawerProps> = ({
[t, filters, setFilters]
);

const getGroup = React.useCallback(
(g: ColumnGroup, i: number, content: React.ReactElement) => {
const toggleId = `toggle-${i}`;
const key = `group-${i}`;
return g.title ? (
<div className="record-group-container" key={key} data-test-id={key}>
<Divider />
<AccordionItem data-test-id={key}>
{
<AccordionToggle
className="borderless-accordion"
onClick={() => toggle(toggleId)}
isExpanded={!hidden.includes(toggleId)}
id={toggleId}
>
{g.title}
</AccordionToggle>
}
<AccordionContent
className="borderless-accordion"
id={toggleId + '-content'}
isHidden={hidden.includes(toggleId)}
>
{content}
</AccordionContent>
</AccordionItem>
</div>
) : (
<div className="record-group-container" key={key} data-test-id={key}>
{content}
</div>
);
},
[hidden, toggle]
);

const groups = getColumnGroups(
columns.filter(
c =>
Expand All @@ -157,43 +213,58 @@ export const RecordPanel: React.FC<RecordDrawerProps> = ({
);
return (
<DrawerPanelContent
data-test={id}
data-test-id={id}
id={id}
className="drawer-panel-content"
isResizable
defaultSize={defaultSize}
minSize={minSize}
maxSize={maxSize}
>
<DrawerHead data-test="drawer-head">
<Text data-test="drawer-head-text" component={TextVariants.h2}>
{t('Flow Details')}
<DrawerHead id={`${id}-drawer-head`} data-test-id="drawer-head" className="drawer-head">
<Text data-test-id="drawer-head-text" component={TextVariants.h2}>
{t('Flow information')}
</Text>
<DrawerActions>
<DrawerCloseButton data-test="drawer-close-button" onClick={onClose} />
<DrawerCloseButton data-test-id="drawer-close-button" className="drawer-close-button" onClick={onClose} />
</DrawerActions>
</DrawerHead>
<DrawerPanelBody data-test="drawer-body">
{record && (
<>
{groups.map((g, i) => (
<div className="record-group-container" key={`group-${i}`} data-test={`drawer-group-${g.title}`}>
{g.title && <Text component={TextVariants.h3}>{g.title}</Text>}
{g.columns.map(c => (
<TextContent
className={`record-field-container ${g.title ? 'grouped' : ''}`}
key={c.id}
data-test={`drawer-field-${c.id}`}
>
<Text component={TextVariants.h4}>{c.name}</Text>
<RecordField flow={record} column={c} filter={getFilter(c)} size={'s'} useLinks={true} />
</TextContent>
))}
</div>
))}
<TextContent className="record-field-container" data-test="drawer-json-container">
<Divider />
<DrawerPanelBody id={`${id}-drawer-body`} className="drawer-body" data-test-id="drawer-body">
<Tabs
id="drawer-tabs"
activeKey={activeTab}
usePageInsets
onSelect={(e, key) => setActiveTab(key as string)}
role="region"
>
<Tab className="drawer-tab" eventKey={'details'} title={<TabTitleText>{t('Details')}</TabTitleText>}>
<Accordion asDefinitionList={false}>
{groups.map((g, i) =>
getGroup(
g,
i,
<div className="record-group-container">
{g.columns.map(c => (
<TextContent
className={`record-field-container ${g.title ? 'grouped' : ''}`}
key={c.id}
data-test-id={`drawer-field-${c.id}`}
>
<Text component={TextVariants.h4}>{c.name}</Text>
<RecordField flow={record} column={c} filter={getFilter(c)} size={'s'} useLinks={true} />
</TextContent>
))}
</div>
)
)}
</Accordion>
</Tab>
<Tab className="drawer-tab" eventKey={'raw'} title={<TabTitleText>{t('Raw')}</TabTitleText>}>
<TextContent className="record-field-container" data-test-id="drawer-json-container">
<Text component={TextVariants.h4}>{t('JSON')}</Text>
<ClipboardCopy
data-test="drawer-json-copy"
data-test-id="drawer-json-copy"
isCode
isReadOnly
isExpanded
Expand All @@ -204,8 +275,8 @@ export const RecordPanel: React.FC<RecordDrawerProps> = ({
{JSON.stringify(record, null, 2)}
</ClipboardCopy>
</TextContent>
</>
)}
</Tab>
</Tabs>
</DrawerPanelBody>
</DrawerPanelContent>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as React from 'react';
import { Filter } from '../../../model/filters';
import { TopologyMetrics } from '../../../api/loki';
import { MetricFunction, MetricScope, MetricType } from '../../../model/flow-query';
import { ElementPanel, ElementPanelContent } from '../element-panel';
import { ElementPanel, ElementPanelDetailsContent, ElementPanelMetricsContent } from '../element-panel';
import { dataSample } from '../__tests-data__/metrics';
import { NodeData } from '../../../model/topology';

Expand Down Expand Up @@ -54,27 +54,37 @@ describe('<ElementPanel />', () => {
expect(mocks.onClose).toHaveBeenCalled();
});

it('should render content', async () => {
const wrapper = mount(<ElementPanelContent {...mocks} />);
expect(wrapper.find(ElementPanelContent)).toBeTruthy();
it('should render <ElementPanelDetailsContent />', async () => {
const wrapper = mount(<ElementPanelDetailsContent {...mocks} />);
expect(wrapper.find(ElementPanelDetailsContent)).toBeTruthy();

//check node infos
expect(wrapper.find('#addressValue').last().text()).toBe('10.129.0.15');

//update to edge
wrapper.setProps({ ...mocks, element: getEdge() });
expect(wrapper.find('#source-content').last().text()).toBe('PodIP10.131.0.18');
expect(wrapper.find('#destination-content').last().text()).toBe('ServiceIP172.30.0.10');
});

it('should render <ElementPanelMetricsContent />', async () => {
const wrapper = mount(<ElementPanelMetricsContent {...mocks} />);
expect(wrapper.find(ElementPanelMetricsContent)).toBeTruthy();

//check node metrics
expect(wrapper.find('#inCount').last().text()).toBe('94.7 MB');
expect(wrapper.find('#outCount').last().text()).toBe('4.1 MB');
expect(wrapper.find('#total').last().text()).toBe('98.8 MB');

//update to edge
wrapper.setProps({ ...mocks, element: getEdge() });
expect(wrapper.find('#source').last().text()).toBe('SourcePodIP10.131.0.18');
expect(wrapper.find('#destination').last().text()).toBe('DestinationServiceIP172.30.0.10');
expect(wrapper.find('#inCount').last().text()).toBe('1.1 MB');
expect(wrapper.find('#outCount').last().text()).toBe('4.5 MB');
expect(wrapper.find('#total').last().text()).toBe('5.6 MB');
});

it('should filter content', async () => {
const wrapper = mount(<ElementPanelContent {...mocks} />);
it('should filter <ElementPanelDetailsContent />', async () => {
const wrapper = mount(<ElementPanelDetailsContent {...mocks} />);
const filterButtons = wrapper.find(Button);
// Two buttons: first for pod filter, second for IP filter
filterButtons.last().simulate('click');
Expand Down
11 changes: 11 additions & 0 deletions web/src/components/netflow-topology/element-panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@
padding-left: 15px;
}

#source-content>.pf-c-accordion__expanded-content-body,
#destination-content>.pf-c-accordion__expanded-content-body {
padding-left: 2.2em !important;
margin-bottom: 15px;
}

.element-text {
margin-top: 0 !important;
}

.element-metrics-container {
height: 400px;
}

#pf-tab-section-details-drawer-tabs,
#pf-tab-section-metrics-drawer-tabs {
padding: var(--pf-c-drawer--child--PaddingTop) var(--pf-c-drawer--child--PaddingRight) var(--pf-c-drawer--child--PaddingBottom) var(--pf-c-drawer--child--PaddingLeft);
}
Loading

0 comments on commit dce1518

Please sign in to comment.