mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 20:54:22 -06:00
727a4bd9e4
* Navigation: Remove plus button behind feature toggle * Navigation: Add home button behind feature toggle * Navigation: Move settings/admin to bottom section behind feature toggle * Navigation: Refactor grafana logo to be a NavBarItem * Navigation: Create new PluginSection and styling changes to support new sections * Navigation: Hack to use mobile menu as a mega menu for now * Navigation: Only render plugin section if there are items * Navigation: mobile menu is always 100% width if toggle is off * Navigation: Reset width back to 48 and fix broken css property * Navigation: Create generic NavBarSection component to reduce repetition * Navigation: Don't show sublinks for core items * Navigation: Comments from UX review * Navigation: Remove mobile menu hack * Navigation: Unit tests for enrichConfigItems and other minor review comments * Navigation: Move section logic to backend * Navigation: Refactor alerting links out into a separate function * Navigation: More tests for isLinkActive * Linting... * Navigation: Create new NavBar component for when feature toggle is enabled
138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
import React, { ComponentType } from 'react';
|
|
import { Router, Route, Redirect, Switch } from 'react-router-dom';
|
|
import { config, locationService, navigationLogger } from '@grafana/runtime';
|
|
import { Provider } from 'react-redux';
|
|
import { store } from 'app/store/store';
|
|
import { ErrorBoundaryAlert, GlobalStyles, ModalRoot, ModalsProvider } from '@grafana/ui';
|
|
import { GrafanaApp } from './app';
|
|
import { getAppRoutes } from 'app/routes/routes';
|
|
import { ConfigContext, ThemeProvider } from './core/utils/ConfigProvider';
|
|
import { RouteDescriptor } from './core/navigation/types';
|
|
import { contextSrv } from './core/services/context_srv';
|
|
import { NavBar } from './core/components/NavBar/NavBar';
|
|
import { NavBarNext } from './core/components/NavBar/NavBarNext';
|
|
import { GrafanaRoute } from './core/navigation/GrafanaRoute';
|
|
import { AppNotificationList } from './core/components/AppNotifications/AppNotificationList';
|
|
import { SearchWrapper } from 'app/features/search';
|
|
import { LiveConnectionWarning } from './features/live/LiveConnectionWarning';
|
|
|
|
interface AppWrapperProps {
|
|
app: GrafanaApp;
|
|
}
|
|
|
|
interface AppWrapperState {
|
|
ngInjector: any;
|
|
}
|
|
|
|
/** Used by enterprise */
|
|
let bodyRenderHooks: ComponentType[] = [];
|
|
let pageBanners: ComponentType[] = [];
|
|
|
|
export function addBodyRenderHook(fn: ComponentType) {
|
|
bodyRenderHooks.push(fn);
|
|
}
|
|
|
|
export function addPageBanner(fn: ComponentType) {
|
|
pageBanners.push(fn);
|
|
}
|
|
export class AppWrapper extends React.Component<AppWrapperProps, AppWrapperState> {
|
|
container = React.createRef<HTMLDivElement>();
|
|
|
|
constructor(props: AppWrapperProps) {
|
|
super(props);
|
|
|
|
this.state = {
|
|
ngInjector: null,
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
if (this.container) {
|
|
this.bootstrapNgApp();
|
|
} else {
|
|
throw new Error('Failed to boot angular app, no container to attach to');
|
|
}
|
|
}
|
|
|
|
bootstrapNgApp() {
|
|
const injector = this.props.app.angularApp.bootstrap();
|
|
this.setState({ ngInjector: injector });
|
|
}
|
|
|
|
renderRoute = (route: RouteDescriptor) => {
|
|
const roles = route.roles ? route.roles() : [];
|
|
|
|
return (
|
|
<Route
|
|
exact={route.exact === undefined ? true : route.exact}
|
|
path={route.path}
|
|
key={route.path}
|
|
render={(props) => {
|
|
navigationLogger('AppWrapper', false, 'Rendering route', route, 'with match', props.location);
|
|
// TODO[Router]: test this logic
|
|
if (roles?.length) {
|
|
if (!roles.some((r: string) => contextSrv.hasRole(r))) {
|
|
return <Redirect to="/" />;
|
|
}
|
|
}
|
|
|
|
return <GrafanaRoute {...props} route={route} />;
|
|
}}
|
|
/>
|
|
);
|
|
};
|
|
|
|
renderRoutes() {
|
|
return <Switch>{getAppRoutes().map((r) => this.renderRoute(r))}</Switch>;
|
|
}
|
|
|
|
render() {
|
|
navigationLogger('AppWrapper', false, 'rendering');
|
|
|
|
// @ts-ignore
|
|
const appSeed = `<grafana-app ng-cloak></app-notifications-list></grafana-app>`;
|
|
const newNavigationEnabled = config.featureToggles.newNavigation;
|
|
|
|
return (
|
|
<Provider store={store}>
|
|
<ErrorBoundaryAlert style="page">
|
|
<ConfigContext.Provider value={config}>
|
|
<ThemeProvider>
|
|
<ModalsProvider>
|
|
<GlobalStyles />
|
|
<div className="grafana-app">
|
|
<Router history={locationService.getHistory()}>
|
|
{newNavigationEnabled ? <NavBarNext /> : <NavBar />}
|
|
<main className="main-view">
|
|
{pageBanners.map((Banner, index) => (
|
|
<Banner key={index.toString()} />
|
|
))}
|
|
|
|
<div
|
|
id="ngRoot"
|
|
ref={this.container}
|
|
dangerouslySetInnerHTML={{
|
|
__html: appSeed,
|
|
}}
|
|
/>
|
|
|
|
<AppNotificationList />
|
|
<SearchWrapper />
|
|
{this.state.ngInjector && this.container && this.renderRoutes()}
|
|
{bodyRenderHooks.map((Hook, index) => (
|
|
<Hook key={index.toString()} />
|
|
))}
|
|
</main>
|
|
</Router>
|
|
</div>
|
|
<LiveConnectionWarning />
|
|
<ModalRoot />
|
|
</ModalsProvider>
|
|
</ThemeProvider>
|
|
</ConfigContext.Provider>
|
|
</ErrorBoundaryAlert>
|
|
</Provider>
|
|
);
|
|
}
|
|
}
|