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

search filters #23

Merged
merged 13 commits into from
Sep 17, 2024
Merged

search filters #23

merged 13 commits into from
Sep 17, 2024

Conversation

bejoinka
Copy link
Contributor

support search filters with multi-select and single-select

// onUpdateSelectedOptions
}: Props<T>) => {
const { setActiveFilter, updateFilter } = useProviderFilter();
const isActive = filter.selectedOptions && filter.selectedOptions.length > 0;
Copy link
Contributor

@nckhell nckhell Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To prevent unnecessary recalculations during re-renders, you could wrap this in useMemo

const isActive = useMemo(() => filter.selectedOptions && filter.selectedOptions.length > 0, [filter.selectedOptions]);

Comment on lines +42 to +49
onClick={(e) => {
e.stopPropagation();
const updatedFilter = {
...filter,
selectedOptions: []
} as unknown as FilterType<FilterEnum>;
updateFilter(updatedFilter);
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For readability, I'd suggest putting this in a separate function

  const handleBadgeClick = useCallback(
    (e) => {
      e.stopPropagation();
        const updatedFilter = {
          ...filter,
          selectedOptions: []
        } as unknown as FilterType<FilterEnum>;
        updateFilter(updatedFilter);
    },
    [filter, updateFilter]
  );
<SVG
    onClick={handleBadgeClick}
    ...
/>

// onUpdateSelectedOptions: (filter: FilterType<T>) => void;
}

const FilterBadge = <T extends FilterEnum>({
Copy link
Contributor

@nckhell nckhell Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not export const ...? Then we don't need this awkward export { FilterOverlay } at the bottom of the file + we use consistent way of exporting components throughout all the files.

Suggested change
const FilterBadge = <T extends FilterEnum>({
export const FilterBadge:FC<Props> = ({filter}) => {
...
}

Comment on lines +20 to +27
const onTextFilteredOptionsChange = (value: string) => {
const opts = filter.options.filter(
(option) =>
String(option.label).toLowerCase().includes(value.toLowerCase()) ||
String(option.value).toLowerCase().includes(value.toLowerCase())
);
setTextFilteredOptions(opts);
};
Copy link
Contributor

@nckhell nckhell Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could benefit from useCallback

Comment on lines +29 to +43
const onSelectOption = (value: string) => {
if (filter.selectType === 'single') {
updateFilter({
...filter,
selectedOptions: [value]
});
} else {
updateFilter({
...filter,
selectedOptions: filter.selectedOptions.includes(value)
? filter.selectedOptions.filter((v) => v !== value)
: [...filter.selectedOptions, value]
});
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could benefit from useCallback

Comment on lines +41 to +47
const updateFilters = debounce((newFilters: FilterType<FilterEnum>[]) => {
const updatedPreferences = updatePreferencesWithFilters(
initialPreferences,
newFilters
);
onProviderPreferencesChange(updatedPreferences);
}, 500);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCallback?

Comment on lines +49 to +57
const updateFilter = (filter: FilterType<FilterEnum>) => {
const updatedFilters = filters.map((f) => {
if (f.key === filter.key) {
return filter;
}
return f;
});
setFilters(updatedFilters);
updateFilters(updatedFilters);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCallback?

Comment on lines +60 to +68
const changeActiveFilter = (
newFilterKey: keyof GetProvidersInputType | null
) => {
if (newFilterKey === null || newFilterKey === activeFilter) {
setActiveFilter(null);
} else if (newFilterKey !== null) {
setActiveFilter(newFilterKey);
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCallback?

Comment on lines +70 to +74
const getActiveFilter = () => {
return filters.find(
(f) => f.key === activeFilter
) as FilterType<FilterEnum>;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCallback?

Comment on lines +97 to +129
const [providerPrefs, setProviderPrefs] = useState<GetProvidersInputType>(
args.providerPreferences as GetProvidersInputType
);

const handlePrefsChange = (prefs: GetProvidersInputType) => {
console.log('Prefs changed', prefs);
setProviderPrefs(prefs);
fetchProvidersFn();
};

const fetchProvidersFn = useCallback(async () => {
const { data, ...rest } = await args.fetchProviders();
console.log('fetched providers');
return {
data: data.filter((p) => {
if (
providerPrefs.clinicalFocus &&
providerPrefs.clinicalFocus.length > 0
) {
return some(
providerPrefs.clinicalFocus.map((f) =>
p.clinicalFocus
?.map((cf) => cf.toLowerCase())
.includes(f.toLowerCase())
)
);
} else {
return true;
}
}),
...rest
};
}, [providerPrefs]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Challenge: IMO this logic should live inside the SchedulingActivity component and not in the Story/Hosted Pages? I think this would require us to parameterize the `fetchProviders function so it includes the preferences to make that happen?

That way, we avoid business logic inside the story and Hosted Pages.

const [providerPrefs, setProviderPrefs] = useState<GetProvidersInputType>(
  args.providerPreferences as GetProvidersInputType
);

// When preferences change, we force the component to call `fetchProviders` again
// This is logic we can add in SchedulingActivity.tsx
const handlePrefsChange = useCallback(() => args.onProviderPreferencesChange(), []);

// providerPrefs becomes a parameter of the fetch function
const fetchProvidersFn = useCallback((providerPrefs) => args.fetchProviders(providerPrefs), []);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wondered about this myself. we can actually reduce the number of props to the activity if we do that. so, 👍

@bejoinka
Copy link
Contributor Author

thanks for the feedback! i also need to move the close button from the selector to the overlay.

re: useCallback, i'm not excited about putting it in a bunch of places because (1) it reduces readability; and (2) we're not making any external calls anywhere so the benefits are quite minimal. so, on the whole, i don't think it adds value.

when we move the fetchProvidersFn so it handles prefs, i'll add useCallback for that. 👍

@sharlotta93 sharlotta93 merged commit d02ba3f into main Sep 17, 2024
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants