Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tracking the usage of filters #344 #493

Merged
merged 6 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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 { TrackFilterOpenedFunction } from "../../../../config/entities";
import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
import { CloseDrawerIconButton } from "../../../common/IconButton/iconButton.styles";
import { FilterLabel } from "../FilterLabel/filterLabel";
Expand All @@ -18,19 +19,23 @@ const DRAWER_SLOT_PROPS: PopoverProps["slotProps"] = {
};

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

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

return (
Expand Down Expand Up @@ -95,6 +101,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
@@ -1,4 +1,5 @@
import { Divider } from "@mui/material";
import { TrackFilterOpenedFunction } from "config/entities";
import React, { Fragment, useEffect, useRef, useState } from "react";
import { CategoryTag, SelectCategoryView } from "../../../../common/entities";
import {
Expand All @@ -22,6 +23,7 @@ export interface FiltersProps {
closeAncestor?: () => void;
disabled: boolean; // Global disabling of filters; typically in "related" entity view.
onFilter: OnFilterFn;
trackFilterOpened?: TrackFilterOpenedFunction;
}

/**
Expand Down Expand Up @@ -65,6 +67,7 @@ export const Filters = ({
closeAncestor,
disabled = false,
onFilter,
trackFilterOpened,
}: FiltersProps): JSX.Element => {
const isFilterDrawer = useBreakpointHelper(
BREAKPOINT_FN_NAME.DOWN,
Expand All @@ -80,16 +83,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}
trackFilterOpened={trackFilterOpened}
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
39 changes: 39 additions & 0 deletions packages/data-explorer-ui/src/config/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,44 @@ type RelatedSearchFunction = (
selectedCategoryValues: SelectedFilterValue | undefined
) => Promise<RelatedSearchResult | undefined>;

/**
* Filter applied tracking payload
*/
export interface TrackFilterAppliedPayload {
category: string;
fromSearchAll: boolean;
searchTerm: string;
section: string;
selected: boolean;
value: string;
}

/**
* Filter applied tracking function
*/
export type TrackFilterAppliedFunction = (
payload: TrackFilterAppliedPayload
) => void;

/**
* Filter opened tracking payload
*/
export interface TrackFilterOpenedPayload {
category: string;
}

/**
* Filter opened tracking function
*/
export type TrackFilterOpenedFunction = (
payload: TrackFilterOpenedPayload
) => void;

interface TrackingConfig {
trackFilterApplied?: TrackFilterAppliedFunction;
trackFilterOpened?: TrackFilterOpenedFunction;
}

/**
* Product of the related search function.
*/
Expand Down Expand Up @@ -321,6 +359,7 @@ export interface SiteConfig {
redirectRootToPath: string;
summaryConfig?: SummaryConfig;
themeOptions?: ThemeOptions;
trackingConfig?: TrackingConfig;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion packages/data-explorer-ui/src/hooks/useCategoryFilter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export interface FilterInstance {
export type OnFilterFn = (
categoryKey: CategoryKey,
selectedCategoryValue: CategoryValueKey,
selected: boolean
selected: boolean,
categorySection?: string,
searchTerm?: string
) => void;

/**
Expand Down
Loading