Skip to content

Commit

Permalink
ref(ui): Updated breadcrumbs filter logic (#19004)
Browse files Browse the repository at this point in the history
  • Loading branch information
priscilawebdev committed May 28, 2020
1 parent e143d6d commit 8e90724
Show file tree
Hide file tree
Showing 24 changed files with 527 additions and 422 deletions.
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import Feature from 'app/components/acl/feature';
import BreadcrumbsInterface from 'app/components/events/interfaces/breadcrumbs/breadcrumbs';
import {Breadcrumbs} from 'app/components/events/interfaces/breadcrumbsV2/breadcrumbs';
import Breadcrumbs from 'app/components/events/interfaces/breadcrumbsV2/breadcrumbs';

type Props = React.ComponentProps<typeof Breadcrumbs>;
type BreadcrumbsInterfaceProps = React.ComponentProps<typeof BreadcrumbsInterface>;
Expand Down
Expand Up @@ -20,28 +20,27 @@ import {
BreadcrumbType,
BreadcrumbLevelType,
} from './types';
import {getTypeDetails} from './getTypeDetails';
import {Filter} from './filter/filter';
import {converType} from './convertType';
import {FilterGroupType} from './filter/types';
import {ListHeader} from './listHeader';
import {ListBody} from './listBody';
import {Level} from './level';
import {Icon} from './icon';
import transformCrumbs from './transformCrumbs';
import Filter from './filter/filter';
import ListHeader from './listHeader';
import ListBody from './listBody';
import Level from './level';
import Icon from './icon';

const MAX_CRUMBS_WHEN_COLLAPSED = 10;

type BreadcrumbWithDetails = Breadcrumb & BreadcrumbDetails & {id: number};
type FilterGroups = React.ComponentProps<typeof Filter>['filterGroups'];
type BreadcrumbsWithDetails = Array<Breadcrumb & BreadcrumbDetails & {id: number}>;
type FilterOptions = React.ComponentProps<typeof Filter>['options'];

type State = {
isCollapsed: boolean;
searchTerm: string;
breadcrumbs: Array<BreadcrumbWithDetails>;
filteredByFilter: Array<BreadcrumbWithDetails>;
filteredByCustomSearch: Array<BreadcrumbWithDetails>;
filteredBreadcrumbs: Array<BreadcrumbWithDetails>;
filterGroups: FilterGroups;
breadcrumbs: BreadcrumbsWithDetails;
filteredByFilter: BreadcrumbsWithDetails;
filteredByCustomSearch: BreadcrumbsWithDetails;
filteredBreadcrumbs: BreadcrumbsWithDetails;
filterOptions: FilterOptions;
listBodyHeight?: React.CSSProperties['maxHeight'];
};

type Props = {
Expand All @@ -61,13 +60,28 @@ class Breadcrumbs extends React.Component<Props, State> {
filteredByFilter: [],
filteredByCustomSearch: [],
filteredBreadcrumbs: [],
filterGroups: [],
filterOptions: [[], []],
};

componentDidMount() {
this.loadBreadcrumbs();
}

componentDidUpdate() {
this.loadListBodyHeight();
}

listBodyRef = React.createRef<HTMLDivElement>();

loadListBodyHeight = () => {
if (!this.state.listBodyHeight) {
const offsetHeight = this.listBodyRef?.current?.offsetHeight;
this.setState({
listBodyHeight: offsetHeight ? `${offsetHeight}px` : 'none',
});
}
};

loadBreadcrumbs = () => {
const {data} = this.props;
let breadcrumbs = data.values;
Expand All @@ -78,54 +92,75 @@ class Breadcrumbs extends React.Component<Props, State> {
breadcrumbs = [...breadcrumbs, virtualCrumb];
}

const breadcrumbTypes: FilterGroups = [];
const breadcrumbLevels: FilterGroups = [];
const tranformedCrumbs = transformCrumbs(breadcrumbs);
const filterOptions = this.getFilterOptions(tranformedCrumbs);

this.setState({
breadcrumbs: tranformedCrumbs,
filteredBreadcrumbs: tranformedCrumbs,
filteredByFilter: tranformedCrumbs,
filteredByCustomSearch: tranformedCrumbs,
filterOptions,
});
};

getFilterOptions = (breadcrumbs: ReturnType<typeof transformCrumbs>): FilterOptions => {
const types = this.getFilterTypes(breadcrumbs);
const levels = this.getFilterLevels(types);
return [types, levels];
};

const convertedBreadcrumbs = breadcrumbs.map((breadcrumb, index) => {
const convertedBreadcrumb = converType(breadcrumb);
const breadcrumbTypeDetails = getTypeDetails(convertedBreadcrumb.type);
getFilterTypes = (breadcrumbs: ReturnType<typeof transformCrumbs>) => {
const filterTypes: FilterOptions[0] = [];

if (!breadcrumbTypes.find(b => b.type === convertedBreadcrumb.type)) {
breadcrumbTypes.push({
groupType: FilterGroupType.TYPE,
type: convertedBreadcrumb.type,
description: breadcrumbTypeDetails.description,
symbol: <Icon {...omit(breadcrumbTypeDetails, 'description')} size="xs" />,
for (const index in breadcrumbs) {
const breadcrumb = breadcrumbs[index];
const foundFilterType = filterTypes.findIndex(f => f.type === breadcrumb.type);

if (foundFilterType === -1) {
filterTypes.push({
type: breadcrumb.type,
description: breadcrumb.description,
symbol: <Icon {...omit(breadcrumb, 'description')} size="xs" />,
levels: breadcrumb?.level ? [breadcrumb.level] : [],
isChecked: true,
isDisabled: false,
});
continue;
}

if (!breadcrumbLevels.find(b => b.type === String(convertedBreadcrumb?.level))) {
breadcrumbLevels.push({
groupType: FilterGroupType.LEVEL,
type: String(convertedBreadcrumb?.level) as BreadcrumbLevelType,
symbol: <Level level={convertedBreadcrumb.level} />,
if (
breadcrumb?.level &&
!filterTypes[foundFilterType].levels.includes(breadcrumb.level)
) {
filterTypes[foundFilterType].levels.push(breadcrumb.level);
}
}

return filterTypes;
};

getFilterLevels = (types: FilterOptions[0]) => {
const filterLevels: FilterOptions[1] = [];

for (const indexType in types) {
for (const indexLevel in types[indexType].levels) {
const level = types[indexType].levels[indexLevel];

if (filterLevels.some(f => f.type === level)) {
continue;
}

filterLevels.push({
type: level,
symbol: <Level level={level} />,
isChecked: true,
isDisabled: false,
});
}
}

return {
id: index,
...convertedBreadcrumb,
...breadcrumbTypeDetails,
};
});

this.setState({
breadcrumbs: convertedBreadcrumbs,
filteredBreadcrumbs: convertedBreadcrumbs,
filteredByFilter: convertedBreadcrumbs,
filteredByCustomSearch: convertedBreadcrumbs,
filterGroups: [
...breadcrumbTypes
// in case of a breadcrumb of type BreadcrumbType.DEFAULT, moves it to the last position of the array
.filter(crumbType => crumbType.type !== BreadcrumbType.DEFAULT)
.concat(
breadcrumbTypes.filter(crumbType => crumbType.type === BreadcrumbType.DEFAULT)
),
...breadcrumbLevels,
],
});
return filterLevels;
};

moduleToCategory = (module: any) => {
Expand Down Expand Up @@ -176,7 +211,7 @@ class Breadcrumbs extends React.Component<Props, State> {
};

getCollapsedCrumbQuantity = (): {
filteredCollapsedBreadcrumbs: Array<BreadcrumbWithDetails>;
filteredCollapsedBreadcrumbs: BreadcrumbsWithDetails;
collapsedQuantity: number;
} => {
const {isCollapsed, filteredBreadcrumbs} = this.state;
Expand All @@ -195,52 +230,6 @@ class Breadcrumbs extends React.Component<Props, State> {
};
};

handleFilter = (filterGroups: FilterGroups) => () => {
//types
const breadcrumbFilterGroupTypes = filterGroups
.filter(
breadcrumbFilterGroup =>
breadcrumbFilterGroup.groupType === 'type' && breadcrumbFilterGroup.isChecked
)
.map(breadcrumbFilterGroup => breadcrumbFilterGroup.type);

//levels
const breadcrumbFilterGroupLevels = filterGroups
.filter(
breadcrumbFilterGroup =>
breadcrumbFilterGroup.groupType === 'level' && breadcrumbFilterGroup.isChecked
)
.map(breadcrumbFilterGroup => breadcrumbFilterGroup.type);

const filteredByFilter = this.state.breadcrumbs.filter(({type, level}) => {
if (
breadcrumbFilterGroupLevels.length > 0 &&
breadcrumbFilterGroupTypes.length > 0
) {
return (
breadcrumbFilterGroupTypes.includes(type) ||
breadcrumbFilterGroupLevels.includes(String(level) as BreadcrumbLevelType)
);
}

if (breadcrumbFilterGroupLevels.length > 0) {
return breadcrumbFilterGroupLevels.includes(String(level) as BreadcrumbLevelType);
}

return breadcrumbFilterGroupTypes.includes(type);
});

this.setState(
{
filteredByFilter,
filterGroups,
},
() => {
this.handleFilterBySearchTerm(this.state.searchTerm);
}
);
};

handleFilterBySearchTerm = (value: string) => {
const {filteredByFilter} = this.state;

Expand Down Expand Up @@ -279,23 +268,79 @@ class Breadcrumbs extends React.Component<Props, State> {
};

handleResetFilter = () => {
this.handleClickFilterCheckAll(true);
};

handleClickFilterCheckAll = (checkAll: boolean) => {
const {filterOptions} = this.state;
const updatedFilterOptions: FilterOptions = [[], []];

for (const index in filterOptions) {
for (const option in filterOptions[index]) {
updatedFilterOptions[index][option] = {
...filterOptions[index][option],
isChecked: checkAll,
isDisabled: false,
};
}
}

this.setState(
prevState => ({
filteredByFilter: prevState.breadcrumbs,
filterGroups: prevState.filterGroups.map(filterGroup => ({
...filterGroup,
isChecked: true,
})),
filteredByFilter: checkAll ? prevState.breadcrumbs : [],
filterOptions: updatedFilterOptions,
}),
() => {
this.handleFilterBySearchTerm(this.state.searchTerm);
}
);
};

filterCrumbsBy = (
type: keyof Pick<BreadcrumbsWithDetails[0], 'level' | 'type'>,
breadcrumbs: BreadcrumbsWithDetails,
filterOptions: Array<FilterOptions[0][0] | FilterOptions[1][0]>
) =>
breadcrumbs.filter(b => {
const crumbProperty = b[type];
if (!crumbProperty) {
return true;
}
const foundInFilterOptions = filterOptions.find(f => f.type === crumbProperty);
if (foundInFilterOptions) {
return foundInFilterOptions.isChecked;
}
return true;
});

handleFilter = (filterOptions: FilterOptions) => {
const {breadcrumbs} = this.state;

const filteredCrumbsByType = this.filterCrumbsBy(
'type',
breadcrumbs,
filterOptions[0]
);
const filteredCrumbsByLevel = this.filterCrumbsBy(
'level',
filteredCrumbsByType,
filterOptions[1]
);

this.setState(
{
filterOptions,
filteredByFilter: filteredCrumbsByLevel,
},
() => {
this.handleFilterBySearchTerm(this.state.searchTerm);
}
);
};

render() {
const {type, event, orgId} = this.props;
const {filterGroups, searchTerm} = this.state;
const {filterOptions, searchTerm, listBodyHeight} = this.state;

const {
collapsedQuantity,
Expand All @@ -307,12 +352,16 @@ class Breadcrumbs extends React.Component<Props, State> {
type={type}
title={
<GuideAnchor target="breadcrumbs" position="bottom">
<h3>{t('Breadcrumbs')} </h3>
<h3>{t('Breadcrumbs')}</h3>
</GuideAnchor>
}
actions={
<Search>
<Filter onFilter={this.handleFilter} filterGroups={filterGroups} />
<Filter
onCheckAll={this.handleClickFilterCheckAll}
onFilter={this.handleFilter}
options={filterOptions}
/>
<StyledSearchBar
placeholder={t('Search breadcrumbs\u2026')}
onChange={this.handleFilterBySearchTerm}
Expand All @@ -333,6 +382,8 @@ class Breadcrumbs extends React.Component<Props, State> {
onToggleCollapse={this.handleToggleCollapse}
collapsedQuantity={collapsedQuantity}
breadcrumbs={filteredCollapsedBreadcrumbs}
maxHeight={listBodyHeight}
ref={this.listBodyRef}
/>
</React.Fragment>
) : (
Expand All @@ -353,7 +404,7 @@ class Breadcrumbs extends React.Component<Props, State> {
}
}

export {Breadcrumbs};
export default Breadcrumbs;

const Content = styled('div')`
box-shadow: ${p => p.theme.dropShadowLightest};
Expand Down
Expand Up @@ -19,7 +19,7 @@ const Category = ({category}: Props) => {
);
};

export {Category};
export default Category;

const Wrapper = styled('div')`
color: ${p => p.theme.gray800};
Expand Down

0 comments on commit 8e90724

Please sign in to comment.