Skip to content

Commit

Permalink
add support for date type filter
Browse files Browse the repository at this point in the history
  • Loading branch information
chriscollins3456 committed Dec 11, 2024
1 parent 063f50e commit ab2101a
Show file tree
Hide file tree
Showing 8 changed files with 181 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import moment from 'moment';
import { Text } from '@src/alchemy-components';
import React, { useCallback, useRef, useState } from 'react';
import { DatePicker } from 'antd';
import styled from 'styled-components';
import { FacetFilterInput, FacetMetadata, FilterOperator } from '@src/types.generated';
import { useFilterDisplayName } from '../utils';
import useDateRangeFilterValues, { Datetime } from './useDateRangeFilterValues';

const { RangePicker } = DatePicker;

const Container = styled.div`
padding: 16px;
background-color: #ffffff;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
border-radius: 8px;
min-width: 225px;
`;

interface Props {
field: FacetMetadata;
manuallyUpdateFilters: (newValues: FacetFilterInput[]) => void;
}

export default function DateRangeMenu({ field, manuallyUpdateFilters }: Props) {
const displayName = useFilterDisplayName(field);
moment.tz.setDefault('GMT');

const [startDate, setStartDate] = useState<Datetime>(null);
const [endDate, setEndDate] = useState<Datetime>(null);
const [isOpen, setIsOpen] = useState<boolean>(false);
const ref = useRef<any>(null);

useDateRangeFilterValues({ filterField: field.field, setStartDate, setEndDate });

const handleOpenChange = useCallback(
(open: boolean) => {
setIsOpen(open);
if (!open) {
ref.current?.blur();
if (startDate && endDate) {
manuallyUpdateFilters([
{
field: field.field,
values: [startDate.valueOf().toString()],
condition: FilterOperator.GreaterThan,
},
{
field: field.field,
values: [endDate.valueOf().toString()],
condition: FilterOperator.LessThan,
},
]);
}
}
},
[startDate, endDate, field.field, manuallyUpdateFilters],
);

const handleRangeChange = useCallback((dates: [Datetime, Datetime] | null) => {
const [start, end] = dates || [null, null];

start?.set({ hour: 0, minute: 0, second: 0, millisecond: 0 });
end?.set({ hour: 23, minute: 59, second: 59, millisecond: 999 });

setStartDate(start);
setEndDate(end);
}, []);

return (
<Container>
<Text weight="bold">Filter by {displayName}</Text>
<RangePicker
ref={ref}
open={isOpen}
allowClear
allowEmpty={[true, true]}
bordered={false}
value={[startDate, endDate]}
format="ll"
onChange={handleRangeChange}
onOpenChange={handleOpenChange}
onCalendarChange={() => handleOpenChange(true)}
style={{ paddingLeft: 0, paddingRight: 0 }}
/>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useEffect, useMemo } from 'react';
import { FilterOperator } from '@src/types.generated';
import moment from 'moment';
import useGetSearchQueryInputs from '../../useGetSearchQueryInputs';

export type Datetime = moment.Moment | null;

interface Props {
filterField: string;
setStartDate: React.Dispatch<React.SetStateAction<Datetime>>;
setEndDate: React.Dispatch<React.SetStateAction<Datetime>>;
}

export default function useDateRangeFilterValues({ filterField, setStartDate, setEndDate }: Props) {
const { filters: activeFilters } = useGetSearchQueryInputs();
const matchingFilters = useMemo(
() => activeFilters.filter((f) => f.field === filterField),
[activeFilters, filterField],
);
const startDateFromFilter = useMemo(
() => matchingFilters.find((f) => f.condition === FilterOperator.GreaterThan)?.values?.[0],
[matchingFilters],
);
const endDateFromFilter = useMemo(
() => matchingFilters.find((f) => f.condition === FilterOperator.LessThan)?.values?.[0],
[matchingFilters],
);

useEffect(() => {
if (startDateFromFilter) {
setStartDate(
moment(parseInt(startDateFromFilter, 10)).set({ hour: 0, minute: 0, second: 0, millisecond: 0 }),
);
} else {
setStartDate(null);
}
}, [startDateFromFilter, setStartDate]);

useEffect(() => {
if (endDateFromFilter) {
setEndDate(
moment(parseInt(endDateFromFilter, 10)).set({ hour: 23, minute: 59, second: 59, millisecond: 999 }),
);
} else {
setEndDate(null);
}
}, [endDateFromFilter, setEndDate]);
}
3 changes: 3 additions & 0 deletions datahub-web-react/src/app/search/filters/MoreFilterOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default function MoreFilterOption({ filter, activeFilters, onChangeFilter
areFiltersLoading,
searchQuery,
updateSearchQuery,
manuallyUpdateFilters,
} = useSearchFilterDropdown({
filter,
activeFilters,
Expand All @@ -56,6 +57,8 @@ export default function MoreFilterOption({ filter, activeFilters, onChangeFilter
updateSearchQuery={updateSearchQuery}
isLoading={areFiltersLoading}
searchPlaceholder={displayName || ''}
filter={filter}
manuallyUpdateFilters={manuallyUpdateFilters}
/>
)}
>
Expand Down
15 changes: 15 additions & 0 deletions datahub-web-react/src/app/search/filters/OptionsDropdownMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import styled from 'styled-components/macro';
import { useEntityRegistry } from '../../useEntityRegistry';
import { SearchBar } from '../SearchBar';
import { useEnterKeyListener } from '../../shared/useEnterKeyListener';
import { FacetFilterInput, FacetMetadata } from '@src/types.generated';
import { getIsDateRangeFilter } from './utils';
import DateRangeMenu from './DateRangeMenu/DateRangeMenu';

const StyledButton = styled(Button)`
width: 100%;
Expand Down Expand Up @@ -53,6 +56,8 @@ interface Props {
updateSearchQuery: (query: string) => void;
searchPlaceholder?: string;
style?: CSSProperties;
filter?: FacetMetadata;
manuallyUpdateFilters?: (newValues: FacetFilterInput[]) => void;
}

export default function OptionsDropdownMenu({
Expand All @@ -63,11 +68,21 @@ export default function OptionsDropdownMenu({
updateSearchQuery,
searchPlaceholder,
style,
filter,
manuallyUpdateFilters,
}: Props) {
const entityRegistry = useEntityRegistry();

useEnterKeyListener({ querySelectorToExecuteClick: '#updateFiltersButton' });

if (filter && manuallyUpdateFilters && getIsDateRangeFilter(filter)) {
return (
<DropdownMenu data-testid="filter-dropdown" style={style}>
<DateRangeMenu field={filter} manuallyUpdateFilters={manuallyUpdateFilters} />
</DropdownMenu>
);
}

return (
<DropdownMenu data-testid="filter-dropdown" style={style}>
<ScrollableContent>
Expand Down
3 changes: 3 additions & 0 deletions datahub-web-react/src/app/search/filters/SearchFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default function SearchFilter({ filter, activeFilters, onChangeFilters }:
areFiltersLoading,
searchQuery,
updateSearchQuery,
manuallyUpdateFilters,
} = useSearchFilterDropdown({
filter,
activeFilters,
Expand All @@ -46,6 +47,8 @@ export default function SearchFilter({ filter, activeFilters, onChangeFilters }:
updateIsMenuOpen={updateIsMenuOpen}
setSearchQuery={updateSearchQuery}
updateFilters={updateFilters}
filter={filter}
manuallyUpdateFilters={manuallyUpdateFilters}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { CaretDownFilled } from '@ant-design/icons';
import { Dropdown } from 'antd';
import React from 'react';
import styled from 'styled-components';
import { FacetFilterInput, FacetMetadata } from '@src/types.generated';
import OptionsDropdownMenu from './OptionsDropdownMenu';
import { capitalizeFirstLetterOnly } from '../../shared/textUtil';
import { DisplayedFilterOption } from './mapFilterOption';
import { SearchFilterLabel } from './styledComponents';

Expand All @@ -27,6 +27,8 @@ interface Props {
updateIsMenuOpen: (isOpen: boolean) => void;
setSearchQuery: (query: string) => void;
updateFilters: () => void;
filter?: FacetMetadata;
manuallyUpdateFilters?: (newValues: FacetFilterInput[]) => void;
}

export default function SearchFilterView({
Expand All @@ -40,6 +42,8 @@ export default function SearchFilterView({
updateIsMenuOpen,
setSearchQuery,
updateFilters,
filter,
manuallyUpdateFilters,
}: Props) {
return (
<Dropdown
Expand All @@ -55,6 +59,8 @@ export default function SearchFilterView({
updateSearchQuery={setSearchQuery}
isLoading={loading}
searchPlaceholder={displayName}
filter={filter}
manuallyUpdateFilters={manuallyUpdateFilters}
/>
)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ export default function useSearchFilterDropdown({ filter, activeFilters, onChang
setIsMenuOpen(false);
}

function manuallyUpdateFilters(newFilters: FacetFilterInput[]) {
// remove any filters that are in newFilters to overwrite them
const filtersNotInNewFilters = activeFilters.filter(
(f) => !newFilters.find((newFilter) => newFilter.field === f.field),
);
onChangeFilters([...filtersNotInNewFilters, ...newFilters]);
setIsMenuOpen(false);
}

function updateSearchQuery(newQuery: string) {
setSearchQuery(newQuery);
if (newQuery && FACETS_TO_ENTITY_TYPES[filter.field]) {
Expand Down Expand Up @@ -128,5 +137,6 @@ export default function useSearchFilterDropdown({ filter, activeFilters, onChang
areFiltersLoading: loading,
searchQuery,
updateSearchQuery,
manuallyUpdateFilters,
};
}
7 changes: 7 additions & 0 deletions datahub-web-react/src/app/search/filters/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,3 +437,10 @@ export function useFilterDisplayName(filter: FacetMetadata, predicateDisplayName

return predicateDisplayName || filter.displayName || filter.field;
}

export function getIsDateRangeFilter(field: FacetMetadata) {
if (field.entity && field.entity.type === EntityType.StructuredProperty) {
return (field.entity as StructuredPropertyEntity).definition?.valueType?.urn === DATE_TYPE_URN;
}
return false;
}

0 comments on commit ab2101a

Please sign in to comment.