Skip to content
This repository was archived by the owner on Jan 22, 2019. It is now read-only.

Commit 189cf30

Browse files
author
Stephen Gutekanst
authored
fix: command palette (#263)
A regression (#262) which broke the command palette was introduced (but not published). This fixes that regression. - The regression was introduced by @sqs's change to e-c-c here: sourcegraph/extensions-client-common@19df39f - The dependency was updated in this repository by @chrismwendt here: 8616d66 The issue is that we are using a `@shopify/react-shortcuts` package to declare keyboard shortcuts in e-c-c. The package registers keyboard shortcuts via a `<Shortcut>` component which requires that a `<ShortcutProvider>` component be somewhere above it in the React rendering tree, in order to pass down some React context. In the Sourcegraph web application, we provide such an implementation: https://sourcegraph.sgdev.org/github.com/sourcegraph/sourcegraph/-/blob/src/SourcegraphWebApp.tsx#L259:14 Here in the browser extension, however, we do not -- and this causes any `<Shortcut>` component to break due to missing React context data. If we were to publish the browser extension today, the Sourcegraph Extension command palette would be broken outright (clicking on it causes it to disappear). Fixing the issue is tricky: `@shopify/react-shortcuts`'s `ShortcutProvider` component is designed with the assumption that there will only ever be a single React component rendering the entire application. It uses this assumption to only register a single document keyboard event handler, [otherwise it will install multiple global event handlers to the `document` that conflict with one another](https://github.com/Shopify/quilt/blob/master/packages/react-shortcuts/src/ShortcutManager/ShortcutManager.tsx#L21). In most applications, this assumption holds true, but in our situation here with the browser extension this is not true as we inject independent React views into several different locations on the page and multiple expect to be able to add `<Shortcut>`s. Fixing this becomes more tricky because `@shopify/react-shortcuts` does not expose [the `Provider` part of the `React.createContext` result](https://github.com/Shopify/quilt/blob/master/packages/react-shortcuts/src/ShortcutProvider/ShortcutProvider.tsx#L12) -- meaning it is impossible for us to implement our own `ShortcutProvider` that fixes this issue. I've had to temporarily fork react-shortcuts to expose this, and if all looks good I will create a PR upstream for this: https://github.com/slimsag/quilt/tree/publish-provider In the meantime, we will have to use my fork everywhere (if we don't, React context will not grab the right data and it'll e.g. break the command palette on Sourcegraph.com). This does not actually cause us to register shortcuts in the browser extension for the command palette -- but we certainly can now after this change. See also: - sourcegraph/extensions-client-common#60 - https://github.com/sourcegraph/sourcegraph/pull/500 Fixes #262
1 parent 5ab0915 commit 189cf30

File tree

6 files changed

+78
-17
lines changed

6 files changed

+78
-17
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
"worker-loader": "^2.0.0"
112112
},
113113
"dependencies": {
114+
"@slimsag/react-shortcuts": "^1.2.1",
114115
"@sourcegraph/codeintellify": "^3.9.0",
115116
"@sourcegraph/extensions-client-common": "^10.2.2",
116117
"@sourcegraph/react-loading-spinner": "0.0.6",

src/libs/code_intelligence/extensions.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import * as H from 'history'
2525
import { isErrorLike } from '../../shared/backend/errors'
2626
import { createExtensionsContextController, createMessageTransports } from '../../shared/backend/extensions'
2727
import { GlobalDebug } from '../../shared/components/GlobalDebug'
28+
import { ShortcutProvider } from '../../shared/components/ShortcutProvider'
2829
import { sourcegraphUrl } from '../../shared/util/context'
2930
import { getGlobalDebugMount } from '../github/extensions'
3031
import { MountGetter } from './code_intelligence'
@@ -111,11 +112,13 @@ export function initializeExtensions(
111112
const { extensionsContextController, extensionsController } = createControllers(documents)
112113

113114
render(
114-
<CommandListPopoverButton
115-
extensionsController={extensionsController}
116-
menu={ContributableMenu.CommandPalette}
117-
extensions={extensionsContextController}
118-
/>,
115+
<ShortcutProvider>
116+
<CommandListPopoverButton
117+
extensionsController={extensionsController}
118+
menu={ContributableMenu.CommandPalette}
119+
extensions={extensionsContextController}
120+
/>
121+
</ShortcutProvider>,
119122
getCommandPaletteMount()
120123
)
121124

src/libs/github/extensions.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ContributableMenu } from 'sourcegraph/module/protocol'
88
import * as React from 'react'
99
import { render } from 'react-dom'
1010
import { GlobalDebug } from '../../shared/components/GlobalDebug'
11+
import { ShortcutProvider } from '../../shared/components/ShortcutProvider'
1112

1213
export function getCommandPaletteMount(): HTMLElement {
1314
const headerElem = document.querySelector('div.HeaderMenu>div:last-child')
@@ -51,11 +52,13 @@ export function injectExtensionsGlobalComponents({
5152
extensionsContextController: Controller<ConfigurationSubject, Settings>
5253
}): void {
5354
render(
54-
<CommandListPopoverButton
55-
extensionsController={extensionsController}
56-
menu={ContributableMenu.CommandPalette}
57-
extensions={extensionsContextController}
58-
/>,
55+
<ShortcutProvider>
56+
<CommandListPopoverButton
57+
extensionsController={extensionsController}
58+
menu={ContributableMenu.CommandPalette}
59+
extensions={extensionsContextController}
60+
/>
61+
</ShortcutProvider>,
5962
getCommandPaletteMount()
6063
)
6164

src/shared/components/GlobalDebug.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as H from 'history'
66
import MenuDownIcon from 'mdi-react/MenuDownIcon'
77
import * as React from 'react'
88
import { sourcegraphUrl } from '../util/context'
9+
import { ShortcutProvider } from './ShortcutProvider'
910

1011
interface Props {
1112
location: H.Location
@@ -28,13 +29,17 @@ export const GlobalDebug: React.SFC<Props> = props =>
2829
<div className="global-debug navbar navbar-expand">
2930
<ul className="navbar-nav align-items-center">
3031
<li className="nav-item">
31-
<ExtensionStatusPopover
32-
location={props.location}
33-
loaderIcon={LoadingSpinner as React.ComponentType<{ className: string; onClick?: () => void }>}
34-
caretIcon={MenuDownIcon as React.ComponentType<{ className: string; onClick?: () => void }>}
35-
extensionsController={props.extensionsController}
36-
link={ExtensionLink}
37-
/>
32+
<ShortcutProvider>
33+
<ExtensionStatusPopover
34+
location={props.location}
35+
loaderIcon={
36+
LoadingSpinner as React.ComponentType<{ className: string; onClick?: () => void }>
37+
}
38+
caretIcon={MenuDownIcon as React.ComponentType<{ className: string; onClick?: () => void }>}
39+
extensionsController={props.extensionsController}
40+
link={ExtensionLink}
41+
/>
42+
</ShortcutProvider>
3843
</li>
3944
</ul>
4045
</div>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { Context, ContextProvider, ProviderProps, ShortcutManager } from '@slimsag/react-shortcuts'
2+
import * as React from 'react'
3+
4+
/**
5+
* Describes the variable this file injects into the `global` object. It is
6+
* heavily prefixed to avoid collisions.
7+
*/
8+
interface GlobalContext {
9+
/** The singleton ShortcutManager object. */
10+
browserExtensionShortcutManager?: ShortcutManager
11+
}
12+
13+
// This ShortcutProvider is derived from the default @shopify/react-shortcuts
14+
// implementation:
15+
//
16+
// https://github.com/Shopify/quilt/blob/master/packages/react-shortcuts/src/ShortcutProvider/ShortcutProvider.tsx
17+
//
18+
// We cannot use the default implementation above because it assumes the
19+
// application is rendered via a single React component in order to create the
20+
// ShortcutManager singleton. In our case, there are multiple React components
21+
// on the page and they each need to use a single ShortcutManager, so we must
22+
// manage it ourselves here. If we did not do this, we would have multiple
23+
// ShortcutManagers and each would register their own conflicting document
24+
// event handlers.
25+
export class ShortcutProvider extends React.Component<ProviderProps, never> {
26+
public componentDidMount(): void {
27+
const globals = global as GlobalContext
28+
if (!globals.browserExtensionShortcutManager) {
29+
globals.browserExtensionShortcutManager = new ShortcutManager()
30+
globals.browserExtensionShortcutManager.setup()
31+
}
32+
}
33+
34+
public render(): JSX.Element | null {
35+
const globals = global as GlobalContext
36+
const context: Context = {
37+
shortcutManager: globals.browserExtensionShortcutManager,
38+
}
39+
40+
return <ContextProvider value={context}>{this.props.children}</ContextProvider>
41+
}
42+
}

yarn.lock

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,13 @@
13061306
dependencies:
13071307
prop-types "^15.6.2"
13081308

1309+
"@slimsag/react-shortcuts@^1.2.1":
1310+
version "1.2.1"
1311+
resolved "https://registry.yarnpkg.com/@slimsag/react-shortcuts/-/react-shortcuts-1.2.1.tgz#a3ea054a057137de8636bc9fabac61369103e597"
1312+
integrity sha512-dUTQjBX1yjnHbqkTL5ditqtx5R6QhcXI3lCnjOy8e5XUOr5LOtVpol1mufU2lAb7DtEYrSVRQpUeNmZrTEgLcg==
1313+
dependencies:
1314+
prop-types "^15.6.2"
1315+
13091316
"@sourcegraph/codeintellify@^3.9.0":
13101317
version "3.9.0"
13111318
resolved "https://registry.yarnpkg.com/@sourcegraph/codeintellify/-/codeintellify-3.9.0.tgz#1e3f002558cc367e8c5ee9b0a54e0c47c6ee8761"

0 commit comments

Comments
 (0)