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

Add SiteSearch Type-Ahead #332

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open

Add SiteSearch Type-Ahead #332

wants to merge 32 commits into from

Conversation

Foxcapades
Copy link
Member

@Foxcapades Foxcapades commented Jun 28, 2023

Replaces the SiteSearch text input box with a type-ahead component that offers word completion suggestions based on the user's input.

ETA This can't be merged until r51683 is completed.

@Foxcapades Foxcapades added the enhancement New feature or request label Jun 28, 2023
@Foxcapades Foxcapades requested a review from dmfalke June 28, 2023 13:37
@Foxcapades Foxcapades self-assigned this Jun 28, 2023
@Foxcapades Foxcapades requested a review from jernestmyers June 28, 2023 13:37
@Foxcapades Foxcapades marked this pull request as ready for review June 29, 2023 15:36
Copy link
Member

@dmfalke dmfalke left a comment

Choose a reason for hiding this comment

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

This is looking great. I am not opposed to everything residing in this module, as-is. One exception is buildDebounder. I suppose it can go in /packages/libs/web-common/src/util; or even a new package, /packages/libs/utils.

if (ulReference.current?.firstElementChild === e.currentTarget) {
props.inputReference.current?.focus();
} else {
(e.currentTarget.previousElementSibling as HTMLLIElement | null)?.focus();
Copy link
Member

Choose a reason for hiding this comment

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

Wow, I did not know about this property (or nextElementSibling)!

Comment on lines 148 to 186
// region Debouncer

/**
* Debouncer Function Type.
*
* Represents a function that may be used to build a "debounced" or "debouncing"
* function by passing in a target function that takes a value of type `T` and
* returns nothing.
*
* The return value of a Debouncer function call will be a new function that
* wraps the given target function with debouncing.
*
* @param T Type of the value consumed by the function wrapped by and returned
* by the Debouncer function.
*/
type Debouncer<T> = (func: (value: T) => void) => (value: T) => void;


/**
* Builds a new `Debouncer` function that may be used to build one or more
* functions that debounce on the same timer.
*
* @param delay Debouncing delay.
*
* @return A `Debouncer` function that may be used to build one or more
* functions that debounce on the same timer.
*/
function buildDebouncer<T>(delay: number): Debouncer<T> {
let timer: any;

return (func: (value: T) => any) => {
return (value: T) => {
clearTimeout(timer);
timer = setTimeout(() => func(value), delay);
};
};
}

// endregion Debouncer
Copy link
Member

Choose a reason for hiding this comment

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

This is a very useful implementation of a debouncer. Since it allows different functions to use the same timer, it works really well with react components, where a new function is created in every "render" phase.

I've typically used lodash's debounce function, which has the behavior that a new timer is created each time debounce is called.

Something to note is that, since buildDebouncer is invoked at the module scope, if we had multiple TypeAheadInput's on a page, they could potentially cancel one another. That is unlikely to be an issue in this case, but if we were to generalize this, it could be an issue. We would likely want to wrap this in a hook, and use a Ref to hold on the to "timer" value.

/**
* Wrapper for the SiteSearch Type-Ahead HTTP API.
*/
class TypeAheadAPI {
Copy link
Member

Choose a reason for hiding this comment

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

See https://github.com/VEuPathDB/web-monorepo/blob/main/packages/libs/http-utils/src/FetchClient.ts, for a base class you can use to implement this. Note that you can use it with https://github.com/VEuPathDB/web-monorepo/blob/main/packages/libs/http-utils/src/ioTransformer.ts to build run-time validation.

See an example of how it's used here: https://github.com/VEuPathDB/web-monorepo/blob/main/packages/libs/eda/src/lib/core/api/SubsettingClient/index.ts.

Also, note that API methods return a Promise<T>, rather than taking a callback, so you would also have to modify the caller of typeahead, if you go this route.

Copy link
Member

@dmfalke dmfalke left a comment

Choose a reason for hiding this comment

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

Changes look good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants