Feature: Use ui extension hooks where available (#4765)

**What this PR does / why we need it:**
This PR updates usage of plugin extensions APIs to take advantage of the
new hooks API where available. In older versions we fallback to the
currently used hook. This prevents an issue where due to the reactive
registry the older APIs don't receive the full list of extensions. It
also paves the way for frontend performance improvements in Grafana
core.

**Which issue(s) this PR fixes:**
Related: https://github.com/grafana/grafana-community-team/issues/174

**Special notes for your reviewer:**
We would really appreciate some assistance in testing this PR in both
the latest version of Grafana 11 and the minimum supported Grafana
version.

---------

Co-authored-by: Dominik <dominik.broj@grafana.com>
This commit is contained in:
Levente Balogh 2024-08-13 12:18:20 +02:00 committed by GitHub
parent 18726432af
commit 66f2fafce9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 915 additions and 524 deletions

View file

@ -12,7 +12,7 @@ module.exports = {
{
files: ['src/**/*.{ts,tsx}'],
rules: {
'deprecation/deprecation': 'warn',
'deprecation/deprecation': 'off',
},
parserOptions: {
project: './tsconfig.json',

View file

@ -135,14 +135,14 @@
"@dnd-kit/sortable": "^7.0.2",
"@dnd-kit/utilities": "^3.2.1",
"@emotion/css": "11.10.6",
"@grafana/data": "^10.2.3",
"@grafana/data": "^11.1.3",
"@grafana/faro-web-sdk": "^1.4.2",
"@grafana/faro-web-tracing": "^1.4.2",
"@grafana/labels": "~1.5.1",
"@grafana/runtime": "^10.2.2",
"@grafana/runtime": "^11.1.3",
"@grafana/scenes": "^1.28.0",
"@grafana/schema": "^10.2.2",
"@grafana/ui": "10.2.0",
"@grafana/schema": "^11.1.3",
"@grafana/ui": "^11.1.3",
"@lifeomic/attempt": "^3.0.3",
"array-move": "^4.0.0",
"axios": "^1.6.7",

View file

@ -1,7 +1,11 @@
import React, { ReactElement, useMemo, useState } from 'react';
import { PluginExtensionLink } from '@grafana/data';
import { getPluginLinkExtensions } from '@grafana/runtime';
import {
type GetPluginExtensionsOptions,
getPluginLinkExtensions,
usePluginLinks as originalUsePluginLinks,
} from '@grafana/runtime';
import { Dropdown, ToolbarButton } from '@grafana/ui';
import { OnCallPluginExtensionPoints } from 'types';
@ -16,6 +20,9 @@ interface Props {
grafanaIncidentId: string | null;
}
// `usePluginLinks()` is only available in Grafana>=11.1.0, so we have a fallback for older versions
const usePluginLinks = originalUsePluginLinks === undefined ? usePluginLinksFallback : originalUsePluginLinks;
export function ExtensionLinkDropdown({
incident,
extensionPointId,
@ -24,15 +31,15 @@ export function ExtensionLinkDropdown({
}: Props): ReactElement | null {
const [isOpen, setIsOpen] = useState(false);
const context = useExtensionPointContext(incident);
const extensions = useExtensionLinks(context, extensionPointId);
const { links, isLoading } = usePluginLinks({ context, extensionPointId, limitPerPlugin: 3 });
if (extensions.length === 0) {
if (links.length === 0 || isLoading) {
return null;
}
const menu = (
<ExtensionLinkMenu
extensions={extensions}
extensions={links}
declareIncidentLink={declareIncidentLink}
grafanaIncidentId={grafanaIncidentId}
/>
@ -51,24 +58,31 @@ function useExtensionPointContext(incident: ApiSchemas['AlertGroup']): PluginExt
return { alertGroup: incident };
}
function useExtensionLinks<T extends object>(
context: T,
extensionPointId: OnCallPluginExtensionPoints
): PluginExtensionLink[] {
function usePluginLinksFallback({ context, extensionPointId, limitPerPlugin }: GetPluginExtensionsOptions): {
links: PluginExtensionLink[];
isLoading: boolean;
} {
return useMemo(() => {
// getPluginLinkExtensions is available in Grafana>=10.0,
// so will be undefined in earlier versions. Just return an
// empty list of extensions in this case.
if (getPluginLinkExtensions === undefined) {
return [];
return {
links: [],
isLoading: false,
};
}
const { extensions } = getPluginLinkExtensions({
extensionPointId,
context,
limitPerPlugin: 3,
limitPerPlugin,
});
return extensions;
return {
links: extensions,
isLoading: false,
};
}, [context]);
}

View file

@ -30,12 +30,10 @@ exports[`AddResponders should properly display the add responders button when hi
>
<div>
<button
aria-disabled="false"
class="css-8b29hm-button"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
<span
class="css-1riaxdn"
>
@ -117,12 +115,10 @@ exports[`AddResponders should render properly in create mode 1`] = `
>
<div>
<button
aria-disabled="false"
class="css-8b29hm-button"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
<span
class="css-1riaxdn"
>
@ -170,12 +166,10 @@ exports[`AddResponders should render properly in update mode 1`] = `
>
<div>
<button
aria-disabled="false"
class="css-8b29hm-button"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
<span
class="css-1riaxdn"
>
@ -223,12 +217,10 @@ exports[`AddResponders should render selected team and users properly 1`] = `
>
<div>
<button
aria-disabled="false"
class="css-8b29hm-button"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
<span
class="css-1riaxdn"
>
@ -282,15 +274,11 @@ exports[`AddResponders should render selected team and users properly 1`] = `
>
<button
aria-label="Remove responder"
class="css-17584xm"
class="css-a2noi1"
data-testid="team-responder-delete-icon"
tabindex="0"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
</button>
/>
</div>
</div>
</li>
@ -352,6 +340,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="css-1i88p6p"
@ -366,6 +355,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
Select...
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-describedby="react-select-2-placeholder"
aria-expanded="false"
@ -382,11 +372,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
</div>
<div
class="css-zyjsuv-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
</div>
@ -395,15 +381,11 @@ exports[`AddResponders should render selected team and users properly 1`] = `
>
<button
aria-label="Remove responder"
class="css-17584xm"
class="css-a2noi1"
data-testid="user-responder-delete-icon"
tabindex="0"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
</button>
/>
</div>
</div>
</div>
@ -467,6 +449,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="css-1i88p6p"
@ -481,6 +464,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
Select...
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-describedby="react-select-3-placeholder"
aria-expanded="false"
@ -496,11 +480,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
</div>
<div
class="css-zyjsuv-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
</div>
@ -509,15 +489,11 @@ exports[`AddResponders should render selected team and users properly 1`] = `
>
<button
aria-label="Remove responder"
class="css-17584xm"
class="css-a2noi1"
data-testid="user-responder-delete-icon"
tabindex="0"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
</button>
/>
</div>
</div>
</div>
@ -581,6 +557,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="css-1i88p6p"
@ -595,6 +572,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
Select...
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-describedby="react-select-4-placeholder"
aria-expanded="false"
@ -610,11 +588,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
</div>
<div
class="css-zyjsuv-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
</div>
@ -623,15 +597,11 @@ exports[`AddResponders should render selected team and users properly 1`] = `
>
<button
aria-label="Remove responder"
class="css-17584xm"
class="css-a2noi1"
data-testid="user-responder-delete-icon"
tabindex="0"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
</button>
/>
</div>
</div>
</div>
@ -640,28 +610,24 @@ exports[`AddResponders should render selected team and users properly 1`] = `
<div
aria-label="[object Object]"
class="css-10yjoiw css-182y09v"
data-testid="data-testid Alert info"
role="status"
>
<div
class="css-1td7znu"
class="css-1ewk8v0"
data-testid="data-testid Alert info"
>
<div
class="css-ufgc62"
class="css-9n8jpb"
>
<div
class="css-tluiue"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
<div
class="css-1gmwkrf"
class="css-vjkmk1"
>
<span
class="css-9om60p"
class="css-b9x8ok"
>
<span
class="css-77ouhj--primary css-77ouhj--medium css-1287p17"
@ -686,11 +652,7 @@ exports[`AddResponders should render selected team and users properly 1`] = `
</div>
<div
class="css-12kn7ff-layoutChildrenWrapper"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</span>
</a>

View file

@ -22,11 +22,7 @@ exports[`AddRespondersPopup it shows a loading message initially 1`] = `
/>
<div
class="css-7099m8-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
<div
@ -35,16 +31,17 @@ exports[`AddRespondersPopup it shows a loading message initially 1`] = `
>
<div
class="css-1hvl7lx"
data-testid="data-testid radio-button"
>
<input
checked=""
class="css-1f9hgw3"
class="css-18nv6l3"
id="option-teams-radiogroup-1"
name="radiogroup-1"
type="radio"
/>
<label
class="css-10bka4u"
class="css-18zk0h1"
for="option-teams-radiogroup-1"
>
Teams
@ -53,15 +50,16 @@ exports[`AddRespondersPopup it shows a loading message initially 1`] = `
</div>
<div
class="css-1hvl7lx"
data-testid="data-testid radio-button"
>
<input
class="css-1f9hgw3"
class="css-18nv6l3"
id="option-users-radiogroup-1"
name="radiogroup-1"
type="radio"
/>
<label
class="css-10bka4u"
class="css-18zk0h1"
for="option-users-radiogroup-1"
>
Users
@ -75,14 +73,9 @@ exports[`AddRespondersPopup it shows a loading message initially 1`] = `
Loading...
<div
class="css-1tqtz24"
class="css-1baulvz"
data-testid="Spinner"
>
<i
aria-label="loading spinner"
class="fa fa-spinner fa-spin fa-spin"
/>
</div>
/>
</div>
</div>
</div>

View file

@ -14,6 +14,7 @@ exports[`NotificationPoliciesSelect disabled state 1`] = `
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="css-1i88p6p"
@ -27,6 +28,7 @@ exports[`NotificationPoliciesSelect disabled state 1`] = `
Default
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
@ -42,11 +44,7 @@ exports[`NotificationPoliciesSelect disabled state 1`] = `
</div>
<div
class="css-zyjsuv-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
</div>
@ -66,6 +64,7 @@ exports[`NotificationPoliciesSelect it renders properly 1`] = `
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="css-1i88p6p"
@ -79,6 +78,7 @@ exports[`NotificationPoliciesSelect it renders properly 1`] = `
Default
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
@ -93,11 +93,7 @@ exports[`NotificationPoliciesSelect it renders properly 1`] = `
</div>
<div
class="css-zyjsuv-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
</div>

View file

@ -43,15 +43,11 @@ exports[`TeamResponder it renders data properly 1`] = `
>
<button
aria-label="Remove responder"
class="css-17584xm"
class="css-a2noi1"
data-testid="team-responder-delete-icon"
tabindex="0"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
</button>
/>
</div>
</div>
</li>

View file

@ -60,6 +60,7 @@ exports[`UserResponder it renders data properly 1`] = `
aria-live="polite"
aria-relevant="additions text"
class="css-1f43avz-a11yText-A11yText"
role="log"
/>
<div
class="css-1i88p6p"
@ -73,6 +74,7 @@ exports[`UserResponder it renders data properly 1`] = `
Important
</div>
<input
aria-activedescendant=""
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
@ -87,11 +89,7 @@ exports[`UserResponder it renders data properly 1`] = `
</div>
<div
class="css-zyjsuv-input-suffix"
>
<div
class="css-1j2891d-Icon"
/>
</div>
/>
</div>
</div>
</div>
@ -100,15 +98,11 @@ exports[`UserResponder it renders data properly 1`] = `
>
<button
aria-label="Remove responder"
class="css-17584xm"
class="css-a2noi1"
data-testid="user-responder-delete-icon"
tabindex="0"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
</button>
/>
</div>
</div>
</div>

View file

@ -24,14 +24,9 @@ exports[`MobileAppConnection it shows a QR code if the app isn't already connect
Loading...
<div
class="css-1tqtz24"
class="css-1baulvz"
data-testid="Spinner"
>
<i
aria-label="loading spinner"
class="fa fa-spinner fa-spin fa-spin"
/>
</div>
/>
</div>
</div>
<div
@ -150,14 +145,9 @@ exports[`MobileAppConnection it shows a loading message if it is currently disco
Loading...
<div
class="css-1tqtz24"
class="css-1baulvz"
data-testid="Spinner"
>
<i
aria-label="loading spinner"
class="fa fa-spinner fa-spin fa-spin"
/>
</div>
/>
</div>
</div>
<div
@ -276,14 +266,9 @@ exports[`MobileAppConnection it shows a loading message if it is currently fetch
Loading...
<div
class="css-1tqtz24"
class="css-1baulvz"
data-testid="Spinner"
>
<i
aria-label="loading spinner"
class="fa fa-spinner fa-spin fa-spin"
/>
</div>
/>
</div>
</div>
<div
@ -401,12 +386,10 @@ exports[`MobileAppConnection it shows a warning when cloud is not connected 1`]
href="/a/grafana-oncall-app/cloud"
>
<button
aria-disabled="false"
class="css-8b29hm-button"
type="button"
>
<div
class="css-1j2891d-Icon"
/>
<span
class="css-1riaxdn"
>

View file

@ -3,6 +3,7 @@
exports[`DisconnectButton it renders properly 1`] = `
<div>
<button
aria-disabled="false"
class="css-ttl745-button disconnect-button"
data-testid="test__disconnect"
type="button"

View file

@ -28,6 +28,7 @@ exports[`LinkLoginButton it renders properly 1`] = `
class="css-12oo3x0-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-td06pi-button"
type="button"
>

View file

@ -90,6 +90,7 @@ exports[`PluginConfigPage If onCallApiUrl is not set in the plugin's meta jsonDa
</div>
</div>
<button
aria-disabled="false"
class="css-td06pi-button"
type="submit"
>
@ -130,6 +131,7 @@ exports[`PluginConfigPage If onCallApiUrl is set, and updatePluginStatus returns
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-td06pi-button"
type="button"
>
@ -144,6 +146,7 @@ exports[`PluginConfigPage If onCallApiUrl is set, and updatePluginStatus returns
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-ttl745-button"
type="button"
>
@ -198,6 +201,7 @@ exports[`PluginConfigPage It doesn't make any network calls if the plugin config
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-ttl745-button"
type="button"
>
@ -253,6 +257,7 @@ exports[`PluginConfigPage OnCallApiUrl is set, and checkTokenAndIfPluginIsConnec
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-ttl745-button"
type="button"
>
@ -349,6 +354,7 @@ exports[`PluginConfigPage OnCallApiUrl is set, and checkTokenAndIfPluginIsConnec
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-td06pi-button"
type="button"
>
@ -363,6 +369,7 @@ exports[`PluginConfigPage OnCallApiUrl is set, and checkTokenAndIfPluginIsConnec
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-ttl745-button"
type="button"
>
@ -404,6 +411,7 @@ exports[`PluginConfigPage Plugin reset: successful - false 1`] = `
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-td06pi-button"
type="button"
>
@ -418,6 +426,7 @@ exports[`PluginConfigPage Plugin reset: successful - false 1`] = `
class="css-18qv8yz-layoutChildrenWrapper"
>
<button
aria-disabled="false"
class="css-ttl745-button"
type="button"
>
@ -522,6 +531,7 @@ exports[`PluginConfigPage Plugin reset: successful - true 1`] = `
</div>
</div>
<button
aria-disabled="false"
class="css-td06pi-button"
type="submit"
>

View file

@ -87,16 +87,14 @@ exports[`ConfigurationForm It doesn't allow the user to submit if the URL is inv
class="css-9z7wq3"
role="alert"
>
<div
class="css-1j2891d-Icon"
/>
Must be a valid URL
</div>
</div>
</div>
</div>
<button
class="css-td06pi-button"
aria-disabled="false"
class="css-9hybrt-button"
disabled=""
type="submit"
>
@ -255,6 +253,7 @@ exports[`ConfigurationForm It shows an error message if the self hosted plugin A
</span>
</div>
<button
aria-disabled="false"
class="css-td06pi-button"
type="submit"
>

View file

@ -4,7 +4,8 @@ exports[`RemoveCurrentConfigurationButton It renders properly when disabled 1`]
<body>
<div>
<button
class="css-ttl745-button"
aria-disabled="false"
class="css-mgdi0l-button"
disabled=""
type="button"
>
@ -22,6 +23,7 @@ exports[`RemoveCurrentConfigurationButton It renders properly when enabled 1`] =
<body>
<div>
<button
aria-disabled="false"
class="css-ttl745-button"
type="button"
>

File diff suppressed because it is too large Load diff