Skip to content

Commit

Permalink
Add ability to hide all groups in topology
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Jul 31, 2020
1 parent 569779f commit a03ab5e
Show file tree
Hide file tree
Showing 22 changed files with 415 additions and 98 deletions.
10 changes: 5 additions & 5 deletions frontend/package.json
Expand Up @@ -88,13 +88,13 @@
"dependencies": {
"@fortawesome/fontawesome-free": "^5.9.0",
"@patternfly/patternfly": "4.23.3",
"@patternfly/react-catalog-view-extension": "4.5.1",
"@patternfly/react-catalog-view-extension": "4.6.2",
"@patternfly/react-charts": "6.6.0",
"@patternfly/react-core": "4.32.1",
"@patternfly/react-table": "4.12.1",
"@patternfly/react-core": "4.34.2",
"@patternfly/react-table": "4.13.16",
"@patternfly/react-tokens": "4.6.0",
"@patternfly/react-topology": "4.4.30",
"@patternfly/react-virtualized-extension": "4.5.20",
"@patternfly/react-topology": "4.4.51",
"@patternfly/react-virtualized-extension": "4.5.39",
"abort-controller": "3.0.0",
"ajv": "^6.12.3",
"apollo-cache-inmemory": "^1.6.5",
Expand Down
Expand Up @@ -89,7 +89,7 @@ const QuickStartTaskHeader: React.FC<QuickStartTaskHeaderProps> = ({
}
step={taskIndex}
onNavItemClick={onTaskSelect}
navItemComponent="Button"
navItemComponent="button"
/>
</div>
);
Expand Down
Expand Up @@ -7,6 +7,7 @@ import { PROMETHEUS_TENANCY_BASE_PATH } from '@console/internal/components/graph
import { TopologyDataResources, TrafficData } from './topology-types';
import ModelContext, { ExtensibleModel } from './data-transforms/ModelContext';
import { baseDataModelGetter } from './data-transforms';
import { getFilterById, useDisplayFilters, SHOW_GROUPS_FILTER_ID } from './filters';

export interface RenderProps {
showGraphView: boolean;
Expand Down Expand Up @@ -38,6 +39,8 @@ export const TopologyDataRenderer: React.FC<TopologyDataRendererProps> = ({
const dataModelContext = React.useContext<ExtensibleModel>(ModelContext);
const [model, setModel] = React.useState<Model>(null);
const [loadError, setLoadError] = React.useState<string>(null);
const filters = useDisplayFilters();
const showGroups = getFilterById(SHOW_GROUPS_FILTER_ID, filters)?.value ?? true;

const url = PROMETHEUS_TENANCY_BASE_PATH
? `${PROMETHEUS_TENANCY_BASE_PATH}/api/v1/rules?namespace=${namespace}`
Expand Down Expand Up @@ -96,14 +99,14 @@ export const TopologyDataRenderer: React.FC<TopologyDataRendererProps> = ({
dataModelContext.namespace,
resources,
workloadResources,
depicters,
showGroups ? depicters : [],
trafficData,
monitoringAlerts,
),
);
})
.catch(() => {});
}, [resources, trafficData, dataModelContext, kindsInFlight, monitoringAlerts]);
}, [resources, trafficData, dataModelContext, kindsInFlight, monitoringAlerts, showGroups]);

