-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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(ui/graphql) Add ability to sort search results from search results page #8595
feat(ui/graphql) Add ability to sort search results from search results page #8595
Conversation
@@ -456,7 +456,7 @@ public SearchResult searchAcrossEntities(@Nonnull List<String> entities, @Nonnul | |||
@Nonnull | |||
public SearchResult searchAcrossEntities(@Nonnull List<String> entities, @Nonnull String input, | |||
@Nullable Filter filter, int start, int count, @Nullable SearchFlags searchFlags, | |||
@Nonnull final Authentication authentication, @Nullable List<String> facets) | |||
@Nullable SortCriterion sortCriterion, @Nonnull final Authentication authentication, @Nullable List<String> facets) | |||
throws RemoteInvocationException { | |||
|
|||
final EntitiesDoSearchAcrossEntitiesRequestBuilder requestBuilder = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the EntitiesDoSearchAcrossEntitiesRequestBuilder
actually has a parameter for the sort criterion: Do you want to add it here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice good call! adding now
const { filterMode, filterModeRef, setFilterMode } = useFilterMode(filters, unionType); | ||
const { selectedSortOption } = useSearchContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a great place for a useSelectedSortOption
custom hook
import { DEFAULT_SORT_OPTION, SORT_OPTIONS } from './constants'; | ||
|
||
export default function SearchContextProvider({ children }: { children: React.ReactNode }) { | ||
const [selectedSortOption, setSelectedSortOption] = useState(DEFAULT_SORT_OPTION); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if I'm missing something, but why do we need another state value here? Couldn't we just map what we need from params
into another var and pass that into the context? so updateSelectedSortOption would mutate the url and then params would swap the value for a fresh context object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah tbh this is because I built it without storing in the URLfirst to start and then added storing in the URL later - but now with fresh eyes I totally agree. I like having this context as it's easier to grab values and set values and we can extend it later for other global search state stuff.
so I think i'll just remove this local state and pass in selectedSortOption
as the value from URL params and the setter function to update url params.
export const SORT_OPTIONS = { | ||
[RECOMMENDED]: { label: 'Recommended', field: RECOMMENDED, sortOrder: SortOrder.Descending }, | ||
[`${NAME_FIELD}_${SortOrder.Ascending}`]: { label: 'A to Z', field: NAME_FIELD, sortOrder: SortOrder.Ascending }, | ||
[`${NAME_FIELD}_${SortOrder.Descending}`]: { label: 'Z to A', field: NAME_FIELD, sortOrder: SortOrder.Descending }, | ||
[`${LAST_OPERATION_TIME_FIELD}_${SortOrder.Descending}`]: { | ||
label: 'Last Modified in Platform', | ||
field: LAST_OPERATION_TIME_FIELD, | ||
sortOrder: SortOrder.Descending, | ||
}, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Soooooo good
|
||
export function useSearchContext() { | ||
return useContext(SearchContext); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could throw a few other custom hooks in here for selecting off of this context
import { RouteComponentProps } from 'react-router'; | ||
|
||
export function updateUrlParam(history: RouteComponentProps['history'], key: string, value: string) { | ||
const url = new URL(window.location.href); | ||
const { searchParams } = url; | ||
searchParams.set(key, value); | ||
history.replace(url.search); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels even more generic than search and could go in a higher-level utils file somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious how the browse sidebar behaves when we mutate url params without adjusting a filter.... should be fine but I know we got a useEffect in there that kicks off some stuff when params change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I could see that and may move this. But I tested the browse sidebar with this functionality and things look solid. it's not a URL param they're listening to so things look unaffected
@@ -13,7 +14,9 @@ export default function AppProviders({ children }: Props) { | |||
<AppConfigProvider> | |||
<UserContextProvider> | |||
<EducationStepsProvider> | |||
<QuickFiltersProvider>{children}</QuickFiltersProvider> | |||
<QuickFiltersProvider> | |||
<SearchContextProvider>{children}</SearchContextProvider> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So happy we have this now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably where our subscription state could go in the future if we ever wanted to hoist it up.
|
||
export function useSearchContext() { | ||
const context = useContext(SearchContext); | ||
if (context === null) throw new Error(`${useSearchContext.name} must be used under a SearchContextProvider`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this guard to work you would need to initialize createContext with null I think. Making the type up there SearchContextType | null. Not a big deal since we're not building a library here, just a wire-up for our state.
`; | ||
|
||
export default function SearchSortSelect() { | ||
const { selectedSortOption, setSelectedSortOption } = useSearchContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be another opportunity for some custom hooks, non blocking feedback here of course!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what would the benefit of using two different custom hooks here? at the end of the day I'm just trying to get these values from our context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this example, not much. But as contexts grow and become more complex, having less usage of the global context and more specific selection of the values becomes cleaner imo.
265e2a4
into
datahub-project:master
This PR updates our
searchAcrossEntities
endpoint to accept a newsortInput
field that allows users to pass in a way to sort their search results. This endpoint will accept ASCENDING or DESCENDING on any field on an entity (so long as it's stored in ES on the entity index). Right now we have it only built out to accept a singular sorting option, but thissortInput
could be extended later to accept a list of fields or something even more complicated.This adds a dropdown in search results to choose from 4 sorting options we provide in the UI
Adding a new sorting option is very easy, just 2 lines of code change - check out my final commit for an example.
Screenshots:
sort name ascending
sort name descending
last modified in source platform
Checklist