From 9a9d9fe5eb4fef10e304477fd7a95dcb99d84402 Mon Sep 17 00:00:00 2001 From: Joseph Axisa Date: Thu, 14 Oct 2021 10:04:52 +0100 Subject: [PATCH] chore: make environment adaptor globally available (#855) * make env adaptor globally available - eliminates the need for the envAdaptor context - Paves the way for envAdaptor usage in sagas * move env adaptor registration into useEffect ensures registration happens after mounting, during the initialization phase, and not on every rerender --- packages/api-explorer/src/ApiExplorer.tsx | 42 ++++++++++++------- .../components/DocMarkdown/DocMarkdown.tsx | 5 ++- .../SelectorContainer/SdkLanguageSelector.tsx | 7 ++-- .../context/envAdaptor/EnvAdaptorContext.ts | 39 ----------------- packages/api-explorer/src/context/index.ts | 1 - .../index.ts => test-utils/envAdaptor.tsx} | 10 +++-- packages/api-explorer/src/test-utils/index.ts | 1 + .../api-explorer/src/test-utils/redux.tsx | 26 +++--------- .../src/test-utils/render_with_lode.tsx | 8 +--- packages/api-explorer/src/utils/envAdaptor.ts | 26 ++++++++++++ 10 files changed, 72 insertions(+), 93 deletions(-) delete mode 100644 packages/api-explorer/src/context/envAdaptor/EnvAdaptorContext.ts rename packages/api-explorer/src/{context/envAdaptor/index.ts => test-utils/envAdaptor.tsx} (81%) diff --git a/packages/api-explorer/src/ApiExplorer.tsx b/packages/api-explorer/src/ApiExplorer.tsx index 35b3be517..5ff52a87b 100644 --- a/packages/api-explorer/src/ApiExplorer.tsx +++ b/packages/api-explorer/src/ApiExplorer.tsx @@ -32,15 +32,16 @@ import { Aside, ComponentsProvider, Layout, Page } from '@looker/components' import type { SpecList } from '@looker/sdk-codegen' import type { RunItSetter } from '@looker/run-it' import { funFetch, fallbackFetch, OAuthScene } from '@looker/run-it' -import { - SearchContext, - LodeContext, - defaultLodeContextValue, - EnvAdaptorContext, -} from './context' +import { SearchContext, LodeContext, defaultLodeContextValue } from './context' import type { IApixEnvAdaptor } from './utils' -import { EnvAdaptorConstants, getLoded, oAuthPath } from './utils' -import { Header, SideNav, ErrorBoundary } from './components' +import { + EnvAdaptorConstants, + getLoded, + oAuthPath, + registerEnvAdaptor, + unregisterEnvAdaptor, +} from './utils' +import { Header, SideNav, ErrorBoundary, Loader } from './components' import { specReducer, initDefaultSpecState, @@ -71,10 +72,10 @@ const ApiExplorer: FC = ({ declarationsLodeUrl = `${apixFilesHost}/declarationsIndex.json`, headless = false, }) => { + const [initializing, setInitializing] = useState(true) const location = useLocation() const { setSdkLanguageAction } = useActions() const oauthReturn = location.pathname === `/${oAuthPath}` - const [specState, specDispatch] = useReducer( specReducer, initDefaultSpecState(specs, location) @@ -97,6 +98,13 @@ const ApiExplorer: FC = ({ } }, []) + useEffect(() => { + registerEnvAdaptor(envAdaptor) + setInitializing(false) + + return () => unregisterEnvAdaptor() + }, []) + useEffect(() => { if (headless) { window.addEventListener('message', hasNavigationToggle) @@ -144,16 +152,18 @@ const ApiExplorer: FC = ({ initSdkLanguage() }, [envAdaptor, setSdkLanguageAction]) - const { loadGoogleFonts, themeCustomizations } = envAdaptor.themeOverrides() + const themeOverrides = envAdaptor.themeOverrides() return ( <> - - + {initializing ? ( + + ) : ( + = ({ - - + + )} {!headless && } diff --git a/packages/api-explorer/src/components/DocMarkdown/DocMarkdown.tsx b/packages/api-explorer/src/components/DocMarkdown/DocMarkdown.tsx index b8bc553e0..7707726b6 100644 --- a/packages/api-explorer/src/components/DocMarkdown/DocMarkdown.tsx +++ b/packages/api-explorer/src/components/DocMarkdown/DocMarkdown.tsx @@ -28,7 +28,8 @@ import type { FC } from 'react' import React, { useContext } from 'react' import { useHistory } from 'react-router-dom' import { Markdown } from '@looker/code-editor' -import { EnvAdaptorContext, SearchContext } from '../../context' +import { SearchContext } from '../../context' +import { getEnvAdaptor } from '../../utils' import { transformURL } from './utils' interface DocMarkdownProps { @@ -37,7 +38,6 @@ interface DocMarkdownProps { } export const DocMarkdown: FC = ({ source, specKey }) => { - const { envAdaptor } = useContext(EnvAdaptorContext) const { searchSettings: { pattern }, } = useContext(SearchContext) @@ -50,6 +50,7 @@ export const DocMarkdown: FC = ({ source, specKey }) => { } else if (url.startsWith(`/${specKey}`)) { history.push(url) } else if (url.startsWith('https://')) { + const envAdaptor = getEnvAdaptor() envAdaptor.openBrowserWindow(url) } } diff --git a/packages/api-explorer/src/components/SelectorContainer/SdkLanguageSelector.tsx b/packages/api-explorer/src/components/SelectorContainer/SdkLanguageSelector.tsx index 96e6ece1a..fef38afe1 100644 --- a/packages/api-explorer/src/components/SelectorContainer/SdkLanguageSelector.tsx +++ b/packages/api-explorer/src/components/SelectorContainer/SdkLanguageSelector.tsx @@ -24,7 +24,7 @@ */ import type { FC } from 'react' -import React, { useContext } from 'react' +import React from 'react' import { codeGenerators } from '@looker/sdk-codegen' import { Select } from '@looker/components' import { useSelector } from 'react-redux' @@ -32,8 +32,7 @@ import type { SelectOptionProps } from '@looker/components' import { useActions } from '../../hooks' import { getSelectedSdkLanguage } from '../../state' -import { EnvAdaptorContext } from '../../context' -import { EnvAdaptorConstants } from '../../utils' +import { EnvAdaptorConstants, getEnvAdaptor } from '../../utils' /** * Allows the user to select their preferred SDK language @@ -41,7 +40,6 @@ import { EnvAdaptorConstants } from '../../utils' export const SdkLanguageSelector: FC = () => { const { setSdkLanguageAction } = useActions() const selectedSdkLanguage = useSelector(getSelectedSdkLanguage) - const { envAdaptor } = useContext(EnvAdaptorContext) const allSdkLanguages: SelectOptionProps[] = codeGenerators.map((gen) => ({ value: gen.language, @@ -57,6 +55,7 @@ export const SdkLanguageSelector: FC = () => { const handleChange = (language: string) => { setSdkLanguageAction(language) + const envAdaptor = getEnvAdaptor() envAdaptor.localStorageSetItem( EnvAdaptorConstants.LOCALSTORAGE_SDK_LANGUAGE_KEY, language diff --git a/packages/api-explorer/src/context/envAdaptor/EnvAdaptorContext.ts b/packages/api-explorer/src/context/envAdaptor/EnvAdaptorContext.ts deleted file mode 100644 index c98694cee..000000000 --- a/packages/api-explorer/src/context/envAdaptor/EnvAdaptorContext.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - - MIT License - - Copyright (c) 2021 Looker Data Sciences, Inc. - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - */ -import { createContext } from 'react' -import type { IApixEnvAdaptor } from '../../utils' -import { StandaloneEnvAdaptor } from '../../utils' - -export interface EnvAdaptorContextProps { - envAdaptor: IApixEnvAdaptor -} - -export const defaultEnvAdaptorContextValue: EnvAdaptorContextProps = { - envAdaptor: new StandaloneEnvAdaptor(), -} -export const EnvAdaptorContext = createContext( - defaultEnvAdaptorContextValue -) diff --git a/packages/api-explorer/src/context/index.ts b/packages/api-explorer/src/context/index.ts index 10d7b6d2d..781c60f44 100644 --- a/packages/api-explorer/src/context/index.ts +++ b/packages/api-explorer/src/context/index.ts @@ -25,4 +25,3 @@ */ export { LodeContext, defaultLodeContextValue } from './lode' export { SearchContext, defaultSearchContextValue } from './search' -export { EnvAdaptorContext, defaultEnvAdaptorContextValue } from './envAdaptor' diff --git a/packages/api-explorer/src/context/envAdaptor/index.ts b/packages/api-explorer/src/test-utils/envAdaptor.tsx similarity index 81% rename from packages/api-explorer/src/context/envAdaptor/index.ts rename to packages/api-explorer/src/test-utils/envAdaptor.tsx index f09791dba..fe1972faf 100644 --- a/packages/api-explorer/src/context/envAdaptor/index.ts +++ b/packages/api-explorer/src/test-utils/envAdaptor.tsx @@ -23,7 +23,9 @@ SOFTWARE. */ -export { - defaultEnvAdaptorContextValue, - EnvAdaptorContext, -} from './EnvAdaptorContext' +import type { IApixEnvAdaptor } from '../utils' +import { registerEnvAdaptor, StandaloneEnvAdaptor } from '../utils' + +export const registerTestEnvAdaptor = (envAdaptor?: IApixEnvAdaptor) => { + registerEnvAdaptor(envAdaptor || new StandaloneEnvAdaptor()) +} diff --git a/packages/api-explorer/src/test-utils/index.ts b/packages/api-explorer/src/test-utils/index.ts index 0a0870450..8ba3a5821 100644 --- a/packages/api-explorer/src/test-utils/index.ts +++ b/packages/api-explorer/src/test-utils/index.ts @@ -37,3 +37,4 @@ export { renderWithReduxProvider, withReduxProvider, } from './redux' +export { registerTestEnvAdaptor } from './envAdaptor' diff --git a/packages/api-explorer/src/test-utils/redux.tsx b/packages/api-explorer/src/test-utils/redux.tsx index 28090b3f9..d5df034de 100644 --- a/packages/api-explorer/src/test-utils/redux.tsx +++ b/packages/api-explorer/src/test-utils/redux.tsx @@ -32,43 +32,29 @@ import type { RenderOptions } from '@testing-library/react' import type { RootState } from '../state' import { configureStore } from '../state' -import type { IApixEnvAdaptor } from '../utils' -import { StandaloneEnvAdaptor } from '../utils' -import { EnvAdaptorContext } from '../context' +import { registerEnvAdaptor, StandaloneEnvAdaptor } from '../utils' import { renderWithRouter } from '.' const defaultStore = configureStore() export const withReduxProvider = ( consumers: ReactElement, - store: Store = defaultStore, - envAdaptor: IApixEnvAdaptor = new StandaloneEnvAdaptor() + store: Store = defaultStore ) => { - return ( - - - {consumers} - - - ) + registerEnvAdaptor(new StandaloneEnvAdaptor()) + return {consumers} } export const renderWithReduxProvider = ( consumers: ReactElement, store?: Store, - envAdaptor?: IApixEnvAdaptor, options?: Omit -) => renderWithTheme(withReduxProvider(consumers, store, envAdaptor), options) +) => renderWithTheme(withReduxProvider(consumers, store), options) export const renderWithRouterAndReduxProvider = ( consumers: ReactElement, initialEntries: string[] = ['/'], store?: Store, - envAdaptor?: IApixEnvAdaptor, options?: Omit ) => - renderWithRouter( - withReduxProvider(consumers, store, envAdaptor), - initialEntries, - options - ) + renderWithRouter(withReduxProvider(consumers, store), initialEntries, options) diff --git a/packages/api-explorer/src/test-utils/render_with_lode.tsx b/packages/api-explorer/src/test-utils/render_with_lode.tsx index 78cdc3294..440364352 100644 --- a/packages/api-explorer/src/test-utils/render_with_lode.tsx +++ b/packages/api-explorer/src/test-utils/render_with_lode.tsx @@ -33,7 +33,6 @@ import { renderWithTheme } from '@looker/components-test-utils' import type { IDeclarationMine, IExampleMine } from '@looker/sdk-codegen' import { LodeContext } from '../context' import type { RootState } from '../state' -import type { IApixEnvAdaptor } from '../utils' import { withReduxProvider } from './redux' const withLode = ( @@ -62,14 +61,9 @@ export const renderWithReduxProviderAndLode = ( examples: IExampleMine, declarations?: IDeclarationMine, store?: Store, - envAdaptor?: IApixEnvAdaptor, options?: Omit ) => renderWithTheme( - withReduxProvider( - withLode(component, examples, declarations), - store, - envAdaptor - ), + withReduxProvider(withLode(component, examples, declarations), store), options ) diff --git a/packages/api-explorer/src/utils/envAdaptor.ts b/packages/api-explorer/src/utils/envAdaptor.ts index 0137b7709..4d96f9a62 100644 --- a/packages/api-explorer/src/utils/envAdaptor.ts +++ b/packages/api-explorer/src/utils/envAdaptor.ts @@ -118,3 +118,29 @@ export class StandaloneEnvAdaptor implements IApixEnvAdaptor { export enum EnvAdaptorConstants { LOCALSTORAGE_SDK_LANGUAGE_KEY = 'sdkLanguage', } + +let envAdaptor: IApixEnvAdaptor | undefined + +/** + * Register the environment adaptor. The API Explorer will automatically call this. + */ +export const registerEnvAdaptor = (adaptor: IApixEnvAdaptor) => { + envAdaptor = adaptor +} + +/** + * Unregister the envAdaptor. The API Explorer will automatically call this when it is unmounted. + */ +export const unregisterEnvAdaptor = () => { + envAdaptor = undefined +} + +/** + * Global access to the envAdaptor. An error will be thrown if accessed prematurely. + */ +export const getEnvAdaptor = () => { + if (!envAdaptor) { + throw new Error('Environment adaptor not initialized.') + } + return envAdaptor +}