return render({
loaded: !!model,
Expand Down
Expand Up @@ -22,6 +22,7 @@ import {
getFilterById,
useDisplayFilters,
useSearchFilter,
useAllowEdgeCreation,
SHOW_LABELS_FILTER_ID,
} from '../../filters';
import { NodeShadows, NODE_SHADOW_FILTER_ID_HOVER, NODE_SHADOW_FILTER_ID } from '../NodeShadows';
Expand Down Expand Up @@ -89,16 +90,17 @@ const ObservedBaseNode: React.FC<BaseNodeProps> = ({
const showLabelsFilter = getFilterById(SHOW_LABELS_FILTER_ID, displayFilters);
const showLabels = showLabelsFilter?.value || hover;
const refs = useCombineRefs<SVGEllipseElement>(hoverRef, dragNodeRef);
const allowEdgeCreation = useAllowEdgeCreation();

React.useLayoutEffect(() => {
if (editAccess) {
if (editAccess && allowEdgeCreation) {
if (hover) {
onShowCreateConnector && onShowCreateConnector();
} else {
onHideCreateConnector && onHideCreateConnector();
}
}
}, [hover, onShowCreateConnector, onHideCreateConnector, editAccess]);
}, [hover, onShowCreateConnector, onHideCreateConnector, editAccess, allowEdgeCreation]);

return (
<g
Expand Down
Expand Up @@ -6,7 +6,7 @@ import {
TEST_KINDS_MAP,
} from '../../__tests__/topology-test-data';
import { updateModelFromFilters } from '../updateModelFromFilters';
import { EXPAND_GROUPS_FILTER_ID, getFilterById } from '../../filters';
import { EXPAND_GROUPS_FILTER_ID, getFilterById, SHOW_GROUPS_FILTER_ID } from '../../filters';
import { DEFAULT_TOPOLOGY_FILTERS, EXPAND_APPLICATION_GROUPS_FILTER_ID } from '../../filters/const';
import { ALL_APPLICATIONS_KEY } from '@console/shared/src';
import { baseDataModelGetter } from '../data-transformer';
Expand Down Expand Up @@ -79,6 +79,7 @@ describe('topology model ', () => {
it('should flag application groups as collapsed when expand groups is false', () => {
const topologyTransformedData = getTransformedTopologyData();
getFilterById(EXPAND_APPLICATION_GROUPS_FILTER_ID, filters).value = true;
getFilterById(SHOW_GROUPS_FILTER_ID, filters).value = true;
getFilterById(EXPAND_GROUPS_FILTER_ID, filters).value = false;
const newModel = updateModelFromFilters(
topologyTransformedData,
Expand All @@ -89,4 +90,32 @@ describe('topology model ', () => {
expect(newModel.nodes.filter((n) => n.group).length).toBe(2);
expect(newModel.nodes.filter((n) => n.group && n.collapsed).length).toBe(2);
});

it('should show no groups when show groups is false', () => {
const topologyTransformedData = getTransformedTopologyData();
getFilterById(EXPAND_GROUPS_FILTER_ID, filters).value = true;
getFilterById(SHOW_GROUPS_FILTER_ID, filters).value = false;
const newModel = updateModelFromFilters(
topologyTransformedData,
filters,
ALL_APPLICATIONS_KEY,
filterers,
);
expect(newModel.nodes.filter((n) => !n.group).length).toBe(10);
expect(newModel.nodes.filter((n) => n.group).length).toBe(0);
});

it('should show all nodes when is false and expand groups is false', () => {
const topologyTransformedData = getTransformedTopologyData();
getFilterById(EXPAND_GROUPS_FILTER_ID, filters).value = false;
getFilterById(SHOW_GROUPS_FILTER_ID, filters).value = false;
const newModel = updateModelFromFilters(
topologyTransformedData,
filters,
ALL_APPLICATIONS_KEY,
filterers,
);
expect(newModel.nodes.filter((n) => !n.group).length).toBe(10);
expect(newModel.nodes.filter((n) => n.group).length).toBe(0);
});
});
Expand Up @@ -96,36 +96,50 @@ const getBaseTopologyDataModel = (
return baseDataModel;
};

export const baseDataModelGetter = (
model: Model,
namespace: string,
resources: TopologyDataResources,
workloadResources: K8sResourceKind[],
dataModelDepicters?: TopologyDataModelDepicted[],
trafficData?: TrafficData,
monitoringAlerts?: Alerts,
): Model => {
const res = { ...resources, monitoringAlerts };
const baseModel = getBaseTopologyDataModel(res);
addToTopologyDataModel(baseModel, model, dataModelDepicters);

const updateAppGroupChildren = (model: Model) => {
model.nodes.forEach((n) => {
if (n.type === TYPE_APPLICATION_GROUP) {
// Filter out any children removed by depicters
n.children = n.children.filter((id) => model.nodes.find((child) => child.id === id));
n.data.groupResources = n.children?.map((id) => model.nodes.find((c) => id === c.id)) ?? [];
}
});
};

const createVisualConnectors = (model: Model, workloadResources: K8sResourceKind[]) => {
// Create all visual connectors
workloadResources.forEach((dc) => {
model.edges.push(...getTopologyEdgeItems(dc, workloadResources));
});
};

const createTrafficConnectors = (
model: Model,
workloadResources: K8sResourceKind[],
trafficData?: TrafficData,
) => {
// Create traffic connectors
if (trafficData) {
model.edges.push(...getTrafficConnectors(trafficData, workloadResources));
}
};

export const baseDataModelGetter = (
model: Model,
namespace: string,
resources: TopologyDataResources,
workloadResources: K8sResourceKind[],
dataModelDepicters?: TopologyDataModelDepicted[],
trafficData?: TrafficData,
monitoringAlerts?: Alerts,
): Model => {
const res = { ...resources, monitoringAlerts };
const baseModel = getBaseTopologyDataModel(res);
addToTopologyDataModel(baseModel, model, dataModelDepicters);

updateAppGroupChildren(model);
createVisualConnectors(model, workloadResources);
createTrafficConnectors(model, workloadResources, trafficData);

return model;
};
Expand Up @@ -4,6 +4,8 @@ import {
EXPAND_APPLICATION_GROUPS_FILTER_ID,
DEFAULT_SUPPORTED_FILTER_IDS,
isExpanded,
SHOW_GROUPS_FILTER_ID,
getFilterById,
} from '../filters';
import { TYPE_APPLICATION_GROUP, TYPE_AGGREGATE_EDGE } from '../components/const';
import { TopologyApplyDisplayOptions, DisplayFilters } from '../topology-types';
Expand Down Expand Up @@ -36,6 +38,7 @@ export const updateModelFromFilters = (
const supportedFilters = [...DEFAULT_SUPPORTED_FILTER_IDS];
let appGroupFound = false;
const expanded = isExpanded(EXPAND_APPLICATION_GROUPS_FILTER_ID, filters);
const showGroups = getFilterById(SHOW_GROUPS_FILTER_ID, filters)?.value ?? true;
dataModel.nodes.forEach((d) => {
d.visible = true;
if (displayFilterers) {
Expand All @@ -61,6 +64,11 @@ export const updateModelFromFilters = (
});
}

if (!showGroups) {
dataModel.nodes = dataModel.nodes.filter((n) => !n.group);
dataModel.edges = [];
}

// create links from data, only include those which have a valid source and target
const edges = dataModel.edges.filter(
(d) =>
Expand Down
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Select, SelectGroup, SelectOption, SelectVariant, Switch } from '@patternfly/react-core';
import { TopologyDisplayFilterType, DisplayFilters } from '../topology-types';
import { EXPAND_GROUPS_FILTER_ID } from './const';
import { EXPAND_GROUPS_FILTER_ID, SHOW_GROUPS_FILTER_ID } from './const';

import './FilterDropdown.scss';

Expand All @@ -19,6 +19,7 @@ const FilterDropdown: React.FC<FilterDropdownProps> = ({
opened = false,
}) => {
const [isOpen, setIsOpen] = React.useState(opened);
const showGroups = filters?.find((f) => f.id === SHOW_GROUPS_FILTER_ID)?.value ?? true;
const groupsExpanded = filters?.find((f) => f.id === EXPAND_GROUPS_FILTER_ID)?.value ?? true;

const onToggle = (open: boolean): void => setIsOpen(open);
Expand All @@ -28,6 +29,18 @@ const FilterDropdown: React.FC<FilterDropdownProps> = ({
onChange([...filters.slice(0, index), filter, ...filters.slice(index + 1)]);
};

const onShowGroupsChange = (value: boolean) => {
const index = filters?.findIndex((f) => f.id === SHOW_GROUPS_FILTER_ID) ?? -1;
if (index === -1) {
return;
}
const filter = {
...filters[index],
value,
};
onChange([...filters.slice(0, index), filter, ...filters.slice(index + 1)]);
};

const onGroupsExpandedChange = (value: boolean) => {
const index = filters?.findIndex((f) => f.id === EXPAND_GROUPS_FILTER_ID) ?? -1;
if (index === -1) {
Expand Down Expand Up @@ -55,6 +68,12 @@ const FilterDropdown: React.FC<FilterDropdownProps> = ({

const selectContent = (
<div className="odc-topology-filter-dropdown">
<div className="odc-topology-filter-dropdown__group">
<span className="odc-topology-filter-dropdown__expand-groups-switcher">
<span className="pf-c-select__menu-group-title">Show Groups</span>
<Switch aria-label="Show Groups" isChecked={showGroups} onChange={onShowGroupsChange} />
</span>
</div>
{expandFilters.length ? (
<div className="odc-topology-filter-dropdown__group">
<span className="odc-topology-filter-dropdown__expand-groups-switcher">
Expand All @@ -63,14 +82,15 @@ const FilterDropdown: React.FC<FilterDropdownProps> = ({
aria-label="Collapse Groups"
isChecked={groupsExpanded}
onChange={onGroupsExpandedChange}
isDisabled={!showGroups}
/>
</span>
<SelectGroup className="odc-topology-filter-dropdown__expand-groups-label">
{expandFilters.map((filter) => (
<SelectOption
key={filter.id}
value={filter.id}
isDisabled={!groupsExpanded}
isDisabled={!groupsExpanded || !showGroups}
isChecked={filter.value}
>
{filter.label}
Expand Down
Expand Up @@ -3,7 +3,13 @@ import { mount, shallow } from 'enzyme';
import { SelectOption, Switch } from '@patternfly/react-core';
import FilterDropdown from '../FilterDropdown';
import { DisplayFilters } from '../../topology-types';
import { DEFAULT_TOPOLOGY_FILTERS, EXPAND_APPLICATION_GROUPS_FILTER_ID } from '../const';
import {
DEFAULT_TOPOLOGY_FILTERS,
EXPAND_APPLICATION_GROUPS_FILTER_ID,
EXPAND_GROUPS_FILTER_ID,
SHOW_GROUPS_FILTER_ID,
} from '../const';
import { getFilterById } from '../filter-utils';

describe(FilterDropdown.displayName, () => {
let dropdownFilter: DisplayFilters;
Expand Down Expand Up @@ -50,7 +56,7 @@ describe(FilterDropdown.displayName, () => {
expect(wrapper.find(SelectOption)).toHaveLength(1);
});

it('should contain the show expand groups switch', () => {
it('should contain the show groups and expand groups switches', () => {
const wrapper = mount(
<FilterDropdown
filters={dropdownFilter}
Expand All @@ -59,6 +65,48 @@ describe(FilterDropdown.displayName, () => {
opened
/>,
);
expect(wrapper.find(Switch)).toHaveLength(1);
expect(wrapper.find(Switch)).toHaveLength(2);
});

it('should disable individual group expand when expand groups is false', () => {
getFilterById(EXPAND_GROUPS_FILTER_ID, dropdownFilter).value = false;
const wrapper = mount(
<FilterDropdown
filters={dropdownFilter}
supportedFilters={dropdownFilter.map((f) => f.id)}
onChange={onChange}
opened
/>,
);
expect(
wrapper
.find(SelectOption)
.first()
.props().isDisabled,
).toBeTruthy();
});

it('should disable expand groups and individual group expands when show groups is false', () => {
getFilterById(SHOW_GROUPS_FILTER_ID, dropdownFilter).value = false;
const wrapper = mount(
<FilterDropdown
filters={dropdownFilter}
supportedFilters={dropdownFilter.map((f) => f.id)}
onChange={onChange}
opened
/>,
);
expect(
wrapper
.find(Switch)
.at(1)
.props().isDisabled,
).toBeTruthy();
expect(
wrapper
.find(SelectOption)
.first()
.props().isDisabled,
).toBeTruthy();
});
});
Expand Up @@ -4,8 +4,16 @@ export const SHOW_POD_COUNT_FILTER_ID = 'show-pod-count';
export const SHOW_LABELS_FILTER_ID = 'show-labels';
export const EXPAND_APPLICATION_GROUPS_FILTER_ID = 'expand-app-groups';
export const EXPAND_GROUPS_FILTER_ID = 'expand-groups';
export const SHOW_GROUPS_FILTER_ID = 'show-groups';

export const DEFAULT_TOPOLOGY_FILTERS = [
{
type: TopologyDisplayFilterType.expand,
id: SHOW_GROUPS_FILTER_ID,
label: 'Show Groups',
priority: 1,
value: true,
},
{
type: TopologyDisplayFilterType.expand,
id: EXPAND_GROUPS_FILTER_ID,
Expand Down

0 comments on commit a03ab5e

Please sign in to comment.