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: Added generic and SearchBar and Orama powered SearchBar. #5419

Closed
wants to merge 1 commit into from
Closed

feat: Added generic and SearchBar and Orama powered SearchBar. #5419

wants to merge 1 commit into from

Conversation

ShogunPanda
Copy link

Description

This PR introduces a Search Bar component and it also introduces a Orama based implementation of the same component.

Check List

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run npx turbo lint to ensure the code follows the style guide. And run npx turbo lint:fix to fix the style errors if necessary.
  • I have run npx turbo format to ensure the code follows the style guide.
  • I have run npx turbo test to check if all tests are passing, and/or npx turbo test:snapshot to update snapshots if I created and/or updated React Components.
  • I've covered new added functionality with unit tests if necessary.

@ShogunPanda ShogunPanda requested a review from a team as a code owner June 5, 2023 12:55
@vercel
Copy link

vercel bot commented Jun 5, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nodejs-org ❌ Failed (Inspect) Jul 4, 2023 9:34am
nodejs-org-stories ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 4, 2023 9:34am

@vercel
Copy link

vercel bot commented Jun 5, 2023

@ShogunPanda is attempting to deploy a commit to the OpenJS Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 5, 2023 12:56 Inactive
@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 5, 2023 12:57 Inactive
@tniessen
Copy link
Member

tniessen commented Jun 5, 2023

Is this a client-side index that is generated at build time? I am not familiar with Orama or Next.js but I believe @ovflowd suggested multiple alternatives and wanted to avoid client-side processing of search indices.

Refs: nodejs/admin#766 (comment)

@ovflowd
Copy link
Member

ovflowd commented Jun 5, 2023

I also haven't checked this PR yet. But I asked the Orama team for something that allowed us leverage both world. A client-side only on first build but if Server-side is available to leverage that 🤔

Will check if this PR is implementing this 👀

@ShogunPanda
Copy link
Author

This PR is opened to both possibilities.
At the moment I'm not generating any index and I'm just providing the component for storybook.
In the future the index can be generated on the server and downloaded via fetch in the setup call. Or can be generated in the browser (always in the setup call).

@vercel vercel bot temporarily deployed to Preview – nodejs-org June 5, 2023 16:11 Inactive
Copy link
Member

@ovflowd ovflowd left a comment

Choose a reason for hiding this comment

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

I haven't gone through the SearchBar and useKeypress hook yet, but this are my comments so far :)

components/Common/OramaSearchBar/index.tsx Show resolved Hide resolved
components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/OramaSearchBar/index.tsx Show resolved Hide resolved
components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

I think all the application logic, like performing search and etc should be done in a Hook and utils files.

Copy link
Author

Choose a reason for hiding this comment

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

Done. What about now?

Copy link
Member

Choose a reason for hiding this comment

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

Let me re-review 👀

components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/SearchBar/index.module.scss Show resolved Hide resolved
@AugustinMauroy
Copy link
Member

AugustinMauroy commented Jun 5, 2023

Hey!
You should update NewHeader with search feat.

@ovflowd
Copy link
Member

ovflowd commented Jun 5, 2023

No, @AugustinMauroy he doesn't need to. He (or anybody else) can do that on a follow-up PR. As this is out of scope of this PR.

@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 6, 2023 20:20 Inactive
@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 6, 2023 20:32 Inactive
@ovflowd
Copy link
Member

ovflowd commented Jun 8, 2023

FYI I still need to review the remaining code once I get some time 👀

@ShogunPanda
Copy link
Author

No worries. Let me know if I have to do anything elsee

components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/OramaSearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/SearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/SearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/SearchBar/index.tsx Outdated Show resolved Hide resolved
components/Common/SearchBar/index.tsx Outdated Show resolved Hide resolved
i18n/locales/en.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 13, 2023 19:11 Inactive
@ovflowd
Copy link
Member

ovflowd commented Jun 14, 2023

FYI gonna finally review the remaining of the PR this week.

@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 14, 2023 11:57 Inactive
@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories June 14, 2023 11:58 Inactive
@ovflowd
Copy link
Member

ovflowd commented Jun 24, 2023

Relates to #5204


setQuery(e.target.value);
},
[setQuery, setResults]
Copy link
Contributor

@Harkunwar Harkunwar Jun 30, 2023

Choose a reason for hiding this comment

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

Suggested change
[setQuery, setResults]
[]

We don't need to add these as any setter resulting from useState will never change.

Copy link
Author

Choose a reason for hiding this comment

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

Here and below: while I agree on that, I tend to pass all values (including immutable one) to useCallback/useMemo in order to have a quick view of what is used in the function. I don't think it affect performance or are there any cons in doing this I'm not aware of?

el.querySelector('a')?.focus();
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
},
[listRef]
Copy link
Contributor

Choose a reason for hiding this comment

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

Refs never change and don't need to be in deps array.

Copy link
Author

Choose a reason for hiding this comment

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

const [isExpanded, setExpanded] = useState(false);
const [query, setQuery] = useState('');
const [results, setResults] = useState<SearchResult[]>([]);
const isEmpty = !results || results.length === 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like this Component is doing to many things. The interactions between a lot of the state objects is making it complicated. Can we try to use maybe useReducer to see if we can clean things up here?

Copy link
Author

Choose a reason for hiding this comment

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

I'm -1 on this.

query and results are basically changed at the same time.
expanded is triggered independently.
The only other state is isLoading which is only changed once.

I feel adding adding a reducer would be slightly overengineered.

@vercel vercel bot temporarily deployed to Preview – nodejs-org-stories July 4, 2023 09:33 Inactive
@vercel vercel bot temporarily deployed to Preview – nodejs-org July 4, 2023 09:34 Inactive
@ovflowd
Copy link
Member

ovflowd commented Jul 4, 2023

And here I go reviewing this, sorry for the super significant delay, @ShogunPanda!

Copy link
Member

@ovflowd ovflowd left a comment

Choose a reason for hiding this comment

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

Overall fantastic work done here, @ShogunPanda.

My review has some minor comments and significant concerns, so I'm sadly requesting changes. Mostly are tied to the SearchBar component.

Thank you so much for your effort here! I hope we're able to address these review concerns so we can move forward 💪

justify-content: end;
margin: 5px;

.oramaSearchBarFooterLabel {
Copy link
Member

Choose a reason for hiding this comment

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

You can use &_ statements here, so you don't need to add the whole prefix again. (Actually in all the child Sass styles here)

Eg.:

.oramaSearchBarFooter {

  &_label {

  }

}

Copy link
Author

Choose a reason for hiding this comment

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

Cool, I'll update this

import type { OramaSearchBarProps } from './search';
import type { SearchFunction } from '../../../types';

const Footer: FC = () => {
Copy link
Member

Choose a reason for hiding this comment

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

Can this become a separate component OramaSearchBarFooter.tsx? Just because our approach here is to have one top-level Component per file.

Copy link
Author

Choose a reason for hiding this comment

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

I guess so, it's pretty self contained. Will move it soon.

rel="noopener noreferrer"
className={styles.oramaSearchBarFooterLink}
>
{isDark ? (
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can simplify this to:

const OramaLogo = theme === 'dark' ? OramaLogoDark : OramaLogoLight;

...

<OramaLogo className={styles.oramaSearchBarFooterLogo} />

Copy link
Author

Choose a reason for hiding this comment

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

👍

);
}, [schema, index, documentMapper, setSearchFunction]);

return <SearchBar setup={setup} search={searchFunction} footer={Footer} />;
Copy link
Member

Choose a reason for hiding this comment

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

I assume that the SearchBar awaits for the setup function?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, see on line 157.


export type DocumentMapper = (doc: Document) => SearchResult;

export type OramaSearchBarProps = {
Copy link
Member

Choose a reason for hiding this comment

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

I believe types should be solely defined on a types.ts file just to separate type definitions from actual search logic

Copy link
Author

Choose a reason for hiding this comment

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

Not an issue. Will move them.

database: Orama,
mapper: DocumentMapper,
term: string
): Promise<SearchResult[]> => {
Copy link
Member

Choose a reason for hiding this comment

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

I feel that this could be transformed into a promise. Could we do that?

Copy link
Author

Choose a reason for hiding this comment

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

Ehm... it is already a promise. What do you mean exactly?

Copy link
Member

Choose a reason for hiding this comment

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

You’re mixing async with Promises. I’m referring to use a pure Promise approach for this function

Copy link
Author

Choose a reason for hiding this comment

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

Ehm... Promise it's just the return type. Are looking for something like this?

export const performSearch = (
  database: Orama,
  mapper: DocumentMapper,
  term: string
): Promise<SearchResult[]> => {
  return search(database, { term }).then(results => results.hits.map(h => mapper(h.document)));
};

Copy link
Member

Choose a reason for hiding this comment

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

Yup, that’s what I meant. I also guess we don’t need a return statement as this is an one liner.

Yeah, I meant native promises rather than async/await.

components/Common/OramaSearchBar/search.ts Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

I'm sadly negatively impressed and shocked with the pure size of this Component.

It is so freaking complex. We should discard this component and do something from scratch.

We don't need to keyboard listeners, handlers and all these things now. We can iterate later.

I know this Component is part of the migration, but since it is so complex, it makes it harder to know what's being migrated and what is not.

If I might suggest, let's discard this component and its styles and feel free to make a new one that allows you to type and render the results.

It would be nice if styles, at least of the search container, could be similar, but the content itself, how it collapses/expands, and how it renders the results up to you.

I'm afraid I have to disagree with a Component with many callbacks, effects, conditions and more. Even the useEffect's super complicated.

For example, the rendering of the list (https://github.com/nodejs/nodejs.org/pull/5419/files#diff-cc0fb6b25cb6dad14226e6daa7b1819a859f6c9e5aa102d9557c52b2db485f67R292) is highly complex; we have two effects (https://github.com/nodejs/nodejs.org/pull/5419/files#diff-cc0fb6b25cb6dad14226e6daa7b1819a859f6c9e5aa102d9557c52b2db485f67R157, https://github.com/nodejs/nodejs.org/pull/5419/files#diff-cc0fb6b25cb6dad14226e6daa7b1819a859f6c9e5aa102d9557c52b2db485f67R173) that are also super complex.

I want also to avoid mixing async/await logic on effects, etc., as much as possible. Otherwise, we're creating way too many side-effects.

TL;DR I will find it hard to approve this PR with this Component as it's way too complex. It will on-the-go scare anyone to modify/contribute to this component and it is massively hard to read and understand what it is doing 😅

Copy link
Member

Choose a reason for hiding this comment

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

I would also mention that the SearchBar component should probably be broken down in 3 components:

  • Search Container
    • This is the actual wrapper where you type your query
  • Search Results
    • The container that renders conditionally when there are results
  • Search Result / Search Entry
    • The individual element for each result can have different styles

I would also say that SearchResults would have the reference of what is the current highlighted result and handle the keystrokes from going up and down.

And Search Container would handle the keystrokes for expanding/collapsing the search container.

  • on that, I would also say we should make our key-stroke hook very generic once we implement it. But again, all keystroke logic can be done on a follow-up PR.

Copy link
Author

Choose a reason for hiding this comment

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

Ok, I'll try to split components as you suggested.
Will follow up on this I hope within this week.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah I think by doing that, and simplifying the logic and removing everything that is not necessary we make this whole solution way easier.

Copy link
Member

Choose a reason for hiding this comment

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

But let’s also remove unnecessary callbacks, effects and try to simplify them as much as possible. I haven’t gone through the whole component but I guess there’s room for improvements there.

Copy link
Member

Choose a reason for hiding this comment

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

Let's remove this hook from now. It is complex, and the code quality is arguable.

We can think about accessibility on a second pass for this PR. Let's get the basics done, and then we think about invoking the search bar with keystrokes or moving through entries with keystrokes.

@@ -0,0 +1,12 @@
export type SearchResult = {
Copy link
Member

Choose a reason for hiding this comment

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

Usually, on the types folder we use interfaces whenever possible. So this one should be an interface.

@ovflowd
Copy link
Member

ovflowd commented Jul 13, 2023

Hey @ShogunPanda have you had some time to follow-up on this? Do you need any assistance! Thanks :3

@ShogunPanda
Copy link
Author

Nope, I don't need any help at the moment.
I plan to to work on this next week. I hope to have some updates soon!

@ShogunPanda ShogunPanda closed this by deleting the head repository Aug 4, 2023
@ShogunPanda
Copy link
Author

I mistakenly deleted my fork. Will send a new PR (which fixes all the suggestions outlined here) soon. Sorry for the mess.

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.

6 participants