- [From version 9.3.x to 9.4.x](#from-version-93x-to-94x)
- [Supporting new navigation layout](#supporting-new-navigation-layout)
- [Forwarded HTTP headers in grafana-plugin-sdk-go](#forwarded-http-headers-in-grafana-plugin-sdk-go)
- [From version 9.1.x to 9.2.x](#from-version-91x-to-92x)
- [React and React-dom as peer dependencies](#react-and-react-dom-as-peer-dependencies)
- [NavModelItem requires a valid icon name](#navmodelitem-requires-a-valid-icon-name)
@ -66,6 +67,129 @@ This guide helps you identify the steps required to update a plugin from the Gra
## From version 9.3.x to 9.4.x
### Supporting new navigation layout
First, enable the `topnav` feature flag in `custom.ini` to check how your plugin renders in the new navigation layout:
```ini
[feature_toggles]
enable = topnav
```
#### Migrate from `onNavChanged`
If your plugin uses the `onNavChanged` callback to inform Grafana of its nav model & sub pages, you should see that this results in duplicated navigation elements. If you disable `topnav` it should look just as before.
When `topnav` is enabled we need to update the plugin to take advantage of the new `PluginPage` component and not call `onNavChanged`. `onNavChanged` is now deprecated.
#### Switch to `PluginPage` component
Grafana now exposes a new `PluginPage` component from `@grafana/runtime` that hooks into the new navigation and page layouts and supports the old page layouts when the `topnav` feature is disabled. This new component will also handle rendering the section navigation. The section navigation can include other core sections and other plugins. To control what pages are displayed in the section navigation for a specific plugin, Grafana will use the pages added in `plugin.json` that have `addToNav` set to `true`.
This component is very easy to use. Simply wrap it around your page content:
```tsx
import { PluginPage } from '@grafana/runtime';
...
return (
<PluginPage>
{your page content here}
</PluginPage>
);
```
Grafana will look at the URL to know what plugin and page should be active in the section nav, so this only works for pages you have specified in `plugin.json`. `PluginPage` will then render a page header based on the page name specified in `plugin.json`.
#### Using `PluginPage` for pages not defined in `plugin.json`
The `PluginPage` component also exposes a `pageNav` property that is a `NavModelItem`. This `pageNav` property is useful for pages that are not defined in `plugin.json` (e.g. individual item pages). The `text` and `description` you specify in the `pageNav` model will be used to populate the breadcrumbs and page header.
Example:
```tsx
const pageNav = {
text: 'Write errors cortex-prod-04',
description: 'Incident timeline and details'
};
return (
<PluginPagepageNav={pageNav}>
{your page content here}
</PluginPage>
);
```
The way the active page is matched in the breadcrumbs and section nav relies on the page routes being hierarchical. If you have a list page and an item page, the item page needs to be a subroute of the list page and the list page url needs to be specified in your `plugin.json`. For example, you might have a list of users at `/users`. This means that the item page for a specific user needs to be at `/users/:id`. This may require some refactoring of your routes.
#### Using `PluginPage` with tabs
You can also create a further layer of hierarchy by specifying `children` in the `pageNav` model to created a page with tabbed navigation.
Example:
```tsx
const pageNav = {
text: 'My page',
description: 'Incident timeline and details',
url: '/a/myorgid-pluginname-app',
children: [
{
url: '/a/myorgid-pluginname-app/tab1',
text: 'Tab1',
active: true,
},
{
url: '/a/myorgid-pluginname-app/tab2',
text: 'Tab1',
},
],
};
return (
<PluginPagepageNav={pageNav}>
{your page content here}
</PluginPage>
);
```
#### Using `PluginPage` in a backwards-compatible way
If you want to maintain backwards-compatibility with older versions of Grafana, one way is to implement a `PluginPage` wrapper. If `PluginPage` is available and the `topnav` feature is enabled then use the real `PluginPage`, otherwise fallback to whatever each plugin is doing today (including calling `onNavChanged`).
Example:
```tsx
import { PluginPageProps, PluginPage as RealPluginPage, config } from '@grafana/runtime';
function PluginPageFallback(props: PluginPageProps) {
return props.children;
}
```
There’s an additional step (and if block) needed to hide/show tabs depending on if `config.features.topnav` is `true`. These changes will need to be made in the `useNavModel.ts` file in your plugin:
```tsx
// useNavModel.ts
import { config } from '@grafana/runtime';
...
export function useNavModel({ meta, rootPath, onNavChanged }: Args) {
const { pathname, search } = useLocation();
useEffect(() => {
if (config.featureToggles.topnav) {
return;
}
}, [config]);
...
```
### Forwarded HTTP headers in grafana-plugin-sdk-go
It's recommended to use the `<request>.GetHTTPHeader` or `<request>.GetHTTPHeaders` methods when retrieving forwarded HTTP headers. See [Forward OAuth identity for the logged-in user]({{< relref "add-authentication-for-data-source-plugins.md#forward-oauth-identity-for-the-logged-in-user" >}}), [Forward cookies for the logged-in user
@ -40,6 +40,8 @@ The new navigation is gradually rolling out to all users on Grafana Cloud. If yo
> **Note:** The Grafana documentation has not yet been updated to reflect changes to the navigation.
> **Note:** Plugin developers should refer to [the migration guide]({{< relref "../developers/plugins/migration-guide.md#supporting-new-navigation-layout" >}}) to upgrade their plugins to work seamlessly with the new navigation layout.
{{<figuresrc="/media/docs/grafana/navigation-9-4.png"max-width="750px"caption="Grafana new navigation">}}