Skip to content

Commit

Permalink
feat: tracking the usage of filters #344
Browse files Browse the repository at this point in the history
  • Loading branch information
BruceRodrigues committed Nov 23, 2023
1 parent b801f9e commit 89ea7f0
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { CloseRounded } from "@mui/icons-material";
import { Grow, PopoverPosition, PopoverProps } from "@mui/material";
import React, { MouseEvent, ReactNode, useState } from "react";
import { SelectCategoryView } from "../../../../common/entities";
import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
import {
OnFilterFn,
OnFilterOpenedFn,
} from "../../../../hooks/useCategoryFilter";
import { CloseDrawerIconButton } from "../../../common/IconButton/iconButton.styles";
import { FilterLabel } from "../FilterLabel/filterLabel";
import { FilterMenu } from "../FilterMenu/filterMenu";
Expand All @@ -18,18 +21,22 @@ const DRAWER_SLOT_PROPS: PopoverProps["slotProps"] = {
};

export interface FilterProps {
categorySection?: string;
categoryView: SelectCategoryView;
closeAncestor?: () => void;
isFilterDrawer: boolean;
onFilter: OnFilterFn;
onFilterOpened?: OnFilterOpenedFn;
tags?: ReactNode; // e.g. filter tags
}

export const Filter = ({
categorySection,
categoryView,
closeAncestor,
isFilterDrawer,
onFilter,
onFilterOpened,
tags,
}: FilterProps): JSX.Element => {
const [isOpen, setIsOpen] = useState<boolean>(false);
Expand Down Expand Up @@ -67,6 +74,7 @@ export const Filter = ({
// Set popover position and open state.
setPosition({ left: popoverLeftPos, top: popoverTopPos });
setIsOpen(true);
onFilterOpened?.(categoryView.key);
};

return (
Expand Down Expand Up @@ -95,6 +103,7 @@ export const Filter = ({
/>
)}
<FilterMenu
categorySection={categorySection}
categoryKey={categoryView.key}
categoryLabel={categoryView.label}
isFilterDrawer={isFilterDrawer}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Button, FilterView, FilterViewTools } from "./filterMenu.styles";
export interface FilterMenuProps {
categoryKey: CategoryKey;
categoryLabel: string;
categorySection?: string;
isFilterDrawer: boolean;
menuWidth?: number;
onCloseFilter: () => void;
Expand All @@ -25,6 +26,7 @@ export interface FilterMenuProps {
export const FilterMenu = ({
categoryKey,
categoryLabel,
categorySection,
isFilterDrawer,
menuWidth = 312,
onCloseFilter,
Expand Down Expand Up @@ -55,6 +57,7 @@ export const FilterMenu = ({
</FilterViewTools>
{filteredValues.length > 0 ? (
<VariableSizeList
categorySection={categorySection}
categoryKey={categoryKey}
isFilterDrawer={isFilterDrawer}
onFilter={onFilter}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
BREAKPOINT_FN_NAME,
useBreakpointHelper,
} from "../../../../hooks/useBreakpointHelper";
import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
import {
OnFilterFn,
OnFilterOpenedFn,
} from "../../../../hooks/useCategoryFilter";
import { useWindowResize } from "../../../../hooks/useWindowResize";
import { DESKTOP_SM } from "../../../../theme/common/breakpoints";
import { Filter } from "../Filter/filter";
Expand All @@ -22,6 +25,7 @@ export interface FiltersProps {
closeAncestor?: () => void;
disabled: boolean; // Global disabling of filters; typically in "related" entity view.
onFilter: OnFilterFn;
onFilterOpened?: OnFilterOpenedFn;
}

/**
Expand Down Expand Up @@ -65,6 +69,7 @@ export const Filters = ({
closeAncestor,
disabled = false,
onFilter,
onFilterOpened,
}: FiltersProps): JSX.Element => {
const isFilterDrawer = useBreakpointHelper(
BREAKPOINT_FN_NAME.DOWN,
Expand All @@ -80,16 +85,18 @@ export const Filters = ({

return (
<FilterList disabled={disabled} height={height} ref={filterListRef}>
{categoryFilters.map(({ categoryViews }, i) => (
{categoryFilters.map(({ categoryViews, label }, i) => (
<Fragment key={i}>
{i !== 0 && <Divider />}
{categoryViews.map((categoryView) => (
<Filter
key={categoryView.key}
categorySection={label}
categoryView={categoryView}
closeAncestor={closeAncestor}
isFilterDrawer={isFilterDrawer}
onFilter={onFilter}
onFilterOpened={onFilterOpened}
tags={renderFilterTags(categoryView, onFilter)}
/>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
VariableSizeListProps as ListProps,
} from "react-window";
import { SelectCategoryView } from "../../../../../../common/entities";
import { escapeRegExp } from "../../../../../../common/utils";
import {
BREAKPOINT_FN_NAME,
useBreakpointHelper,
Expand Down Expand Up @@ -59,7 +58,7 @@ interface VariableSizeListData {
filteredItems: SearchAllFiltersItem[];
onFilter: OnFilterFn;
onUpdateItemSizeByItemKey: (key: string, size: number) => void;
searchTermRegExp: RegExp | null;
searchTerm?: string;
}

interface OuterElementContextValue {
Expand All @@ -75,12 +74,8 @@ interface OuterElementContextValue {
function renderListItem(props: ListChildComponentProps): JSX.Element {
const { data, index, style } = props;
delete style.height; // Remove height style to allow variable size list to set item height.
const {
filteredItems,
onFilter,
onUpdateItemSizeByItemKey,
searchTermRegExp,
} = data as VariableSizeListData;
const { filteredItems, onFilter, onUpdateItemSizeByItemKey, searchTerm } =
data as VariableSizeListData;
const item = filteredItems[index];
if (item.type === ITEM_TYPE.DIVIDER) return <Divider style={style} />;
else
Expand All @@ -89,7 +84,7 @@ function renderListItem(props: ListChildComponentProps): JSX.Element {
item={item}
onFilter={onFilter}
onUpdateItemSizeByItemKey={onUpdateItemSizeByItemKey}
searchTermRegExp={searchTermRegExp}
searchTerm={searchTerm}
style={style}
/>
);
Expand Down Expand Up @@ -135,9 +130,6 @@ export const VariableSizeList = forwardRef<
autocompleteListRef
): JSX.Element {
const filteredItems = applyMenuFilter(categoryViews, searchTerm);
const searchTermRegExp = searchTerm
? new RegExp(escapeRegExp(searchTerm), "ig")
: null;
let resizeRequired = true;
const desktopSmDown = useBreakpointHelper(
BREAKPOINT_FN_NAME.DOWN,
Expand Down Expand Up @@ -188,7 +180,7 @@ export const VariableSizeList = forwardRef<
filteredItems,
onFilter,
onUpdateItemSizeByItemKey,
searchTermRegExp,
searchTerm,
}}
itemSize={(index): number => {
const item = filteredItems[index];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Typography,
} from "@mui/material";
import React, { useEffect, useRef } from "react";
import { escapeRegExp } from "../../../../../../common/utils";
import { OnFilterFn } from "../../../../../../hooks/useCategoryFilter";
import { TEXT_BODY_SMALL_400 } from "../../../../../../theme/common/typography";
import { CheckedIcon } from "../../../../../common/CustomIcon/components/CheckedIcon/checkedIcon";
Expand All @@ -18,18 +19,21 @@ interface Props {
item: SearchAllFiltersDynamicItem;
onFilter: OnFilterFn;
onUpdateItemSizeByItemKey: (key: string, size: number) => void;
searchTermRegExp: RegExp | null;
searchTerm?: string;
style: React.CSSProperties;
}

export default function VariableSizeListItem({
item,
onFilter,
onUpdateItemSizeByItemKey,
searchTermRegExp,
searchTerm,
style,
}: Props): JSX.Element {
const { key } = item;
const searchTermRegExp = searchTerm
? new RegExp(escapeRegExp(searchTerm), "ig")
: null;
const listItemRef = useRef<HTMLElement>();

const setRef = (e: HTMLElement | null): void => {
Expand All @@ -50,7 +54,9 @@ export default function VariableSizeListItem({
<ListItemButton
ref={setRef}
key={key}
onClick={(): void => onFilter(categoryKey, valueKey, !selected)}
onClick={(): void =>
onFilter(categoryKey, valueKey, !selected, undefined, searchTerm)
}
selected={selected}
style={style}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export type ItemSizeByItemKey = Map<string, number>;

export interface VariableSizeListProps {
categoryKey: CategoryKey;
categorySection?: string;
height?: number; // Height of list; vertical list must be a number.
isFilterDrawer: boolean;
itemSize?: number; // Default item size.
Expand All @@ -39,9 +40,16 @@ export interface VariableSizeListProps {
*/
function renderListItem(props: ListChildComponentProps): JSX.Element {
const { data, index, style } = props;
const { categoryKey, onFilter, onUpdateItemSizeByItemKey, values } = data;
const {
categoryKey,
categorySection,
onFilter,
onUpdateItemSizeByItemKey,
values,
} = data;
return (
<VariableSizeListItem
categorySection={categorySection}
categoryKey={categoryKey}
listItem={values[index]}
onFilter={onFilter}
Expand All @@ -53,6 +61,7 @@ function renderListItem(props: ListChildComponentProps): JSX.Element {

export const VariableSizeList = ({
categoryKey,
categorySection,
height: initHeight = MAX_LIST_HEIGHT_PX,
isFilterDrawer,
itemSize = LIST_ITEM_HEIGHT,
Expand Down Expand Up @@ -101,6 +110,7 @@ export const VariableSizeList = ({
itemCount={values.length}
itemData={{
categoryKey,
categorySection,
onFilter,
onUpdateItemSizeByItemKey,
values,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { UncheckedIcon } from "../../../common/CustomIcon/components/UncheckedIc

interface Props {
categoryKey: CategoryKey;
categorySection?: string;
listItem: SelectCategoryValueView;
onFilter: OnFilterFn;
onUpdateItemSizeByItemKey: (itemKey: string, itemSize: number) => void;
Expand All @@ -24,6 +25,7 @@ interface Props {

export default function VariableSizeListItem({
categoryKey,
categorySection,
listItem,
onFilter,
onUpdateItemSizeByItemKey,
Expand All @@ -38,10 +40,14 @@ export default function VariableSizeListItem({
onUpdateItemSizeByItemKey(key, listItemRef.current?.clientHeight || 0);
}, [key, onUpdateItemSizeByItemKey]);

const handleItemClicked = (): void => {
onFilter(categoryKey, key, !selected, categorySection);
};

return (
<ListItemButton
ref={listItemRef}
onClick={(): void => onFilter(categoryKey, key, !selected)}
onClick={handleItemClicked}
selected={selected}
style={style}
>
Expand Down
25 changes: 24 additions & 1 deletion packages/data-explorer-ui/src/config/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,23 @@ type RelatedSearchFunction = (
selectedCategoryValues: SelectedFilterValue | undefined
) => Promise<RelatedSearchResult | undefined>;

/**
* Category clicked callback function
*/
type CategoryClickedFunction = (
key: string,
value: string,
section: string,
selected: boolean,
fromSearchAll: boolean,
searchTerm: string
) => void;

/**
* Category opened callback function
*/
type CategoryOpenedFunction = (key: string) => void;

/**
* Product of the related search function.
*/
Expand All @@ -283,6 +300,12 @@ export interface RelatedViewConfig {
searchKey: CategoryKey; // The related search function search parameters' category key.
}

export interface CategorySiteConfig {
categoryGroupConfigs?: CategoryGroupConfig[];
onCategoryClicked?: CategoryClickedFunction;
onCategoryOpened?: CategoryOpenedFunction;
}

/**
* Interface that will hold the whole configuration for a given site.
*/
Expand All @@ -291,7 +314,7 @@ export interface SiteConfig {
appTitle: string;
authentication?: AuthenticationConfig;
browserURL: string;
categoryGroupConfigs?: CategoryGroupConfig[];
categorySiteConfig?: CategorySiteConfig;
contentDir?: string;
contentThemeOptionsFn?: ThemeOptionsFn;
dataSource: DataSourceConfig;
Expand Down
6 changes: 3 additions & 3 deletions packages/data-explorer-ui/src/hooks/useCategoryConfigs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { useConfig } from "./useConfig";
*/
export const useCategoryConfigs = (): CategoryConfig[] | undefined => {
const { config } = useConfig();
const { categoryGroupConfigs } = config;
const { categorySiteConfig } = config;
return useMemo(() => {
return categoryGroupConfigs?.flatMap(
return categorySiteConfig?.categoryGroupConfigs?.flatMap(
({ categoryConfigs }) => categoryConfigs
);
}, [categoryGroupConfigs]);
}, [categorySiteConfig?.categoryGroupConfigs]);
};
9 changes: 8 additions & 1 deletion packages/data-explorer-ui/src/hooks/useCategoryFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,16 @@ export interface FilterInstance {
export type OnFilterFn = (
categoryKey: CategoryKey,
selectedCategoryValue: CategoryValueKey,
selected: boolean
selected: boolean,
categorySection?: string,
searchTerm?: string
) => void;

/**
* Callback function invoked when a filter menu is opened
*/
export type OnFilterOpenedFn = (key: CategoryKey) => void;

/**
* Build the view-specific model of the given category value.
* @param categoryValue - The category value to build a view model of.
Expand Down
Loading

0 comments on commit 89ea7f0

Please sign in to comment.