-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Create search and search-ui packages #29773
Conversation
a109d42
to
a9a92f2
Compare
// Implemented in /web as navbar query state, /vscode as webview query state. | ||
export interface SearchQueryState { |
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 is this? The interface for useNavbarQueryState
store pulled out to be shared with the VS Code extension.
I implemented a separate zustand store (ignore the fact that it's instantiated in a component, it's a temporary hack :) ) in the VS Code prototype and it seems to work well!
@@ -0,0 +1,16 @@ | |||
export enum SectionID { |
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.
Temporary settings must be defined here to prevent circular dependency (search-ui <-> shared). I tried some more flexible solutions, like making the temporary settings schema generic, but it didn't seem to worth the effort to makes the types work (and kind of defeats the purpose of autocomplete knowing about all the possible temporary settings keys)
@@ -224,7 +228,7 @@ export const SearchSidebar: React.FunctionComponent<SearchSidebarProps> = props | |||
|
|||
return ( | |||
<div className={classNames(styles.searchSidebar, props.className)}> | |||
{props.showOnboardingTour && <OnboardingTour className="mb-1" telemetryService={props.telemetryService} />} |
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.
The onboarding tour doesn't make sense for VS Code yet, so SearchSidebar
shouldn't depend on it
@@ -49,16 +52,18 @@ export interface SearchSidebarProps | |||
* Content to render inside sidebar, but before other sections. | |||
*/ | |||
prefixContent?: JSX.Element | |||
|
|||
buildSearchURLQueryFromQueryState: (queryParameters: BuildSearchQueryURLParameters) => string |
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.
The current implementation references the zustand store directly, so it should be passed as a prop by each client
Notifying subscribers in CODENOTIFY files for diff c7f9aea...2b6580f.
|
/** | ||
* Zustand store. Passed as a prop because there may be different global stores across clients | ||
* (e.g. VS Code extension, web app), so the sidebar expects a store with the minimal interface | ||
* for search. | ||
*/ | ||
useQueryState: UseStore<SearchQueryState> |
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'll need to do a more thorough review, but some quick initial thoughts:
Somehow passing a store/hook as prop feels weird to me. If possible, I feel like refactoring the component to accept whatever they get from the store as props, and introducing "container" components that then implement/provide this as necessary (e.g. one of the the extension and one for the web app) would be cleaner. But maybe I'm overthinking this.
Eventually we should think about how to more elegantly manage our zustand stores across clients
Contexts could help with that. We tried to use the zustand stores via context, but their type definition was difficult to get right. But alternatively we could pass the stores themselves via context (those references won't change anyway). This could also be combined with my previous statement. Curious what @sourcegraph/frontend-platform thinks.
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.
@fkling, it seems we can benefit from using zustand stores via context. Is it possible that type definition issues are solved in the new version? Maybe, if it's a couple of hours of effort, it makes sense to fix them ourselves in the third party? It will make the usage via context easier to understand since it will follow zustand documentation.
In general, I agree that it would be great to avoid prop drilling and rely more on React context, especially for cross-package depedencies.
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.
Somehow passing a store/hook as prop feels weird to me. If possible, I feel like refactoring the component to accept whatever they get from the store as props, and introducing "container" components that then implement/provide this as necessary (e.g. one of the the extension and one for the web app) would be cleaner. But maybe I'm overthinking this.
This is a good solution because the reason I didn't do it in the first place was that I un-creatively only considered the current parent as a candidate for lifting the zustand subscription to, which would be counterproductive performance-wise. I'll try the context approach first and fall back to the container approach if it takes too long. Thank you both!
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.
FWIW, I'm OK with having a less optimal solution now and then clean this up. While you can absolutely try context, I feel like this would be better in a separate PR.
And just to clarify what I and Valery meant (because we meant different things 😆 ): I thought that passing down the store functions (e.g. useNavbarQueryState
) via context would be an OK compromise, but it does make it a bit awkward to use (don't actually know the context API off the top of my head but something along these lines:
const useNavbarQueryState = useContext(...)
const {state} = useNavbarQueryState(...)
which is not that common).
Zustand offers proper context support and the nice thing about it that you wouldn't even have to update the components that use the stores (afaik). I think you could get away with just updating the implementation of the stores.
However, there are couple of things to consider:
- In the past we argued that having multiple contexts (one per store) isn't desirable. At the same time I think that merging everything into a single store isn't great either. A compromise could be that we have a single store for "individual" single global values (theme, isSourcegraphDotCom, etc) and a separate store for more complex state (search query state).
- For search query state, I moved a lot of the "action" functions from the store the module level (example). I liked that because components didn't have to subscribe to the store just to get a value (the function) that never changes. But I don't think that would work with context anymore and they would have to be moved back to the store. I'm ok with that but it's additional work.
Not sure how we should best coordinate all that 😅
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 went off and basically implemented what seems to be Valery's proposal before I read this comment 😅. I can do it the zustand way, just wondering now that we have it, does it still feel worth moving to a separate PR? I thought this was a change with limited scope since only search
package components would have to worry about this, but I see that it would open a can of worms with the multiple contexts issue...
For search query state, I moved a lot of the "action" functions from the store the module level (example). I liked that because components didn't have to subscribe to the store just to get a value (the function) that never changes. But I don't think that would work with context anymore and they would have to be moved back to the store. I'm ok with that but it's additional work.
These are used only in web
since <SearchBox>
takes these as props, which is helpful since it means <SearchSidebar>
is the only shared (search
) component that directly references the zustand store. So it's not a problem we have to tackle yet? I don't see it as an issue that web helpers reference the web store directly
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 can do it the zustand way, just wondering now that we have it, does it still feel worth moving to a separate PR?
Yeah, probably not worth it to do in a separate PR. I thought it might be weird to have some stores use context and not others, but maybe we shouldn't be dogmatic about this and use whether makes sense for that specific situations.
I know I proposed this, but seeing the store passed via context is weird too 😆 But it's OK for me if you want to keep it that way for now. I can also migrate it the "zustand way" at some point. I also feel like we might need to have a proper discussion again how we want to tackle this overall, outside of this specific PR.
which is helpful since it means
<SearchSidebar>
is the only shared (search) component that directly references the zustand store
Ah... I somehow didn't realize which components are actually all affected by this.
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.
Got it, I opened an issue (#29984) to track this tech debt
@tjkandala, thank you for extracting it into a separate PR! 💜 I will look into it by the end of tomorrow. Vova, will this work create major merge conflicts? Does it make sense for you to sync with @tjkandala on it? |
To make matters worse, I'm also working on a bigger refactor that adds quite a bit of code to the query state store: https://github.com/sourcegraph/sourcegraph/pull/29825 . We'll have to coordinate this somehow. |
@tjkandala If you're interested in new Monaco field components, they still live in code insights shared components. You can find them here and live demo here So there are no other places where it's used yet. But when I get time I'm gonna implement the rest of the needed UI for Monaco and reduce the number of abstractions over the Monaco input component. (at the moment we have at least 3 places where we use different implementations of Monaco - search page, notebook fields, code insights creation UI) |
@fkling At the risk of sounding naive, I don't think our PRs conflict too much conceptually? Would be a pretty big merge conflict though so coordination is important. I will have some PRs in the near future that would also ideally build off of yours (in the prototype I added lots of custom code for global search context state; your PR would make it a lot easier). If we think this is mergable, how about this order:
@vovakulikov Looks great! So there won't likely be merge conflicts, right? |
a5ade72
to
bc49e0e
Compare
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 the right approach to me. Left some minor comments and will defer to @fkling re: state management, but otherwise LGTM!
@@ -0,0 +1 @@ | |||
export * from './SyntaxHighlightedSearchQuery' |
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 not sure this is related to search UI in particular. This is used in multiple places in the product, not just search UI. I think this component would live better in shared
.
@@ -14,6 +14,7 @@ interface ModalVideoProps { | |||
onToggle?: (isOpen: boolean) => void | |||
showCaption?: boolean | |||
className?: string | |||
assetsRoot?: string | |||
} | |||
|
|||
export const ModalVideo: React.FunctionComponent<ModalVideoProps> = ({ |
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 component also seems to me more apt for shared
than search-ui
@@ -1,7 +1,6 @@ | |||
import { Optional } from 'utility-types' | |||
|
|||
import { SectionID as NoResultsSectionID } from '../../search/results/NoResultsPage' | |||
import { SectionID } from '../../search/results/sidebar/SearchSidebar' | |||
import { SectionID, NoResultsSectionID } from './searchSidebar' |
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 wonder if now that we'll have have temporary settings in shared
if we could easily plug them into the browser and editor extensions for even better settings sync
It's correct. We just need to take into account that there will be a new place where we store our existing search box Monaco abstraction/components (I mean a newly created search-ui package in this PR) if we start Monaco migration before this PR got merged but I suspect we will start doing something with Monaco later and after this PR is merged, so no conflicts |
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.
Looks great! Thank you for taking an iterative approach and instead of moving to the shared
package, creating a couple of workspaces with clearer purpose 🙌
Agree with @limitedmage that some components are not specific only to the search UI, but we can iterate on it in follow-up PRs since we tearing the shared
package apart anyways.
@@ -1,19 +1,18 @@ | |||
import classNames from 'classnames' | |||
import React from 'react' | |||
|
|||
import { ErrorAlert } from '@sourcegraph/branded/src/components/alerts' |
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.
Looks like a great candidate to be moved to the Wildcard
package. We can work on it in a follow-up PR.
Thanks @valerybugakov and @limitedmage! I'll address the issues regarding ideal place for certain files to live in follow-up PRs. I'll merge on approval on state management strategy (and coordinating PRs) from @fkling! |
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.
Yep, merge this PR and I'll rebase my PR 🙂
a53cd3c
to
fcf8f02
Compare
SearchUserNeedsCodeHost depends on code (currently) unsupported on VS Code (Apollo Client)
The web implementation of this function directly references the zustand store
Unblocks SearchSidebar move, along with some other import changes
Co-authored-by: Juliana Peña <juliana@sourcegraph.com>
fcf8f02
to
2b6580f
Compare
Co-authored-by: Juliana Peña <juliana@sourcegraph.com>
Co-authored-by: Juliana Peña <juliana@sourcegraph.com>
Closes #29705.
Motivation
One goal of v1 of the new Sourcegraph VS Code extension is to provide a familiar experience for existing Sourcegraph users in their IDEs, which also makes the potential transition to using Sourcegraph.com easier for new users (acquired through the new VS Code extension). The easiest way to make that happen is to use the same components as the web application.
To that end, this PR introduces two new packages in order to share essential search code between client applications.
New Packages
QueryState
. Can be used in any client context (e.g. web app, browser extension, CLIs).For reviewers
This PR is best reviewed by commit. I've done my best to make sure each commit is simple and that the code is in a working state. The overall diff is very misleading in terms of complexity; the vast majority of changes are updates to imports and stuff like making components accept
PlatformContext
in order to abstract GQL requests. The build is passing and search features check out in manual testing. However, we can split this into multiple PRs if that would increase confidence!Changes
<SearchBox>
(really all ofweb/src/search/input
) tosearch-ui
CurrentAuthState
query, exportAuthenticatedUser
type alias (does this belong in a separate auth package?)web/src/keyboardShortcuts
toshared/src/keyboardShortcuts
isMacPlatform
,lazyComponent
, andobserveResize
fromweb/src/util
to be moved toshared/src/util
web/src/search/useQueryIntelligence
tosearch/src/useQueryIntelligence
. This only depends on stuff already inshared
(moving tosearch
)<MonacoEditor>
fromweb/src/components
toshared/src/components
. This only depends on stuff already inshared
(moving tosearch
)web/src/search/helpers.tsx
to move tosearch
, notsearch-ui
shared
(needed for quick links and<FilterLink>
)shared
web/src/search/backend.tsx
tosearch
SyntaxHighlightedSearchQuery
tosearch-ui
<StreamingSearchResultsList>
tosearch-ui
. We could just move<StreamingSearchResults>
, but that component is too intertwined with web stuff (e.g. code insights, search stack) so I think it makes more sense to pull out the list component and other shared components (likeSearchAlert
) and assemble an independent VSCE search results page. Additionally,<StreamingSearchResults>
renders the search sidebar, whereas we want to render that in a VS Code sidebar webview.<ModalVideo>
tosearch-ui
<ErrorAlert>
tobranded
(maybewildcard
?)<SearchResult>
and<CommitSearchResultMatch>
tosearch-ui
highlightCode
fromweb/src/search/backend.tsx
tosearch
web/src/user/settings/codeHosts/OrgUserNeedsCodeHost.tsx
a render prop (depends on Apollo Client which will not be implemented for VSCE v1)<NoResultsPage>
tosearch-ui
useExperimentalFeatures
(zustand store). Eventually we should think about how to more elegantly manage our zustand stores across clients<AnnotatedSearchInput>
tosearch-ui
<SearchSidebar>
tosearch-ui
as mentioned in the previous point.useNavbarQueryState
, so we should make a generalSearchQueryState
interface insearch
and pass the store instance as a prop.<Revisions>
sidebar section will stay inweb
until we get Apollo client working for VS Code webviews. Refactor<SearchSidebar>
to acceptgetRevisions
prop<OnboardingTour>
is web-only (for now?), so add aprefixContent
prop for web consumers to render it intoclient/web/src/search/helpers/queryExample.ts
tosearch
Candidates for moves in future PRs
shared/src/search/query
,shared/src/search/suggestions
, andshared/src/search/stream.ts
tosearch
<FileMatch>
tosearch-ui