Runtime: Add provider and access hook for location service (#90759)

pull/91400/head
Andrej Ocenas 12 months ago committed by GitHub
parent e39a131110
commit 04d8f0f063
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .betterer.results
  2. 15
      packages/grafana-runtime/src/services/LocationService.test.tsx
  3. 19
      packages/grafana-runtime/src/services/LocationService.tsx
  4. 54
      public/app/AppWrapper.tsx

@ -514,7 +514,7 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Unexpected any. Specify a different type.", "3"] [0, 0, 0, "Unexpected any. Specify a different type.", "3"]
], ],
"packages/grafana-runtime/src/services/LocationService.ts:5381": [ "packages/grafana-runtime/src/services/LocationService.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"], [0, 0, 0, "Unexpected any. Specify a different type.", "2"],

@ -1,4 +1,6 @@
import { locationService } from './LocationService'; import { renderHook } from '@testing-library/react-hooks';
import { locationService, HistoryWrapper, useLocationService, LocationServiceProvider } from './LocationService';
describe('LocationService', () => { describe('LocationService', () => {
describe('getSearchObject', () => { describe('getSearchObject', () => {
@ -51,4 +53,15 @@ describe('LocationService', () => {
}); });
}); });
}); });
describe('hook access', () => {
it('can set and access service from a context', () => {
const locationServiceLocal = new HistoryWrapper();
const wrapper: React.FunctionComponent<{ children: React.ReactNode }> = ({ children }) => (
<LocationServiceProvider service={locationServiceLocal}>{children}</LocationServiceProvider>
);
const hookResult = renderHook(() => useLocationService(), { wrapper });
expect(hookResult.result.current).toBe(locationServiceLocal);
});
});
}); });

@ -1,4 +1,5 @@
import * as H from 'history'; import * as H from 'history';
import React, { useContext } from 'react';
import { deprecationWarning, UrlQueryMap, urlUtil } from '@grafana/data'; import { deprecationWarning, UrlQueryMap, urlUtil } from '@grafana/data';
import { attachDebugger, createLogger } from '@grafana/ui'; import { attachDebugger, createLogger } from '@grafana/ui';
@ -162,3 +163,21 @@ export const navigationLogger = navigationLog.logger;
// For debugging purposes the location service is attached to global _debug variable // For debugging purposes the location service is attached to global _debug variable
attachDebugger('location', locationService, navigationLog); attachDebugger('location', locationService, navigationLog);
// Simple context so the location service can be used without being a singleton
const LocationServiceContext = React.createContext<LocationService | undefined>(undefined);
export function useLocationService(): LocationService {
const service = useContext(LocationServiceContext);
if (!service) {
throw new Error('useLocationService must be used within a LocationServiceProvider');
}
return service;
}
export const LocationServiceProvider: React.FC<{ service: LocationService; children: React.ReactNode }> = ({
service,
children,
}) => {
return <LocationServiceContext.Provider value={service}>{children}</LocationServiceContext.Provider>;
};

@ -4,7 +4,13 @@ import { Provider } from 'react-redux';
import { Router, Redirect, Switch, RouteComponentProps } from 'react-router-dom'; import { Router, Redirect, Switch, RouteComponentProps } from 'react-router-dom';
import { CompatRouter, CompatRoute } from 'react-router-dom-v5-compat'; import { CompatRouter, CompatRoute } from 'react-router-dom-v5-compat';
import { config, locationService, navigationLogger, reportInteraction } from '@grafana/runtime'; import {
config,
locationService,
LocationServiceProvider,
navigationLogger,
reportInteraction,
} from '@grafana/runtime';
import { ErrorBoundaryAlert, GlobalStyles, ModalRoot, PortalContainer, Stack } from '@grafana/ui'; import { ErrorBoundaryAlert, GlobalStyles, ModalRoot, PortalContainer, Stack } from '@grafana/ui';
import { getAppRoutes } from 'app/routes/routes'; import { getAppRoutes } from 'app/routes/routes';
import { store } from 'app/store/store'; import { store } from 'app/store/store';
@ -104,29 +110,31 @@ export class AppWrapper extends Component<AppWrapperProps, AppWrapperState> {
options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }} options={{ enableHistory: true, callbacks: { onSelectAction: commandPaletteActionSelected } }}
> >
<Router history={locationService.getHistory()}> <Router history={locationService.getHistory()}>
<CompatRouter> <LocationServiceProvider service={locationService}>
<ModalsContextProvider> <CompatRouter>
<GlobalStyles /> <ModalsContextProvider>
<div className="grafana-app"> <GlobalStyles />
<AppChrome> <div className="grafana-app">
<AngularRoot /> <AppChrome>
<AppNotificationList /> <AngularRoot />
<Stack gap={0} grow={1} direction="column"> <AppNotificationList />
{pageBanners.map((Banner, index) => ( <Stack gap={0} grow={1} direction="column">
<Banner key={index.toString()} /> {pageBanners.map((Banner, index) => (
<Banner key={index.toString()} />
))}
{ready && this.renderRoutes()}
</Stack>
{bodyRenderHooks.map((Hook, index) => (
<Hook key={index.toString()} />
))} ))}
{ready && this.renderRoutes()} </AppChrome>
</Stack> </div>
{bodyRenderHooks.map((Hook, index) => ( <LiveConnectionWarning />
<Hook key={index.toString()} /> <ModalRoot />
))} <PortalContainer />
</AppChrome> </ModalsContextProvider>
</div> </CompatRouter>
<LiveConnectionWarning /> </LocationServiceProvider>
<ModalRoot />
<PortalContainer />
</ModalsContextProvider>
</CompatRouter>
</Router> </Router>
</KBarProvider> </KBarProvider>
</ThemeProvider> </ThemeProvider>

Loading…
Cancel
Save