setIsExpanded(!isExpanded)} />
- {name} {isProvisioned && }
+ {name} {isProvisioned && }{' '}
+ {missing && (
+
+ This template is misconfigured.
+
+
+ Templates must be defined in both the and{' '}
+ sections of your alertmanager configuration.
+
+ >
+ }
+ >
+
+
+
+
+ )}
{isProvisioned && (
diff --git a/public/app/features/alerting/unified/components/receivers/__snapshots__/NewReceiverView.test.tsx.snap b/public/app/features/alerting/unified/components/receivers/__snapshots__/NewReceiverView.test.tsx.snap
index 681e1ee181f..ededb25feb5 100644
--- a/public/app/features/alerting/unified/components/receivers/__snapshots__/NewReceiverView.test.tsx.snap
+++ b/public/app/features/alerting/unified/components/receivers/__snapshots__/NewReceiverView.test.tsx.snap
@@ -34,142 +34,113 @@ exports[`alerting API server disabled should be able to test and save a receiver
exports[`alerting API server disabled should be able to test and save a receiver 2`] = `
[
- {
- "alertmanager_config": {
- "mute_time_intervals": [],
- "receivers": [
+ [
+ {
+ "grafana_managed_receiver_configs": [
{
- "grafana_managed_receiver_configs": [
- {
- "disableResolveMessage": false,
- "name": "grafana-default-email",
- "secureFields": {},
- "settings": {
- "addresses": "gilles.demey@grafana.com",
- "singleEmail": false,
- },
- "type": "email",
- "uid": "xeKQrBrnk",
- },
- ],
+ "disableResolveMessage": false,
"name": "grafana-default-email",
- },
- {
- "grafana_managed_receiver_configs": [
- {
- "disableResolveMessage": false,
- "name": "provisioned-contact-point",
- "provenance": "api",
- "secureFields": {},
- "settings": {
- "addresses": "gilles.demey@grafana.com",
- "singleEmail": false,
- },
- "type": "email",
- "uid": "s8SdCVjnk",
- },
- ],
- "name": "provisioned-contact-point",
- },
- {
- "grafana_managed_receiver_configs": [
- {
- "disableResolveMessage": false,
- "name": "lotsa-emails",
- "secureFields": {},
- "settings": {
- "addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com",
- "singleEmail": false,
- },
- "type": "email",
- "uid": "af306c96-35a2-4d6e-908a-4993e245dbb2",
- },
- ],
- "name": "lotsa-emails",
- },
- {
- "grafana_managed_receiver_configs": [
- {
- "disableResolveMessage": false,
- "name": "Slack with multiple channels",
- "secureFields": {
- "token": true,
- },
- "settings": {
- "recipient": "test-alerts",
- },
- "type": "slack",
- "uid": "c02ad56a-31da-46b9-becb-4348ec0890fd",
- },
- {
- "disableResolveMessage": false,
- "name": "Slack with multiple channels",
- "secureFields": {
- "token": true,
- },
- "settings": {
- "recipient": "test-alerts2",
- },
- "type": "slack",
- "uid": "b286a3be-f690-49e2-8605-b075cbace2df",
- },
- ],
- "name": "Slack with multiple channels",
- },
- {
- "grafana_managed_receiver_configs": [
- {
- "disableResolveMessage": false,
- "name": "Oncall-integration",
- "settings": {
- "url": "https://oncall-endpoint.example.com",
- },
- "type": "oncall",
- },
- ],
- "name": "OnCall Conctact point",
- },
- {
- "grafana_managed_receiver_configs": [
- {
- "disableResolveMessage": false,
- "name": "my new receiver",
- "secureSettings": {},
- "settings": {
- "addresses": "tester@grafana.com",
- "singleEmail": false,
- },
- "type": "email",
- },
- ],
- "name": "my new receiver",
- },
- ],
- "route": {
- "receiver": "grafana-default-email",
- "routes": [
- {
- "receiver": "provisioned-contact-point",
+ "secureFields": {},
+ "settings": {
+ "addresses": "gilles.demey@grafana.com",
+ "singleEmail": false,
},
- ],
- },
- "templates": [
- "slack-template",
- "custom-email",
- "provisioned-template",
- "template with spaces",
+ "type": "email",
+ "uid": "xeKQrBrnk",
+ },
],
- "time_intervals": [],
+ "name": "grafana-default-email",
},
- "template_file_provenances": {
- "provisioned-template": "api",
+ {
+ "grafana_managed_receiver_configs": [
+ {
+ "disableResolveMessage": false,
+ "name": "provisioned-contact-point",
+ "provenance": "api",
+ "secureFields": {},
+ "settings": {
+ "addresses": "gilles.demey@grafana.com",
+ "singleEmail": false,
+ },
+ "type": "email",
+ "uid": "s8SdCVjnk",
+ },
+ ],
+ "name": "provisioned-contact-point",
},
- "template_files": {
- "custom-email": "{{ define "custom-email" }} Custom email template {{ end }}",
- "provisioned-template": "{{ define "provisioned-template" }} Custom provisioned template {{ end }}",
- "slack-template": "{{ define "slack-template" }} Custom slack template {{ end }}",
- "template with spaces": "{{ define "template with spaces" }} Custom template with spaces in the name {{ end }}",
+ {
+ "grafana_managed_receiver_configs": [
+ {
+ "disableResolveMessage": false,
+ "name": "lotsa-emails",
+ "secureFields": {},
+ "settings": {
+ "addresses": "gilles.demey+1@grafana.com, gilles.demey+2@grafana.com, gilles.demey+3@grafana.com, gilles.demey+4@grafana.com",
+ "singleEmail": false,
+ },
+ "type": "email",
+ "uid": "af306c96-35a2-4d6e-908a-4993e245dbb2",
+ },
+ ],
+ "name": "lotsa-emails",
},
- },
+ {
+ "grafana_managed_receiver_configs": [
+ {
+ "disableResolveMessage": false,
+ "name": "Slack with multiple channels",
+ "secureFields": {
+ "token": true,
+ },
+ "settings": {
+ "recipient": "test-alerts",
+ },
+ "type": "slack",
+ "uid": "c02ad56a-31da-46b9-becb-4348ec0890fd",
+ },
+ {
+ "disableResolveMessage": false,
+ "name": "Slack with multiple channels",
+ "secureFields": {
+ "token": true,
+ },
+ "settings": {
+ "recipient": "test-alerts2",
+ },
+ "type": "slack",
+ "uid": "b286a3be-f690-49e2-8605-b075cbace2df",
+ },
+ ],
+ "name": "Slack with multiple channels",
+ },
+ {
+ "grafana_managed_receiver_configs": [
+ {
+ "disableResolveMessage": false,
+ "name": "Oncall-integration",
+ "settings": {
+ "url": "https://oncall-endpoint.example.com",
+ },
+ "type": "oncall",
+ },
+ ],
+ "name": "OnCall Conctact point",
+ },
+ {
+ "grafana_managed_receiver_configs": [
+ {
+ "disableResolveMessage": false,
+ "name": "my new receiver",
+ "secureSettings": {},
+ "settings": {
+ "addresses": "tester@grafana.com",
+ "singleEmail": false,
+ },
+ "type": "email",
+ },
+ ],
+ "name": "my new receiver",
+ },
+ ],
]
`;
diff --git a/public/app/features/alerting/unified/components/receivers/form/fields/TemplateSelector.test.tsx b/public/app/features/alerting/unified/components/receivers/form/fields/TemplateSelector.test.tsx
index 73b8d455436..03266950cac 100644
--- a/public/app/features/alerting/unified/components/receivers/form/fields/TemplateSelector.test.tsx
+++ b/public/app/features/alerting/unified/components/receivers/form/fields/TemplateSelector.test.tsx
@@ -2,6 +2,7 @@ import { ReactNode } from 'react';
import { render, screen, userEvent } from 'test/test-utils';
import { CodeEditorProps } from '@grafana/ui/src/components/Monaco/types';
+import alertmanagerConfigMock from 'app/features/alerting/unified/components/contact-points/__mocks__/alertmanager.config.mock.json';
import { setupMswServer } from 'app/features/alerting/unified/mockApi';
import { grantUserPermissions } from 'app/features/alerting/unified/mocks';
import { AlertmanagerProvider } from 'app/features/alerting/unified/state/AlertmanagerContext';
@@ -98,7 +99,7 @@ describe('TemplatesPicker', () => {
const input = screen.getByRole('combobox');
expect(screen.queryByText('slack-template')).not.toBeInTheDocument();
await userEvent.click(input);
- expect(screen.getAllByRole('option')).toHaveLength(7); // 4 templates in mock plus 3 in the default template
+ expect(screen.getAllByRole('option')).toHaveLength(Object.keys(alertmanagerConfigMock.template_files).length + 3); // 4 templates in mock plus 3 in the default template
const template = screen.getByRole('option', { name: 'slack-template' });
await userEvent.click(template);
expect(screen.getByText('slack-template')).toBeInTheDocument();
diff --git a/public/app/features/browse-dashboards/permissions.ts b/public/app/features/browse-dashboards/permissions.ts
index d4043434bce..3347fb9f442 100644
--- a/public/app/features/browse-dashboards/permissions.ts
+++ b/public/app/features/browse-dashboards/permissions.ts
@@ -8,7 +8,7 @@ function checkFolderPermission(action: AccessControlAction, folderDTO?: FolderDT
function checkCanCreateFolders(folderDTO?: FolderDTO) {
// Can only create a folder if we have permissions and either we're at root or nestedFolders is enabled
- if (folderDTO && !config.featureToggles.nestedFolders) {
+ if (folderDTO && folderDTO.uid !== 'general' && !config.featureToggles.nestedFolders) {
return false;
}
diff --git a/public/app/features/connections/Connections.test.tsx b/public/app/features/connections/Connections.test.tsx
index 1d752fda505..42995857cde 100644
--- a/public/app/features/connections/Connections.test.tsx
+++ b/public/app/features/connections/Connections.test.tsx
@@ -1,7 +1,7 @@
-import { render, RenderResult, screen } from '@testing-library/react';
-import { TestProvider } from 'test/helpers/TestProvider';
+import { RenderResult, screen } from '@testing-library/react';
+import { Route, Routes } from 'react-router-dom-v5-compat';
+import { render } from 'test/test-utils';
-import { locationService } from '@grafana/runtime';
import { contextSrv } from 'app/core/services/context_srv';
import { getMockDataSources } from 'app/features/datasources/__mocks__';
import * as api from 'app/features/datasources/api';
@@ -11,7 +11,7 @@ import { getPluginsStateMock } from '../plugins/admin/__mocks__';
import Connections from './Connections';
import { navIndex } from './__mocks__/store.navIndex.mock';
-import { ROUTE_BASE_ID, ROUTES } from './constants';
+import { ROUTES } from './constants';
jest.mock('app/core/services/context_srv');
jest.mock('app/features/datasources/api');
@@ -21,15 +21,17 @@ jest.mock('@grafana/runtime', () => ({
}));
const renderPage = (
- path = `/${ROUTE_BASE_ID}`,
+ path: string = ROUTES.Base,
store = configureStore({ navIndex, plugins: getPluginsStateMock([]) })
): RenderResult => {
- locationService.push(path);
-
return render(
-
-
-
+
+ } />
+ ,
+ {
+ store,
+ historyOptions: { initialEntries: [path] },
+ }
);
};
diff --git a/public/app/features/connections/Connections.tsx b/public/app/features/connections/Connections.tsx
index fd0a97b1125..05330a180b5 100644
--- a/public/app/features/connections/Connections.tsx
+++ b/public/app/features/connections/Connections.tsx
@@ -1,7 +1,5 @@
-import { Route, Switch, useLocation } from 'react-router-dom';
-import { Navigate } from 'react-router-dom-v5-compat';
+import { Navigate, Routes, Route, useLocation } from 'react-router-dom-v5-compat';
-import { DataSourcesRoutesContext } from 'app/features/datasources/state';
import { StoreState, useSelector } from 'app/types';
import { ROUTES } from './constants';
@@ -32,39 +30,43 @@ export default function Connections() {
const isAddNewConnectionPageOverridden = Boolean(navIndex['standalone-plugin-page-/connections/add-new-connection']);
return (
-
-
- {/* Redirect to "Add new connection" by default */}
- } />
-
-
-
-
-
+
+ {/* Redirect to "Add new connection" by default */}
+ } />
+ {/* The route paths need to be relative to the parent path (ROUTES.Base), so we need to remove that part */}
+ } />
+ } />
+ }
+ />
+ } />
+ }
+ />
- {/* "Add new connection" page - we don't register a route in case a plugin already registers a standalone page for it */}
- {!isAddNewConnectionPageOverridden && (
-
- )}
-
- {/* Redirect from earlier routes to updated routes */}
-
+ {/* "Add new connection" page - we don't register a route in case a plugin already registers a standalone page for it */}
+ {!isAddNewConnectionPageOverridden && (
}
+ caseSensitive
+ path={ROUTES.AddNewConnection.replace(ROUTES.Base, '')}
+ element={ }
/>
- } />
+ )}
- {/* Not found */}
- } />
-
-
+ {/* Redirect from earlier routes to updated routes */}
+ } />
+ } />
+ }
+ />
+
+ {/* Not found */}
+ } />
+
);
}
diff --git a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts
index 1d94698a9a8..cbf26cc5f09 100644
--- a/public/app/features/connections/hooks/useDataSourceSettingsNav.ts
+++ b/public/app/features/connections/hooks/useDataSourceSettingsNav.ts
@@ -1,4 +1,4 @@
-import { useLocation, useParams } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom-v5-compat';
import { NavModel, NavModelItem } from '@grafana/data';
import { getDataSourceSrv } from '@grafana/runtime';
@@ -9,7 +9,7 @@ import { useGetSingle } from 'app/features/plugins/admin/state/hooks';
import { useSelector } from 'app/types';
export function useDataSourceSettingsNav(pageIdParam?: string) {
- const { uid } = useParams<{ uid: string }>();
+ const { uid = '' } = useParams<{ uid: string }>();
const location = useLocation();
const datasource = useDataSource(uid);
const dataSourceMeta = useDataSourceMeta(datasource.type);
diff --git a/public/app/features/connections/pages/DataSourceDashboardsPage.tsx b/public/app/features/connections/pages/DataSourceDashboardsPage.tsx
index ecda29195d7..9a82b0f0d15 100644
--- a/public/app/features/connections/pages/DataSourceDashboardsPage.tsx
+++ b/public/app/features/connections/pages/DataSourceDashboardsPage.tsx
@@ -1,4 +1,4 @@
-import { useParams } from 'react-router-dom';
+import { useParams } from 'react-router-dom-v5-compat';
import { Page } from 'app/core/components/Page/Page';
import { DataSourceDashboards } from 'app/features/datasources/components/DataSourceDashboards';
@@ -6,7 +6,7 @@ import { DataSourceDashboards } from 'app/features/datasources/components/DataSo
import { useDataSourceSettingsNav } from '../hooks/useDataSourceSettingsNav';
export function DataSourceDashboardsPage() {
- const { uid } = useParams<{ uid: string }>();
+ const { uid = '' } = useParams<{ uid: string }>();
const { navId, pageNav } = useDataSourceSettingsNav('dashboards');
return (
diff --git a/public/app/features/connections/pages/DataSourceDetailsPage.tsx b/public/app/features/connections/pages/DataSourceDetailsPage.tsx
index 9a72ddb5783..9ed3153764b 100644
--- a/public/app/features/connections/pages/DataSourceDetailsPage.tsx
+++ b/public/app/features/connections/pages/DataSourceDetailsPage.tsx
@@ -1,4 +1,4 @@
-import { useParams } from 'react-router-dom';
+import { useParams } from 'react-router-dom-v5-compat';
import { Alert, Badge } from '@grafana/ui';
import { PluginDetailsPage } from 'app/features/plugins/admin/components/PluginDetailsPage';
@@ -8,7 +8,7 @@ import { ROUTES } from '../constants';
export function DataSourceDetailsPage() {
const overrideNavId = 'standalone-plugin-page-/connections/add-new-connection';
- const { id } = useParams<{ id: string }>();
+ const { id = '' } = useParams<{ id: string }>();
const navIndex = useSelector((state: StoreState) => state.navIndex);
const isConnectDataPageOverriden = Boolean(navIndex[overrideNavId]);
const navId = isConnectDataPageOverriden ? overrideNavId : 'connections-add-new-connection'; // The nav id changes (gets a prefix) if it is overriden by a plugin
diff --git a/public/app/features/connections/pages/EditDataSourcePage.tsx b/public/app/features/connections/pages/EditDataSourcePage.tsx
index be105116ca0..fe2d93bc888 100644
--- a/public/app/features/connections/pages/EditDataSourcePage.tsx
+++ b/public/app/features/connections/pages/EditDataSourcePage.tsx
@@ -1,9 +1,9 @@
-import { useLocation, useParams } from 'react-router-dom';
+import { useLocation, useParams } from 'react-router-dom-v5-compat';
import DataSourceTabPage from 'app/features/datasources/components/DataSourceTabPage';
export function EditDataSourcePage() {
- const { uid } = useParams<{ uid: string }>();
+ const { uid = '' } = useParams<{ uid: string }>();
const location = useLocation();
const params = new URLSearchParams(location.search);
const pageId = params.get('page');
diff --git a/public/app/features/connections/routes.tsx b/public/app/features/connections/routes.tsx
index d49a322e8f6..935caa40164 100644
--- a/public/app/features/connections/routes.tsx
+++ b/public/app/features/connections/routes.tsx
@@ -1,12 +1,12 @@
import { SafeDynamicImport } from 'app/core/components/DynamicImports/SafeDynamicImport';
import { RouteDescriptor } from 'app/core/navigation/types';
-import { ROUTE_BASE_ID } from './constants';
+import { ROUTES } from './constants';
export function getRoutes(): RouteDescriptor[] {
return [
{
- path: `/${ROUTE_BASE_ID}`,
+ path: ROUTES.Base,
exact: false,
component: SafeDynamicImport(
() => import(/* webpackChunkName: "Connections"*/ 'app/features/connections/Connections')
diff --git a/public/app/features/connections/tabs/ConnectData/ConnectData.tsx b/public/app/features/connections/tabs/ConnectData/ConnectData.tsx
index 63737f4cdff..bc21502dfcb 100644
--- a/public/app/features/connections/tabs/ConnectData/ConnectData.tsx
+++ b/public/app/features/connections/tabs/ConnectData/ConnectData.tsx
@@ -1,6 +1,5 @@
import { css } from '@emotion/css';
-import { useMemo, useState } from 'react';
-import * as React from 'react';
+import { useMemo, useState, FormEvent, MouseEvent } from 'react';
import { GrafanaTheme2, PluginType } from '@grafana/data';
import { useStyles2, LoadingPlaceholder, EmptyState } from '@grafana/ui';
@@ -38,7 +37,7 @@ export function AddNewConnection() {
const styles = useStyles2(getStyles);
const canCreateDataSources = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate);
- const handleSearchChange = (e: React.FormEvent) => {
+ const handleSearchChange = (e: FormEvent) => {
setQueryParams({
search: e.currentTarget.value.toLowerCase(),
});
@@ -62,7 +61,7 @@ export function AddNewConnection() {
[plugins]
);
- const onClickCardGridItem = (e: React.MouseEvent, item: CardGridItem) => {
+ const onClickCardGridItem = (e: MouseEvent, item: CardGridItem) => {
if (!canCreateDataSources) {
e.preventDefault();
e.stopPropagation();
diff --git a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts
index 3f7ade11335..b954cdd3ecc 100644
--- a/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts
+++ b/public/app/features/dashboard-scene/pages/DashboardScenePageStateManager.ts
@@ -255,6 +255,10 @@ export class DashboardScenePageStateManager extends StateManagerBase ({
- href: `/dashboard/${link.title}`,
- title: link.title,
- tooltip: link.tooltip || null,
-}));
-
-// Mock the getLinkSrv function
-jest.mock('app/features/panel/panellinks/link_srv', () => ({
- getLinkSrv: jest.fn(() => ({
- getAnchorInfo: mockGetAnchorInfo,
- })),
-}));
-
describe('DashboardControls', () => {
describe('Given a standard scene', () => {
it('should initialize with default values', () => {
- const { controls: scene } = buildTestScene();
+ const scene = buildTestScene();
expect(scene.state.variableControls).toEqual([]);
expect(scene.state.timePicker).toBeDefined();
expect(scene.state.refreshPicker).toBeDefined();
});
it('should return if time controls are hidden', () => {
- const { controls: scene } = buildTestScene({
+ const scene = buildTestScene({
hideTimeControls: false,
hideVariableControls: false,
hideLinksControls: false,
@@ -45,14 +31,14 @@ describe('DashboardControls', () => {
describe('Component', () => {
it('should render', () => {
- const { controls: scene } = buildTestScene();
+ const scene = buildTestScene();
expect(() => {
render( );
}).not.toThrow();
});
it('should render visible controls', async () => {
- const { controls: scene } = buildTestScene({
+ const scene = buildTestScene({
variableControls: [new VariableValueSelectors({}), new SceneDataLayerControls()],
});
const renderer = render( );
@@ -65,7 +51,7 @@ describe('DashboardControls', () => {
});
it('should render with hidden controls', async () => {
- const { controls: scene } = buildTestScene({
+ const scene = buildTestScene({
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
@@ -79,13 +65,13 @@ describe('DashboardControls', () => {
describe('UrlSync', () => {
it('should return keys', () => {
- const { controls: scene } = buildTestScene();
+ const scene = buildTestScene();
// @ts-expect-error
expect(scene._urlSync.getKeys()).toEqual(['_dash.hideTimePicker', '_dash.hideVariables', '_dash.hideLinks']);
});
it('should not return url state for hide flags', () => {
- const { controls: scene } = buildTestScene();
+ const scene = buildTestScene();
expect(scene.getUrlState()).toEqual({});
scene.setState({
hideTimeControls: true,
@@ -96,7 +82,7 @@ describe('DashboardControls', () => {
});
it('should update from url', () => {
- const { controls: scene } = buildTestScene();
+ const scene = buildTestScene();
scene.updateFromUrl({
'_dash.hideTimePicker': 'true',
'_dash.hideVariables': 'true',
@@ -116,7 +102,7 @@ describe('DashboardControls', () => {
});
it('should not override state if no new state comes from url', () => {
- const { controls: scene } = buildTestScene({
+ const scene = buildTestScene({
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
@@ -128,7 +114,7 @@ describe('DashboardControls', () => {
});
it('should not call setState if no changes', () => {
- const { controls: scene } = buildTestScene({
+ const scene = buildTestScene({
hideTimeControls: true,
hideVariableControls: true,
hideLinksControls: true,
@@ -144,34 +130,9 @@ describe('DashboardControls', () => {
expect(setState).toHaveBeenCalledTimes(0);
});
});
-
- it('Should update link hrefs when time range changes', () => {
- const { controls, dashboard } = buildTestScene();
- render( );
-
- //clear initial calls to getAnchorInfo
- mockGetAnchorInfo.mockClear();
-
- act(() => {
- // Update time range
- dashboard.state.$timeRange?.setState({
- value: {
- from: toUtc('2021-01-01'),
- to: toUtc('2021-01-02'),
- raw: { from: toUtc('2020-01-01'), to: toUtc('2020-01-02') },
- },
- });
- });
-
- //expect getAnchorInfo to be called after time range change
- expect(mockGetAnchorInfo).toHaveBeenCalledTimes(1);
- });
});
-function buildTestScene(state?: Partial): {
- dashboard: DashboardScene;
- controls: DashboardControls;
-} {
+function buildTestScene(state?: Partial): DashboardControls {
const variable = new TextBoxVariable({
name: 'A',
label: 'A',
@@ -206,5 +167,5 @@ function buildTestScene(state?: Partial): {
dashboard.activate();
variable.activate();
- return { dashboard, controls: dashboard.state.controls as DashboardControls };
+ return dashboard.state.controls as DashboardControls;
}
diff --git a/public/app/features/dashboard-scene/scene/DashboardControls.tsx b/public/app/features/dashboard-scene/scene/DashboardControls.tsx
index 41e125957a5..78fbc64afd8 100644
--- a/public/app/features/dashboard-scene/scene/DashboardControls.tsx
+++ b/public/app/features/dashboard-scene/scene/DashboardControls.tsx
@@ -122,10 +122,9 @@ function DashboardControlsRenderer({ model }: SceneComponentProps
{!hideVariableControls && variableControls.map((c) => )}
- {!hideLinksControls && !editPanel && }
+ {!hideLinksControls && !editPanel && }
{editPanel && }
{!hideTimeControls && (
diff --git a/public/app/features/dashboard-scene/scene/DashboardLinksControls.test.tsx b/public/app/features/dashboard-scene/scene/DashboardLinksControls.test.tsx
new file mode 100644
index 00000000000..d6d89a2e63f
--- /dev/null
+++ b/public/app/features/dashboard-scene/scene/DashboardLinksControls.test.tsx
@@ -0,0 +1,100 @@
+import { act, render } from '@testing-library/react';
+
+import { toUtc } from '@grafana/data';
+import { selectors } from '@grafana/e2e-selectors';
+import { SceneTimeRange } from '@grafana/scenes';
+
+import { DashboardControls } from './DashboardControls';
+import { DashboardScene } from './DashboardScene';
+
+const mockGetAnchorInfo = jest.fn((link) => ({
+ href: `/dashboard/${link.title}`,
+ title: link.title,
+ tooltip: link.tooltip || null,
+}));
+
+// Mock the getLinkSrv function
+jest.mock('app/features/panel/panellinks/link_srv', () => ({
+ getLinkSrv: jest.fn(() => ({
+ getAnchorInfo: mockGetAnchorInfo,
+ })),
+}));
+
+describe('DashboardLinksControls', () => {
+ it('renders dashboard links correctly', () => {
+ const { controls } = buildTestScene();
+ const renderer = render( );
+
+ // // Expect two dashboard link containers to be rendered
+ const linkContainers = renderer.getAllByTestId(selectors.components.DashboardLinks.container);
+ expect(linkContainers).toHaveLength(2);
+
+ // Check link titles and hrefs
+ const links = renderer.getAllByTestId(selectors.components.DashboardLinks.link);
+ expect(links[0]).toHaveTextContent('Link 1');
+ expect(links[1]).toHaveTextContent('Link 2');
+ });
+
+ it('updates link hrefs when time range changes', () => {
+ const { controls, dashboard } = buildTestScene();
+ render( );
+
+ //clear initial calls to getAnchorInfo
+ mockGetAnchorInfo.mockClear();
+
+ act(() => {
+ // Update time range
+ dashboard.state.$timeRange?.setState({
+ value: {
+ from: toUtc('2021-01-01'),
+ to: toUtc('2021-01-02'),
+ raw: { from: toUtc('2020-01-01'), to: toUtc('2020-01-02') },
+ },
+ });
+ });
+
+ //expect getAnchorInfo to be called twice, once for each link, after time range change
+ expect(mockGetAnchorInfo).toHaveBeenCalledTimes(2);
+ });
+});
+
+function buildTestScene(): { controls: DashboardControls; dashboard: DashboardScene } {
+ const dashboard = new DashboardScene({
+ uid: 'A',
+ links: [
+ {
+ title: 'Link 1',
+ url: 'http://localhost:3000/$A',
+ type: 'link',
+ asDropdown: false,
+ icon: '',
+ includeVars: true,
+ keepTime: true,
+ tags: [],
+ targetBlank: false,
+ tooltip: 'Link 1',
+ },
+ {
+ title: 'Link 2',
+ url: 'http://localhost:3000/$A',
+ type: 'link',
+ asDropdown: false,
+ icon: '',
+ includeVars: true,
+ keepTime: true,
+ tags: [],
+ targetBlank: false,
+ tooltip: 'Link 2',
+ },
+ ],
+ controls: new DashboardControls({}),
+ $timeRange: new SceneTimeRange({
+ from: 'now-1',
+ to: 'now',
+ }),
+ });
+
+ dashboard.activate();
+
+ return { controls: dashboard.state.controls as DashboardControls, dashboard };
+}
diff --git a/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx b/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx
index 0337580526a..72a046fc6e5 100644
--- a/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx
+++ b/public/app/features/dashboard-scene/scene/DashboardLinksControls.tsx
@@ -1,5 +1,6 @@
import { sanitizeUrl } from '@grafana/data/src/text/sanitize';
import { selectors } from '@grafana/e2e-selectors';
+import { sceneGraph } from '@grafana/scenes';
import { DashboardLink } from '@grafana/schema';
import { Tooltip } from '@grafana/ui';
import {
@@ -10,12 +11,17 @@ import { getLinkSrv } from 'app/features/panel/panellinks/link_srv';
import { LINK_ICON_MAP } from '../settings/links/utils';
+import { DashboardScene } from './DashboardScene';
+
export interface Props {
links: DashboardLink[];
- uid?: string;
+ dashboard: DashboardScene;
}
-export function DashboardLinksControls({ links, uid }: Props) {
+export function DashboardLinksControls({ links, dashboard }: Props) {
+ sceneGraph.getTimeRange(dashboard).useState();
+ const uid = dashboard.state.uid;
+
if (!links || !uid) {
return null;
}
diff --git a/public/app/features/datasources/components/DataSourceAddButton.tsx b/public/app/features/datasources/components/DataSourceAddButton.tsx
index 8d7de1d5cc0..3d5d9fead3f 100644
--- a/public/app/features/datasources/components/DataSourceAddButton.tsx
+++ b/public/app/features/datasources/components/DataSourceAddButton.tsx
@@ -2,16 +2,14 @@ import { config } from '@grafana/runtime';
import { LinkButton } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { Trans } from 'app/core/internationalization';
+import { ROUTES } from 'app/features/connections/constants';
import { AccessControlAction } from 'app/types';
-import { useDataSourcesRoutes } from '../state';
-
export function DataSourceAddButton(): JSX.Element | null {
const canCreateDataSource = contextSrv.hasPermission(AccessControlAction.DataSourcesCreate);
- const dataSourcesRoutes = useDataSourcesRoutes();
return canCreateDataSource ? (
-
+
Add new data source
) : null;
diff --git a/public/app/features/datasources/components/DataSourcesList.tsx b/public/app/features/datasources/components/DataSourcesList.tsx
index bf5a3f2f829..e9cf467c97f 100644
--- a/public/app/features/datasources/components/DataSourcesList.tsx
+++ b/public/app/features/datasources/components/DataSourcesList.tsx
@@ -9,7 +9,8 @@ import { contextSrv } from 'app/core/core';
import { Trans, t } from 'app/core/internationalization';
import { StoreState, AccessControlAction, useSelector } from 'app/types';
-import { getDataSources, getDataSourcesCount, useDataSourcesRoutes, useLoadDataSources } from '../state';
+import { ROUTES } from '../../connections/constants';
+import { getDataSources, getDataSourcesCount, useLoadDataSources } from '../state';
import { trackDataSourcesListViewed } from '../tracking';
import { DataSourcesListCard } from './DataSourcesListCard';
@@ -54,7 +55,6 @@ export function DataSourcesListView({
hasExploreRights,
}: ViewProps) {
const styles = useStyles2(getStyles);
- const dataSourcesRoutes = useDataSourcesRoutes();
const location = useLocation();
useEffect(() => {
@@ -69,7 +69,7 @@ export function DataSourcesListView({
+
Add data source
}
diff --git a/public/app/features/datasources/components/DataSourcesListCard.tsx b/public/app/features/datasources/components/DataSourcesListCard.tsx
index f93c3d91def..0c77ea501aa 100644
--- a/public/app/features/datasources/components/DataSourcesListCard.tsx
+++ b/public/app/features/datasources/components/DataSourcesListCard.tsx
@@ -5,7 +5,7 @@ import { DataSourceSettings, GrafanaTheme2 } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Card, LinkButton, Stack, Tag, useStyles2 } from '@grafana/ui';
-import { useDataSourcesRoutes } from '../state';
+import { ROUTES } from '../../connections/constants';
import { trackCreateDashboardClicked, trackExploreClicked } from '../tracking';
import { constructDataSourceExploreUrl } from '../utils';
@@ -16,8 +16,7 @@ export interface Props {
}
export function DataSourcesListCard({ dataSource, hasWriteRights, hasExploreRights }: Props) {
- const dataSourcesRoutes = useDataSourcesRoutes();
- const dsLink = config.appSubUrl + dataSourcesRoutes.Edit.replace(/:uid/gi, dataSource.uid);
+ const dsLink = config.appSubUrl + ROUTES.DataSourcesEdit.replace(/:uid/gi, dataSource.uid);
const styles = useStyles2(getStyles);
return (
diff --git a/public/app/features/datasources/components/NewDataSource.tsx b/public/app/features/datasources/components/NewDataSource.tsx
index 86501a76477..e24ab0953fa 100644
--- a/public/app/features/datasources/components/NewDataSource.tsx
+++ b/public/app/features/datasources/components/NewDataSource.tsx
@@ -1,4 +1,4 @@
-import { AnyAction } from 'redux';
+import { Action } from 'redux';
import { DataSourcePluginMeta, PluginType } from '@grafana/data';
import { LinkButton, FilterInput } from '@grafana/ui';
@@ -6,6 +6,7 @@ import PageLoader from 'app/core/components/PageLoader/PageLoader';
import { PluginsErrorsInfo } from 'app/features/plugins/components/PluginsErrorsInfo';
import { DataSourcePluginCategory, StoreState, useDispatch, useSelector } from 'app/types';
+import { ROUTES } from '../../connections/constants';
import { DataSourceCategories } from '../components/DataSourceCategories';
import { DataSourceTypeCardList } from '../components/DataSourceTypeCardList';
import {
@@ -13,7 +14,6 @@ import {
useLoadDataSourcePlugins,
getFilteredDataSourcePlugins,
setDataSourceTypeSearchQuery,
- useDataSourcesRoutes,
} from '../state';
export function NewDataSource() {
@@ -45,7 +45,7 @@ export type ViewProps = {
searchQuery: string;
isLoading: boolean;
onAddDataSource: (dataSource: DataSourcePluginMeta) => void;
- onSetSearchQuery: (q: string) => AnyAction;
+ onSetSearchQuery: (q: string) => Action;
};
export function NewDataSourceView({
@@ -56,8 +56,6 @@ export function NewDataSourceView({
onAddDataSource,
onSetSearchQuery,
}: ViewProps) {
- const dataSourcesRoutes = useDataSourcesRoutes();
-
if (isLoading) {
return ;
}
@@ -68,7 +66,7 @@ export function NewDataSourceView({
diff --git a/public/app/features/datasources/state/contexts.ts b/public/app/features/datasources/state/contexts.ts
deleted file mode 100644
index df94279c2f5..00000000000
--- a/public/app/features/datasources/state/contexts.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { createContext } from 'react';
-
-import { DATASOURCES_ROUTES } from '../constants';
-import { DataSourcesRoutes } from '../types';
-
-// The purpose of this context is to be able to override the data-sources routes (used for links for example) used under
-// the app/features/datasources modules, so we can reuse them more easily in different parts of the application (e.g. under Connections)
-export const DataSourcesRoutesContext = createContext(DATASOURCES_ROUTES);
diff --git a/public/app/features/datasources/state/hooks.ts b/public/app/features/datasources/state/hooks.ts
index 11587a60d42..d2c956a2a4b 100644
--- a/public/app/features/datasources/state/hooks.ts
+++ b/public/app/features/datasources/state/hooks.ts
@@ -1,4 +1,4 @@
-import { useContext, useEffect } from 'react';
+import { useEffect } from 'react';
import { DataSourcePluginMeta, DataSourceSettings } from '@grafana/data';
import { cleanUpAction } from 'app/core/actions/cleanUp';
@@ -7,6 +7,7 @@ import { contextSrv } from 'app/core/core';
import { AccessControlAction, useDispatch, useSelector } from 'app/types';
import { ShowConfirmModalEvent } from 'app/types/events';
+import { ROUTES } from '../../connections/constants';
import { DataSourceRights } from '../types';
import { constructDataSourceExploreUrl } from '../utils';
@@ -20,7 +21,6 @@ import {
updateDataSource,
deleteLoadedDataSource,
} from './actions';
-import { DataSourcesRoutesContext } from './contexts';
import { initialDataSourceSettingsState } from './reducers';
import { getDataSource, getDataSourceMeta } from './selectors';
@@ -42,9 +42,8 @@ export const useInitDataSourceSettings = (uid: string) => {
export const useTestDataSource = (uid: string) => {
const dispatch = useDispatch();
- const dataSourcesRoutes = useDataSourcesRoutes();
- return () => dispatch(testDataSource(uid, dataSourcesRoutes.Edit));
+ return () => dispatch(testDataSource(uid, ROUTES.DataSourcesEdit));
};
export const useLoadDataSources = () => {
@@ -77,10 +76,9 @@ export const useLoadDataSourcePlugins = () => {
export const useAddDatasource = () => {
const dispatch = useDispatch();
- const dataSourcesRoutes = useDataSourcesRoutes();
return (plugin: DataSourcePluginMeta) => {
- dispatch(addDataSource(plugin, dataSourcesRoutes.Edit));
+ dispatch(addDataSource(plugin, ROUTES.DataSourcesEdit));
};
};
@@ -136,7 +134,3 @@ export const useDataSourceRights = (uid: string): DataSourceRights => {
hasDeleteRights,
};
};
-
-export const useDataSourcesRoutes = () => {
- return useContext(DataSourcesRoutesContext);
-};
diff --git a/public/app/features/datasources/state/index.ts b/public/app/features/datasources/state/index.ts
index 37a2d6f5cf0..42fae7a84a7 100644
--- a/public/app/features/datasources/state/index.ts
+++ b/public/app/features/datasources/state/index.ts
@@ -1,6 +1,5 @@
export * from './actions';
export * from './buildCategories';
-export * from './contexts';
export * from './hooks';
export * from './navModel';
export * from './reducers';
diff --git a/public/app/features/migrate-to-cloud/onprem/NameCell.tsx b/public/app/features/migrate-to-cloud/onprem/NameCell.tsx
index b978b1d1b27..62a807c3a4c 100644
--- a/public/app/features/migrate-to-cloud/onprem/NameCell.tsx
+++ b/public/app/features/migrate-to-cloud/onprem/NameCell.tsx
@@ -219,6 +219,12 @@ function ResourceIcon({ resource }: { resource: ResourceTableItem }) {
return ;
case 'LIBRARY_ELEMENT':
return ;
+ case 'MUTE_TIMING':
+ return ;
+ case 'NOTIFICATION_TEMPLATE':
+ return ;
+ case 'CONTACT_POINT':
+ return ;
default:
return undefined;
}
diff --git a/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx b/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx
index 37e962d7b53..50ada9b4d55 100644
--- a/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx
+++ b/public/app/features/migrate-to-cloud/onprem/TypeCell.tsx
@@ -13,6 +13,12 @@ export function prettyTypeName(type: ResourceTableItem['type']) {
return t('migrate-to-cloud.resource-type.folder', 'Folder');
case 'LIBRARY_ELEMENT':
return t('migrate-to-cloud.resource-type.library_element', 'Library Element');
+ case 'MUTE_TIMING':
+ return t('migrate-to-cloud.resource-type.mute_timing', 'Mute Timing');
+ case 'NOTIFICATION_TEMPLATE':
+ return t('migrate-to-cloud.resource-type.notification_template', 'Notification Template');
+ case 'CONTACT_POINT':
+ return t('migrate-to-cloud.resource-type.contact_point', 'Contact Point');
default:
return t('migrate-to-cloud.resource-type.unknown', 'Unknown');
}
diff --git a/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx b/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx
index 705da66b693..aeeec493993 100644
--- a/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx
+++ b/public/app/features/migrate-to-cloud/onprem/useNotifyOnSuccess.tsx
@@ -52,6 +52,12 @@ function getTranslatedMessage(snapshot: GetSnapshotResponseDto) {
types.push(t('migrate-to-cloud.migrated-counts.folders', 'folders'));
} else if (type === 'LIBRARY_ELEMENT') {
types.push(t('migrate-to-cloud.migrated-counts.library_elements', 'library elements'));
+ } else if (type === 'MUTE_TIMING') {
+ types.push(t('migrate-to-cloud.migrated-counts.mute_timings', 'mute timings'));
+ } else if (type === 'NOTIFICATION_TEMPLATE') {
+ types.push(t('migrate-to-cloud.migrated-counts.notification_templates', 'notification templates'));
+ } else if (type === 'CONTACT_POINT') {
+ types.push(t('migrate-to-cloud.migrated-counts.contact_points', 'contact points'));
}
distinctItems += 1;
diff --git a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx
index 2c45596367f..22ba118489b 100644
--- a/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx
+++ b/public/app/features/plugins/admin/components/GetStartedWithPlugin/GetStartedWithDataSource.tsx
@@ -4,7 +4,8 @@ import * as React from 'react';
import { DataSourcePluginMeta } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Button } from '@grafana/ui';
-import { useDataSourcesRoutes, addDataSource } from 'app/features/datasources/state';
+import { ROUTES } from 'app/features/connections/constants';
+import { addDataSource } from 'app/features/datasources/state';
import { useDispatch } from 'app/types';
import { isDataSourceEditor } from '../../permissions';
@@ -16,15 +17,14 @@ type Props = {
export function GetStartedWithDataSource({ plugin }: Props): React.ReactElement | null {
const dispatch = useDispatch();
- const dataSourcesRoutes = useDataSourcesRoutes();
const onAddDataSource = useCallback(() => {
const meta = {
name: plugin.name,
id: plugin.id,
} as DataSourcePluginMeta;
- dispatch(addDataSource(meta, dataSourcesRoutes.Edit));
- }, [dispatch, plugin, dataSourcesRoutes]);
+ dispatch(addDataSource(meta, ROUTES.DataSourcesEdit));
+ }, [dispatch, plugin]);
if (!isDataSourceEditor()) {
return null;
diff --git a/public/app/features/plugins/extensions/logs/log.ts b/public/app/features/plugins/extensions/logs/log.ts
index a78d42ccea0..fc87ba3019b 100644
--- a/public/app/features/plugins/extensions/logs/log.ts
+++ b/public/app/features/plugins/extensions/logs/log.ts
@@ -32,10 +32,12 @@ export class ExtensionsLog {
}
warning(message: string, labels?: Labels): void {
+ console.warn(message, labels);
this.log(LogLevel.warning, message, labels);
}
error(message: string, labels?: Labels): void {
+ console.error(message, labels);
this.log(LogLevel.error, message, labels);
}
diff --git a/public/app/features/query/components/QueryGroupOptions.tsx b/public/app/features/query/components/QueryGroupOptions.tsx
index 8fc36b47e1e..c949e1767b2 100644
--- a/public/app/features/query/components/QueryGroupOptions.tsx
+++ b/public/app/features/query/components/QueryGroupOptions.tsx
@@ -1,11 +1,9 @@
-import { css } from '@emotion/css';
-import { PureComponent, ChangeEvent, FocusEvent } from 'react';
-import * as React from 'react';
+import { css, cx } from '@emotion/css';
+import React, { useState, ChangeEvent, FocusEvent, useCallback } from 'react';
-import { rangeUtil, PanelData, DataSourceApi } from '@grafana/data';
-import { Input, InlineFormLabel, stylesFactory, InlineFieldRow, InlineSwitch } from '@grafana/ui';
+import { rangeUtil, PanelData, DataSourceApi, GrafanaTheme2 } from '@grafana/data';
+import { Input, InlineSwitch, useStyles2, InlineLabel } from '@grafana/ui';
import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
-import { config } from 'app/core/config';
import { QueryGroupOptions } from 'app/types';
interface Props {
@@ -15,149 +13,142 @@ interface Props {
onChange: (options: QueryGroupOptions) => void;
}
-interface State {
- timeRangeFrom: string;
- timeRangeShift: string;
- timeRangeHide: boolean;
- isOpen: boolean;
- relativeTimeIsValid: boolean;
- timeShiftIsValid: boolean;
-}
+export const QueryGroupOptionsEditor = React.memo(({ options, dataSource, data, onChange }: Props) => {
+ const [timeRangeFrom, setTimeRangeFrom] = useState(options.timeRange?.from || '');
+ const [timeRangeShift, setTimeRangeShift] = useState(options.timeRange?.shift || '');
+ const [timeRangeHide, setTimeRangeHide] = useState(options.timeRange?.hide ?? false);
+ const [isOpen, setIsOpen] = useState(false);
+ const [relativeTimeIsValid, setRelativeTimeIsValid] = useState(true);
+ const [timeShiftIsValid, setTimeShiftIsValid] = useState(true);
-export class QueryGroupOptionsEditor extends PureComponent {
- constructor(props: Props) {
- super(props);
+ const styles = useStyles2(getStyles);
- const { options } = props;
+ const onRelativeTimeChange = useCallback((event: ChangeEvent) => {
+ setTimeRangeFrom(event.target.value);
+ }, []);
- this.state = {
- timeRangeFrom: options.timeRange?.from || '',
- timeRangeShift: options.timeRange?.shift || '',
- timeRangeHide: options.timeRange?.hide ?? false,
- isOpen: false,
- relativeTimeIsValid: true,
- timeShiftIsValid: true,
- };
- }
+ const onTimeShiftChange = useCallback((event: ChangeEvent) => {
+ setTimeRangeShift(event.target.value);
+ }, []);
- onRelativeTimeChange = (event: ChangeEvent) => {
- this.setState({
- timeRangeFrom: event.target.value,
- });
- };
+ const onOverrideTime = useCallback(
+ (event: FocusEvent) => {
+ const newValue = emptyToNull(event.target.value);
+ const isValid = timeRangeValidation(newValue);
- onTimeShiftChange = (event: ChangeEvent) => {
- this.setState({
- timeRangeShift: event.target.value,
- });
- };
+ if (isValid && options.timeRange?.from !== newValue) {
+ onChange({
+ ...options,
+ timeRange: {
+ ...(options.timeRange ?? {}),
+ from: newValue,
+ },
+ });
+ }
- onOverrideTime = (event: FocusEvent) => {
- const { options, onChange } = this.props;
+ setRelativeTimeIsValid(isValid);
+ },
+ [onChange, options]
+ );
- const newValue = emptyToNull(event.target.value);
- const isValid = timeRangeValidation(newValue);
+ const onTimeShift = useCallback(
+ (event: FocusEvent) => {
+ const newValue = emptyToNull(event.target.value);
+ const isValid = timeRangeValidation(newValue);
- if (isValid && options.timeRange?.from !== newValue) {
- onChange({
- ...options,
- timeRange: {
- ...(options.timeRange ?? {}),
- from: newValue,
- },
- });
- }
+ if (isValid && options.timeRange?.shift !== newValue) {
+ onChange({
+ ...options,
+ timeRange: {
+ ...(options.timeRange ?? {}),
+ shift: newValue,
+ },
+ });
+ }
- this.setState({ relativeTimeIsValid: isValid });
- };
+ setTimeShiftIsValid(isValid);
+ },
+ [onChange, options]
+ );
- onTimeShift = (event: FocusEvent) => {
- const { options, onChange } = this.props;
-
- const newValue = emptyToNull(event.target.value);
- const isValid = timeRangeValidation(newValue);
-
- if (isValid && options.timeRange?.shift !== newValue) {
- onChange({
- ...options,
- timeRange: {
- ...(options.timeRange ?? {}),
- shift: newValue,
- },
- });
- }
-
- this.setState({ timeShiftIsValid: isValid });
- };
-
- onToggleTimeOverride = () => {
- const { onChange, options } = this.props;
-
- this.setState({ timeRangeHide: !this.state.timeRangeHide }, () => {
- onChange({
- ...options,
- timeRange: {
- ...(options.timeRange ?? {}),
- hide: this.state.timeRangeHide,
- },
- });
- });
- };
-
- onCacheTimeoutBlur = (event: ChangeEvent) => {
- const { options, onChange } = this.props;
+ const onToggleTimeOverride = useCallback(() => {
+ const newTimeRangeHide = !timeRangeHide;
+ setTimeRangeHide(newTimeRangeHide);
onChange({
...options,
- cacheTimeout: emptyToNull(event.target.value),
+ timeRange: {
+ ...(options.timeRange ?? {}),
+ hide: newTimeRangeHide,
+ },
});
- };
+ }, [onChange, options, timeRangeHide]);
- onQueryCachingTTLBlur = (event: ChangeEvent) => {
- const { options, onChange } = this.props;
-
- let ttl: number | null = parseInt(event.target.value, 10);
-
- if (isNaN(ttl) || ttl === 0) {
- ttl = null;
- }
-
- onChange({
- ...options,
- queryCachingTTL: ttl,
- });
- };
-
- onMaxDataPointsBlur = (event: ChangeEvent) => {
- const { options, onChange } = this.props;
-
- let maxDataPoints: number | null = parseInt(event.currentTarget.value, 10);
-
- if (isNaN(maxDataPoints) || maxDataPoints === 0) {
- maxDataPoints = null;
- }
-
- if (maxDataPoints !== options.maxDataPoints) {
+ const onCacheTimeoutBlur = useCallback(
+ (event: ChangeEvent) => {
onChange({
...options,
- maxDataPoints,
+ cacheTimeout: emptyToNull(event.target.value),
});
- }
- };
+ },
+ [onChange, options]
+ );
+
+ const onQueryCachingTTLBlur = useCallback(
+ (event: ChangeEvent) => {
+ let ttl: number | null = parseInt(event.target.value, 10);
+
+ if (isNaN(ttl) || ttl === 0) {
+ ttl = null;
+ }
- onMinIntervalBlur = (event: ChangeEvent) => {
- const { options, onChange } = this.props;
- const minInterval = emptyToNull(event.target.value);
- if (minInterval !== options.minInterval) {
onChange({
...options,
- minInterval,
+ queryCachingTTL: ttl,
});
- }
- };
+ },
+ [onChange, options]
+ );
- renderCacheTimeoutOption() {
- const { dataSource, options } = this.props;
+ const onMaxDataPointsBlur = useCallback(
+ (event: ChangeEvent) => {
+ let maxDataPoints: number | null = parseInt(event.currentTarget.value, 10);
+ if (isNaN(maxDataPoints) || maxDataPoints === 0) {
+ maxDataPoints = null;
+ }
+
+ if (maxDataPoints !== options.maxDataPoints) {
+ onChange({
+ ...options,
+ maxDataPoints,
+ });
+ }
+ },
+ [onChange, options]
+ );
+
+ const onMinIntervalBlur = useCallback(
+ (event: ChangeEvent) => {
+ const minInterval = emptyToNull(event.target.value);
+ if (minInterval !== options.minInterval) {
+ onChange({
+ ...options,
+ minInterval,
+ });
+ }
+ },
+ [onChange, options]
+ );
+
+ const onOpenOptions = useCallback(() => {
+ setIsOpen(true);
+ }, []);
+
+ const onCloseOptions = useCallback(() => {
+ setIsOpen(false);
+ }, []);
+
+ const renderCacheTimeoutOption = () => {
const tooltip = `If your time series store has a query cache this option can override the default cache timeout. Specify a
numeric value in seconds.`;
@@ -166,27 +157,23 @@ export class QueryGroupOptionsEditor extends PureComponent {
}
return (
-
-
-
- Cache timeout
-
-
-
-
+ <>
+
+ Cache timeout
+
+
+ >
);
- }
-
- renderQueryCachingTTLOption() {
- const { dataSource, options } = this.props;
+ };
+ const renderQueryCachingTTLOption = () => {
const tooltip = `Cache time-to-live: How long results from this queries in this panel will be cached, in milliseconds. Defaults to the TTL in the caching configuration for this datasource.`;
if (!dataSource.cachingConfig?.enabled) {
@@ -194,129 +181,101 @@ export class QueryGroupOptionsEditor extends PureComponent {
}
return (
-
+ <>
+ Cache TTL
+
+ >
);
- }
+ };
- renderMaxDataPointsOption() {
- const { data, options } = this.props;
+ const renderMaxDataPointsOption = () => {
const realMd = data.request?.maxDataPoints;
const value = options.maxDataPoints ?? '';
const isAuto = value === '';
return (
-
-
-
- The maximum data points per series. Used directly by some data sources and used in calculation of auto
- interval. With streaming data this value is used for the rolling buffer.
- >
- }
- >
- Max data points
-
-
- {isAuto && (
+ <>
+
- =
- Width of panel
+ The maximum data points per series. Used directly by some data sources and used in calculation of auto
+ interval. With streaming data this value is used for the rolling buffer.
>
- )}
-
-
+ }
+ >
+ Max data points
+
+
+ {isAuto && (
+ <>
+ =
+ Width of panel
+ >
+ )}
+ >
);
- }
+ };
- renderIntervalOption() {
- const { data, dataSource, options } = this.props;
+ const renderIntervalOption = () => {
const realInterval = data.request?.interval;
const minIntervalOnDs = dataSource.interval ?? 'No limit';
return (
<>
-
-
-
- A lower limit for the interval. Recommended to be set to write frequency, for example 1m{' '}
- if your data is written every minute. Default value can be set in data source settings for most data
- sources.
- >
- }
- >
- Min interval
-
-
-
-
-
-
-
- The evaluated interval that is sent to data source and is used in $__interval and{' '}
- $__interval_ms. This value is not exactly equal to{' '}
- Time range / max data points, it will approximate a series of magic number.
- >
- }
- >
- Interval
-
-
{realInterval}
-
=
-
Time range / max data points
-
-
+
+ A lower limit for the interval. Recommended to be set to write frequency, for example 1m if
+ your data is written every minute. Default value can be set in data source settings for most data sources.
+ >
+ }
+ htmlFor="min-interval-input"
+ >
+ Min interval
+
+
+
+ The evaluated interval that is sent to data source and is used in $__interval and{' '}
+ $__interval_ms. This value is not exactly equal to Time range / max data points,
+ it will approximate a series of magic number.
+ >
+ }
+ >
+ Interval
+
+ {realInterval}
+ =
+ Time range / max data points
>
);
- }
-
- onOpenOptions = () => {
- this.setState({ isOpen: true });
};
- onCloseOptions = () => {
- this.setState({ isOpen: false });
- };
-
- renderCollapsedText(styles: StylesType): React.ReactNode | undefined {
- const { data, options } = this.props;
- const { isOpen } = this.state;
-
+ const renderCollapsedText = (): React.ReactNode | undefined => {
if (isOpen) {
return undefined;
}
@@ -326,120 +285,135 @@ export class QueryGroupOptionsEditor extends PureComponent {
mdDesc = `auto = ${data.request.maxDataPoints}`;
}
- let intervalDesc = options.minInterval;
- if (data.request) {
- intervalDesc = `${data.request.interval}`;
- }
+ const intervalDesc = data.request?.interval ?? options.minInterval;
return (
<>
- {MD = {mdDesc}
}
- {Interval = {intervalDesc}
}
+ {MD = {mdDesc} }
+ {Interval = {intervalDesc} }
>
);
- }
-
- render() {
- const { timeRangeHide: hideTimeOverride, relativeTimeIsValid, timeShiftIsValid } = this.state;
- const { timeRangeFrom: relativeTime, timeRangeShift: timeShift, isOpen } = this.state;
- const styles = getStyles();
-
- return (
-
- {this.renderMaxDataPointsOption()}
- {this.renderIntervalOption()}
- {this.renderCacheTimeoutOption()}
- {this.renderQueryCachingTTLOption()}
-
-
-
- Overrides the relative time range for individual panels, which causes them to be different than what is
- selected in the dashboard time picker in the top-right corner of the dashboard. For example to configure
- the Last 5 minutes the Relative time should be now-5m and 5m, or variables
- like $_relativeTime.
- >
- }
- >
- Relative time
-
-
-
-
-
-
- Overrides the time range for individual panels by shifting its start and end relative to the time
- picker. For example to configure the Last 1h the Time shift should be now-1h and{' '}
- 1h, or variables like $_timeShift.
- >
- }
- >
- Time shift
-
-
-
- {(timeShift || relativeTime) && (
-
- Hide time info
-
-
- )}
-
- );
- }
-}
-
-const timeRangeValidation = (value: string | null) => {
- if (!value) {
- return true;
- }
-
- return rangeUtil.isValidTimeSpan(value);
-};
-
-const emptyToNull = (value: string) => {
- return value === '' ? null : value;
-};
-
-const getStyles = stylesFactory(() => {
- const { theme } = config;
-
- return {
- collapsedText: css`
- margin-left: ${theme.spacing.md};
- font-size: ${theme.typography.size.sm};
- color: ${theme.colors.textWeak};
- `,
};
+
+ return (
+
+
+ {renderMaxDataPointsOption()}
+ {renderIntervalOption()}
+ {renderCacheTimeoutOption()}
+ {renderQueryCachingTTLOption()}
+
+
+ Overrides the relative time range for individual panels, which causes them to be different than what is
+ selected in the dashboard time picker in the top-right corner of the dashboard. For example to configure
+ the Last 5 minutes the Relative time should be now-5m and 5m, or variables like{' '}
+ $_relativeTime.
+ >
+ }
+ >
+ Relative time
+
+
+
+ Overrides the time range for individual panels by shifting its start and end relative to the time picker.
+ For example to configure the Last 1h the Time shift should be now-1h and 1h, or
+ variables like $_timeShift.
+ >
+ }
+ >
+ Time shift
+
+
+ {(timeRangeShift || timeRangeFrom) && (
+ <>
+
+ Hide time info
+
+
+ >
+ )}
+
+
+ );
});
-type StylesType = ReturnType;
+QueryGroupOptionsEditor.displayName = 'QueryGroupOptionsEditor';
+
+function timeRangeValidation(value: string | null) {
+ return !value || rangeUtil.isValidTimeSpan(value);
+}
+
+function emptyToNull(value: string) {
+ return value === '' ? null : value;
+}
+
+function getStyles(theme: GrafanaTheme2) {
+ return {
+ grid: css({
+ display: 'grid',
+ gridTemplateColumns: `auto minmax(5em, 1fr) auto 1fr`,
+ gap: theme.spacing(0.5),
+ gridAutoRows: theme.spacing(4),
+ whiteSpace: 'nowrap',
+ }),
+ firstColumn: css({
+ gridColumn: 1,
+ }),
+ collapsedText: css({
+ marginLeft: theme.spacing(2),
+ fontSize: theme.typography.size.sm,
+ color: theme.colors.text.secondary,
+ }),
+ noSquish: css({
+ display: 'flex',
+ alignItems: 'center',
+ padding: theme.spacing(0, 1),
+ fontWeight: theme.typography.fontWeightMedium,
+ fontSize: theme.typography.size.sm,
+ backgroundColor: theme.colors.background.secondary,
+ borderRadius: theme.shape.radius.default,
+ }),
+ left: css({
+ justifySelf: 'left',
+ }),
+ operator: css({
+ color: theme.v1.palette.orange,
+ }),
+ };
+}
diff --git a/public/app/plugins/datasource/azuremonitor/project.json b/public/app/plugins/datasource/azuremonitor/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/azuremonitor/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/cloud-monitoring/project.json b/public/app/plugins/datasource/cloud-monitoring/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/cloud-monitoring/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/project.json b/public/app/plugins/datasource/grafana-postgresql-datasource/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/grafana-postgresql-datasource/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/project.json b/public/app/plugins/datasource/grafana-pyroscope-datasource/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/QueryEditor.tsx b/public/app/plugins/datasource/grafana-testdata-datasource/QueryEditor.tsx
index 8c4e6599073..66eb259dbde 100644
--- a/public/app/plugins/datasource/grafana-testdata-datasource/QueryEditor.tsx
+++ b/public/app/plugins/datasource/grafana-testdata-datasource/QueryEditor.tsx
@@ -10,6 +10,7 @@ import { CSVContentEditor } from './components/CSVContentEditor';
import { CSVFileEditor } from './components/CSVFileEditor';
import { CSVWavesEditor } from './components/CSVWaveEditor';
import ErrorEditor from './components/ErrorEditor';
+import ErrorWithSourceQueryEditor from './components/ErrorWithSourceEditor';
import { GrafanaLiveEditor } from './components/GrafanaLiveEditor';
import { NodeGraphEditor } from './components/NodeGraphEditor';
import { PredictablePulseEditor } from './components/PredictablePulseEditor';
@@ -120,6 +121,9 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
update.usa = {
mode: usaQueryModes[0].value,
};
+ break;
+ case TestDataQueryType.ErrorWithSource:
+ update.errorSource = 'plugin';
}
onUpdate(update);
@@ -379,6 +383,9 @@ export const QueryEditor = ({ query, datasource, onChange, onRunQuery }: Props)
/>
)}
+ {scenarioId === TestDataQueryType.ErrorWithSource && (
+
+ )}
{description && {description}
}
>
diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/components/ErrorWithSourceEditor.tsx b/public/app/plugins/datasource/grafana-testdata-datasource/components/ErrorWithSourceEditor.tsx
new file mode 100644
index 00000000000..a013a68b6bf
--- /dev/null
+++ b/public/app/plugins/datasource/grafana-testdata-datasource/components/ErrorWithSourceEditor.tsx
@@ -0,0 +1,32 @@
+import { InlineField, InlineFieldRow, Select } from '@grafana/ui';
+
+import { EditorProps } from '../QueryEditor';
+
+const OPTIONS = [
+ {
+ label: 'Plugin',
+ value: 'plugin',
+ },
+ {
+ label: 'Downstream',
+ value: 'downstream',
+ },
+];
+
+const ErrorWithSourceQueryEditor = ({ query, onChange }: EditorProps) => {
+ return (
+
+
+ {
+ onChange({ ...query, errorSource: v.value });
+ }}
+ />
+
+
+ );
+};
+
+export default ErrorWithSourceQueryEditor;
diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/dataquery.ts b/public/app/plugins/datasource/grafana-testdata-datasource/dataquery.ts
index 2c339f0e921..119660af972 100644
--- a/public/app/plugins/datasource/grafana-testdata-datasource/dataquery.ts
+++ b/public/app/plugins/datasource/grafana-testdata-datasource/dataquery.ts
@@ -33,6 +33,7 @@ export enum TestDataQueryType {
Trace = 'trace',
USA = 'usa',
VariablesQuery = 'variables-query',
+ ErrorWithSource = 'error_with_source',
}
export interface StreamingQuery {
@@ -125,6 +126,7 @@ export interface TestDataDataQuery extends common.DataQuery {
stream?: StreamingQuery;
stringInput?: string;
usa?: USAQuery;
+ errorSource?: 'plugin' | 'downstream';
}
export const defaultTestDataDataQuery: Partial = {
diff --git a/public/app/plugins/datasource/grafana-testdata-datasource/project.json b/public/app/plugins/datasource/grafana-testdata-datasource/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/grafana-testdata-datasource/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/jaeger/project.json b/public/app/plugins/datasource/jaeger/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/jaeger/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/mysql/project.json b/public/app/plugins/datasource/mysql/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/mysql/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/parca/project.json b/public/app/plugins/datasource/parca/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/parca/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/tempo/project.json b/public/app/plugins/datasource/tempo/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/tempo/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/plugins/datasource/zipkin/project.json b/public/app/plugins/datasource/zipkin/project.json
new file mode 100644
index 00000000000..4247352791d
--- /dev/null
+++ b/public/app/plugins/datasource/zipkin/project.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "../../../../../node_modules/nx/schemas/project-schema.json",
+ "projectType": "library",
+ "tags": ["scope:plugin", "type:datasource"],
+ "targets": {
+ "build": {},
+ "dev": {}
+ }
+}
diff --git a/public/app/routes/RoutesWrapper.tsx b/public/app/routes/RoutesWrapper.tsx
index 19409cd6663..cdb362edeb4 100644
--- a/public/app/routes/RoutesWrapper.tsx
+++ b/public/app/routes/RoutesWrapper.tsx
@@ -5,12 +5,11 @@ import { Router } from 'react-router-dom';
import { CompatRouter } from 'react-router-dom-v5-compat';
import { GrafanaTheme2 } from '@grafana/data/';
-import { HistoryWrapper, locationService, LocationServiceProvider } from '@grafana/runtime';
+import { HistoryWrapper, locationService, LocationServiceProvider, useChromeHeaderHeight } from '@grafana/runtime';
import { GlobalStyles, IconButton, ModalRoot, Stack, useSplitter, useStyles2 } from '@grafana/ui';
import { AngularRoot } from '../angular/AngularRoot';
import { AppChrome } from '../core/components/AppChrome/AppChrome';
-import { TOP_BAR_LEVEL_HEIGHT } from '../core/components/AppChrome/types';
import { AppNotificationList } from '../core/components/AppNotifications/AppNotificationList';
import { ModalsContextProvider } from '../core/context/ModalsContextProvider';
import { useSidecar } from '../core/context/SidecarContext';
@@ -74,10 +73,8 @@ export function ExperimentalSplitPaneRouterWrapper(props: RouterWrapperProps) {
primaryProps = alterStyles(primaryProps);
secondaryProps = alterStyles(secondaryProps);
- // TODO: this should be used to calculate the height of the header but right now results in a error loop when
- // navigating to explore "Cannot destructure property 'range' of 'itemState' as it is undefined."
- // const headerHeight = useChromeHeaderHeight();
- const styles = useStyles2(getStyles, TOP_BAR_LEVEL_HEIGHT * 2);
+ const headerHeight = useChromeHeaderHeight();
+ const styles = useStyles2(getStyles, headerHeight);
const memoryLocationService = new HistoryWrapper(H.createMemoryHistory({ initialEntries: ['/'] }));
return (
diff --git a/public/locales/de-DE/grafana.json b/public/locales/de-DE/grafana.json
index ac7243cb160..2071409b755 100644
--- a/public/locales/de-DE/grafana.json
+++ b/public/locales/de-DE/grafana.json
@@ -284,6 +284,11 @@
"ofQuery": {
"To": ""
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "",
+ "misconfigured-warning": "",
+ "misconfigured-warning-details": ""
}
},
"annotations": {
@@ -1408,7 +1413,9 @@
"dashboards": "",
"datasources": "",
"folders": "",
- "library_elements": ""
+ "library_elements": "",
+ "mute_timings": "",
+ "notification_templates": ""
},
"migration-token": {
"delete-button": "",
@@ -1492,6 +1499,8 @@
"datasource": "Datenquelle",
"folder": "Ordner",
"library_element": "",
+ "mute_timing": "",
+ "notification_template": "",
"unknown": "Unbekannt"
},
"summary": {
diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json
index 376282bc21d..af80b2fbcf9 100644
--- a/public/locales/en-US/grafana.json
+++ b/public/locales/en-US/grafana.json
@@ -284,6 +284,11 @@
"ofQuery": {
"To": "TO"
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "Misconfigured",
+ "misconfigured-warning": "This template is misconfigured.",
+ "misconfigured-warning-details": "Templates must be defined in both the <1>1> and <4>4> sections of your alertmanager configuration."
}
},
"annotations": {
@@ -1405,10 +1410,13 @@
"title": "Let us help you migrate to this stack"
},
"migrated-counts": {
+ "contact_points": "contact points",
"dashboards": "dashboards",
"datasources": "data sources",
"folders": "folders",
- "library_elements": "library elements"
+ "library_elements": "library elements",
+ "mute_timings": "mute timings",
+ "notification_templates": "notification templates"
},
"migration-token": {
"delete-button": "Delete token",
@@ -1488,10 +1496,13 @@
"unknown-datasource-type": "Unknown data source"
},
"resource-type": {
+ "contact_point": "Contact Point",
"dashboard": "Dashboard",
"datasource": "Data source",
"folder": "Folder",
"library_element": "Library Element",
+ "mute_timing": "Mute Timing",
+ "notification_template": "Notification Template",
"unknown": "Unknown"
},
"summary": {
diff --git a/public/locales/es-ES/grafana.json b/public/locales/es-ES/grafana.json
index fde74558ed1..8a2f3b9e438 100644
--- a/public/locales/es-ES/grafana.json
+++ b/public/locales/es-ES/grafana.json
@@ -284,6 +284,11 @@
"ofQuery": {
"To": ""
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "",
+ "misconfigured-warning": "",
+ "misconfigured-warning-details": ""
}
},
"annotations": {
@@ -1408,7 +1413,9 @@
"dashboards": "",
"datasources": "",
"folders": "",
- "library_elements": ""
+ "library_elements": "",
+ "mute_timings": "",
+ "notification_templates": ""
},
"migration-token": {
"delete-button": "",
@@ -1492,6 +1499,8 @@
"datasource": "Fuente de datos",
"folder": "",
"library_element": "",
+ "mute_timing": "",
+ "notification_template": "",
"unknown": "Desconocido"
},
"summary": {
diff --git a/public/locales/fr-FR/grafana.json b/public/locales/fr-FR/grafana.json
index 744b041a763..c302a57f275 100644
--- a/public/locales/fr-FR/grafana.json
+++ b/public/locales/fr-FR/grafana.json
@@ -284,6 +284,11 @@
"ofQuery": {
"To": ""
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "",
+ "misconfigured-warning": "",
+ "misconfigured-warning-details": ""
}
},
"annotations": {
@@ -1408,7 +1413,9 @@
"dashboards": "",
"datasources": "",
"folders": "",
- "library_elements": ""
+ "library_elements": "",
+ "mute_timings": "",
+ "notification_templates": ""
},
"migration-token": {
"delete-button": "",
@@ -1492,6 +1499,8 @@
"datasource": "Source de données",
"folder": "",
"library_element": "",
+ "mute_timing": "",
+ "notification_template": "",
"unknown": "Inconnu"
},
"summary": {
diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json
index 5a68f130569..4e3394f0b41 100644
--- a/public/locales/pseudo-LOCALE/grafana.json
+++ b/public/locales/pseudo-LOCALE/grafana.json
@@ -284,6 +284,11 @@
"ofQuery": {
"To": "ŦØ"
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "Mįşčőʼnƒįģūřęđ",
+ "misconfigured-warning": "Ŧĥįş ŧęmpľäŧę įş mįşčőʼnƒįģūřęđ.",
+ "misconfigured-warning-details": "Ŧęmpľäŧęş mūşŧ þę đęƒįʼnęđ įʼn þőŧĥ ŧĥę <1>1> äʼnđ <4>4> şęčŧįőʼnş őƒ yőūř äľęřŧmäʼnäģęř čőʼnƒįģūřäŧįőʼn."
}
},
"annotations": {
@@ -1405,10 +1410,13 @@
"title": "Ŀęŧ ūş ĥęľp yőū mįģřäŧę ŧő ŧĥįş şŧäčĸ"
},
"migrated-counts": {
+ "contact_points": "čőʼnŧäčŧ pőįʼnŧş",
"dashboards": "đäşĥþőäřđş",
"datasources": "đäŧä şőūřčęş",
"folders": "ƒőľđęřş",
- "library_elements": "ľįþřäřy ęľęmęʼnŧş"
+ "library_elements": "ľįþřäřy ęľęmęʼnŧş",
+ "mute_timings": "mūŧę ŧįmįʼnģş",
+ "notification_templates": "ʼnőŧįƒįčäŧįőʼn ŧęmpľäŧęş"
},
"migration-token": {
"delete-button": "Đęľęŧę ŧőĸęʼn",
@@ -1488,10 +1496,13 @@
"unknown-datasource-type": "Ůʼnĸʼnőŵʼn đäŧä şőūřčę"
},
"resource-type": {
+ "contact_point": "Cőʼnŧäčŧ Pőįʼnŧ",
"dashboard": "Đäşĥþőäřđ",
"datasource": "Đäŧä şőūřčę",
"folder": "Főľđęř",
"library_element": "Ŀįþřäřy Ēľęmęʼnŧ",
+ "mute_timing": "Mūŧę Ŧįmįʼnģ",
+ "notification_template": "Ńőŧįƒįčäŧįőʼn Ŧęmpľäŧę",
"unknown": "Ůʼnĸʼnőŵʼn"
},
"summary": {
diff --git a/public/locales/pt-BR/grafana.json b/public/locales/pt-BR/grafana.json
index e4fcf72a3b7..a14467b178a 100644
--- a/public/locales/pt-BR/grafana.json
+++ b/public/locales/pt-BR/grafana.json
@@ -284,6 +284,11 @@
"ofQuery": {
"To": ""
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "",
+ "misconfigured-warning": "",
+ "misconfigured-warning-details": ""
}
},
"annotations": {
@@ -1408,7 +1413,9 @@
"dashboards": "",
"datasources": "",
"folders": "",
- "library_elements": ""
+ "library_elements": "",
+ "mute_timings": "",
+ "notification_templates": ""
},
"migration-token": {
"delete-button": "",
@@ -1492,6 +1499,8 @@
"datasource": "Fonte de dados",
"folder": "",
"library_element": "",
+ "mute_timing": "",
+ "notification_template": "",
"unknown": "Desconhecido"
},
"summary": {
diff --git a/public/locales/zh-Hans/grafana.json b/public/locales/zh-Hans/grafana.json
index 37c08f6b213..2fc554f0ec1 100644
--- a/public/locales/zh-Hans/grafana.json
+++ b/public/locales/zh-Hans/grafana.json
@@ -280,6 +280,11 @@
"ofQuery": {
"To": ""
}
+ },
+ "templates": {
+ "misconfigured-badge-text": "",
+ "misconfigured-warning": "",
+ "misconfigured-warning-details": ""
}
},
"annotations": {
@@ -1398,7 +1403,9 @@
"dashboards": "",
"datasources": "",
"folders": "",
- "library_elements": ""
+ "library_elements": "",
+ "mute_timings": "",
+ "notification_templates": ""
},
"migration-token": {
"delete-button": "",
@@ -1482,6 +1489,8 @@
"datasource": "数据源",
"folder": "",
"library_element": "",
+ "mute_timing": "",
+ "notification_template": "",
"unknown": "未知"
},
"summary": {
diff --git a/public/openapi3.json b/public/openapi3.json
index fb2ee8f68ca..edd9dfa1bf4 100644
--- a/public/openapi3.json
+++ b/public/openapi3.json
@@ -1314,10 +1314,7 @@
"content": {
"application/json": {
"schema": {
- "items": {
- "$ref": "#/components/schemas/TeamLBACRules"
- },
- "type": "array"
+ "$ref": "#/components/schemas/TeamLBACRules"
}
}
},
@@ -4731,7 +4728,7 @@
"type": "string"
},
"ErrorSource": {
- "$ref": "#/components/schemas/ErrorSource"
+ "$ref": "#/components/schemas/Source"
},
"Frames": {
"$ref": "#/components/schemas/Frames"
@@ -10707,6 +10704,10 @@
},
"type": "object"
},
+ "Source": {
+ "title": "Source type defines the status source.",
+ "type": "string"
+ },
"Span": {
"properties": {
"Length": {
diff --git a/public/sass/_variables.dark.generated.scss b/public/sass/_variables.dark.generated.scss
index 868c07b7a73..c37a2c3def4 100644
--- a/public/sass/_variables.dark.generated.scss
+++ b/public/sass/_variables.dark.generated.scss
@@ -14,143 +14,66 @@
$theme-name: dark;
-$colors-action-hover: rgba(204, 204, 220, 0.16);
-$colors-action-selected: rgba(204, 204, 220, 0.12);
-
// New Colors
// -------------------------
-$blue-light: #6E9FFF;
$blue-base: #3D71D9;
-$blue-shade: rgb(90, 134, 222);
$red-base: #D10E5C;
-$red-shade: rgb(215, 50, 116);
$green-base: #1A7F4B;
-$green-shade: rgb(60, 146, 102);
-$orange-dark: #ff780a;
-
-$gray98: #f7f8fa;
-$gray95: #e9edf2;
-$gray85: #c7d0d9;
-$gray70: #9fa7b3;
-$gray60: #7b8087;
-$gray33: #464c54;
-$gray25: #2c3235;
-$gray15: #202226;
-$gray10: #141619;
-$gray05: #0b0c0e;
// Grays
// -------------------------
$black: #000000;
$dark-1: #141414;
-$dark-2: #161719;
$dark-3: #1f1f20;
-$dark-4: #212124;
-$dark-5: #222426;
$dark-6: #262628;
-$dark-7: #292a2d;
-$dark-8: #2f2f32;
$dark-9: #343436;
$dark-10: #424345;
$gray-1: #555555;
$gray-2: #8e8e8e;
-$gray-3: #b3b3b3;
-$gray-4: #d8d9da;
-$gray-5: #ececec;
$gray-6: #f4f5f8;
$white: #ffffff;
-$layer0: #111217;
-$layer1: #181b1f;
$layer2: #22252b;
-$divider: rgba(204, 204, 220, 0.12);
-
-$border0: rgba(204, 204, 220, 0.12);
-$border1: rgba(204, 204, 220, 0.20);
-
// Accent colors
// -------------------------
$blue: #33a2e5;
$red: $red-base;
$yellow: #ecbb13;
-$orange: #eb7b18;
$purple: #9933cc;
-$variable: #6E9FFF;
-
-$brand-primary: #eb7b18;
-$brand-success: #1A7F4B;
-$brand-warning: #FF9900;
-$brand-danger: #D10E5C;
-
-$query-red: #FF5286;
-$query-green: #6CCF8E;
-$query-purple: #fe85fc;
-$query-orange: #eb7b18;
-
-// Status colors
-// -------------------------¨
-$online: #6CCF8E;
-$warn: #fbad37;
-$critical: #FF5286;
// Scaffolding
// -------------------------
$body-bg: #111217;
-$page-bg: #111217;
-$dashboard-bg: #111217;
-$text-color-strong: #FFFFFF;
$text-color: rgb(204, 204, 220);
-$text-color-semi-weak: rgba(204, 204, 220, 0.65);
$text-color-weak: rgba(204, 204, 220, 0.65);
-$text-color-faint: rgba(204, 204, 220, 0.6);
$text-color-emphasis: #FFFFFF;
-$text-blue: #6E9FFF;
-$text-shadow-faint: 1px 1px 4px rgb(45, 45, 45);
-$textShadow: none;
-
-// gradients
-$brand-gradient-horizontal: linear-gradient(270deg, #F55F3E 0%, #FF8833 100%);
-$brand-gradient-vertical: linear-gradient(0.01deg, #F55F3E 0.01%, #FF8833 99.99%);
// Links
// -------------------------
$link-color: rgb(204, 204, 220);
$link-color-disabled: rgba(204, 204, 220, 0.6);
$link-hover-color: #FFFFFF;
-$external-link-color: #6E9FFF;
// Typography
// -------------------------
-$headings-color: rgb(204, 204, 220);
-$abbr-border-color: $gray-2 !default;
$text-muted: $text-color-weak;
-$hr-border-color: $dark-9;
-
// Panel
// -------------------------
$panel-bg: #181b1f;
-$panel-border: 1px solid rgba(204, 204, 220, 0.12);
-$panel-header-hover-bg: rgba(204, 204, 220, 0.16);
-$panel-box-shadow: none;
-$panel-corner: $panel-bg;
// page header
$page-header-bg: #111217;
$page-header-shadow: inset 0px -4px 14px $dark-3;
$page-header-border-color: #111217;
-$divider-border-color: $gray-1;
-
// Graphite Target Editor
$tight-form-func-bg: #22252b;
-$tight-form-func-highlight-bg: rgb(40, 43, 49);
-$modal-backdrop-bg: rgba(204, 204, 220, 0.16);
$code-tag-bg: $dark-1;
$code-tag-border: $dark-9;
@@ -161,76 +84,32 @@ $card-shadow: none;
// Lists
$list-item-bg: $card-background;
-$list-item-hover-bg: $card-background-hover;
-$list-item-shadow: $card-shadow;
$empty-list-cta-bg: #22252b;
// Scrollbars
$scrollbarBackground: #404357;
$scrollbarBackground2: $dark-10;
-$scrollbarBorder: black;
// Tables
// -------------------------
$table-bg-accent: #22252b;
-$table-border: rgba(204, 204, 220, 0.20);
-$table-bg-odd: rgb(28, 31, 35);
-$table-bg-hover: rgb(35, 38, 42);
// Buttons
// -------------------------
-$btn-primary-bg: $blue-base;
-$btn-primary-bg-hl: $blue-shade;
-
-$btn-secondary-bg: $dark-6;
-$btn-secondary-bg-hl: color.adjust($dark-6, $lightness: 4%);
-
-$btn-success-bg: $green-base;
-$btn-success-bg-hl: $green-shade;
-
-$btn-danger-bg: $red-base;
-$btn-danger-bg-hl: $red-shade;
-
-$btn-inverse-bg: $dark-6;
$btn-inverse-bg-hl: color.adjust($dark-6, $lightness: 4%);
-$btn-inverse-text-color: $link-color;
-$btn-inverse-text-shadow: 0px 1px 0 rgba(0, 0, 0, 0.1);
-
-$btn-link-color: $gray-3;
-
-$iconContainerBackground: $black;
$btn-divider-left: $dark-9;
$btn-divider-right: $dark-3;
$btn-drag-image: '../img/grab_dark.svg';
-$navbar-btn-gicon-brightness: brightness(0.5);
-
-$btn-active-box-shadow: 0px 0px 4px rgba(255, 120, 10, 0.5);
-
// Forms
// -------------------------
$input-bg: #111217;
-$input-bg-disabled: rgba(204, 204, 220, 0.04);
$input-color: rgb(204, 204, 220);
$input-border-color: rgba(204, 204, 220, 0.20);
-$input-box-shadow: none;
-$input-border-focus: #6E9FFF;
-$input-box-shadow-focus: #6E9FFF !default;
-$input-color-placeholder: rgba(204, 204, 220, 0.6);
-$input-label-bg: #22252b;
-$input-color-select-arrow: $white;
-
-// Search
-$search-shadow: 0 0 30px 0 $black;
-
-// Typeahead
-$typeahead-shadow: 0 5px 10px 0 $black;
-$typeahead-selected-bg: $dark-9;
-$typeahead-selected-color: $yellow;
// Dropdowns
// -------------------------
@@ -238,48 +117,20 @@ $dropdownBackground: #181b1f;
$dropdownBorder: rgba(204, 204, 220, 0.12);
$dropdownDividerTop: rgba(204, 204, 220, 0.12);
$dropdownDividerBottom: rgba(204, 204, 220, 0.12);
-$dropdownShadow: 0px 8px 24px rgb(1, 4, 9);
$dropdownLinkColor: $link-color;
$dropdownLinkColorHover: $white;
$dropdownLinkColorActive: $white;
$dropdownLinkBackgroundHover: $dark-9;
-// Horizontal forms & lists
-// -------------------------
-$horizontalComponentOffset: 180px;
-
-// Navbar
-// -------------------------
-$navbarHeight: 55px;
-$navbarBorder: 1px solid $dark-6;
-
-// Sidemenu
-// -------------------------
-$side-menu-bg: $panel-bg;
-$side-menu-bg-mobile: $panel-bg;
-$side-menu-border: none;
-$side-menu-item-hover-bg: #22252b;
-$side-menu-shadow: 0 0 30px #111;
-$side-menu-icon-color: #9fa7b3;
-$side-menu-header-color: rgb(204, 204, 220);
-
// Menu dropdowns
// -------------------------
$menu-dropdown-bg: #181b1f;
$menu-dropdown-hover-bg: rgba(204, 204, 220, 0.16);
$menu-dropdown-shadow: 0px 8px 24px rgb(1, 4, 9);
-// Tabs
-// -------------------------
-$tab-border-color: $dark-9;
-
// Form states and alerts
// -------------------------
-$warning-text-color: #fbad37;
-$error-text-color: #FF5286;
-$success-text-color: #6CCF8E;
-
$alert-error-bg: #D10E5C;
$alert-success-bg: #1A7F4B;
$alert-warning-bg: #FF9900;
@@ -287,16 +138,12 @@ $alert-info-bg: #FF9900;
// Tooltips and popovers
// -------------------------
-$tooltipArrowWidth: 5px;
$tooltipLinkColor: $link-color;
-$tooltipExternalLinkColor: $external-link-color;
+$tooltipExternalLinkColor: #6E9FFF;
$graph-tooltip-bg: $dark-1;
$tooltipBackground: #22252b;
$tooltipColor: rgb(204, 204, 220);
-$tooltipArrowColor: #22252b;
-$tooltipBackgroundError: #D10E5C;
-$tooltipShadow: 0px 4px 8px rgba(1, 4, 9, 0.75);
$popover-bg: #181b1f;
$popover-color: rgb(204, 204, 220);
@@ -306,10 +153,7 @@ $popover-shadow: 0px 8px 24px rgb(1, 4, 9);
$popover-help-bg: $tooltipBackground;
$popover-help-color: $text-color;
-$popover-error-bg: $btn-danger-bg;
-
-$popover-code-bg: $popover-bg;
-$popover-code-boxshadow: $tooltipShadow;
+$popover-error-bg: $red-base;
// images
$checkboxImageUrl: '../img/checkbox.png';
@@ -317,46 +161,6 @@ $checkboxImageUrl: '../img/checkbox.png';
// info box
$info-box-border-color: $blue-base;
-// footer
-$footer-link-color: $gray-2;
-$footer-link-hover: $gray-4;
-
-// json-explorer
-$json-explorer-default-color: $text-color;
-$json-explorer-string-color: #23d662;
-$json-explorer-number-color: $variable;
-$json-explorer-boolean-color: $variable;
-$json-explorer-null-color: #eec97d;
-$json-explorer-undefined-color: rgb(239, 143, 190);
-$json-explorer-function-color: #fd48cb;
-$json-explorer-rotate-time: 100ms;
-$json-explorer-toggler-opacity: 0.6;
-$json-explorer-bracket-color: #9494ff;
-$json-explorer-key-color: #23a0db;
-$json-explorer-url-color: #027bff;
-
-// Changelog and diff
-// -------------------------
-$diff-label-bg: rgba(204, 204, 220, 0.16);
-$diff-label-fg: $white;
-
-$diff-group-bg: #22252b;
-$diff-arrow-color: $white;
-
-$diff-json-bg: #22252b;
-$diff-json-fg: rgb(204, 204, 220);
-
-$diff-json-added: $blue-shade;
-$diff-json-deleted: $red-shade;
-
-$diff-json-old: #a04338;
-$diff-json-new: #457740;
-
-$diff-json-changed-fg: $gray-5;
-$diff-json-changed-num: $text-color;
-
-$diff-json-icon: $gray-5;
-
//Switch Slider
// -------------------------
$switch-bg: $input-bg;
@@ -371,35 +175,3 @@ $checkbox-bg: $dark-1;
$checkbox-border: 1px solid $gray-1;
$checkbox-checked-bg: linear-gradient(0deg, #eb7b18, #d44a3a);
$checkbox-color: $dark-1;
-
-//Panel Edit
-// -------------------------
-$panel-editor-shadow: 0 0 20px black;
-$panel-editor-side-menu-shadow: drop-shadow(0 0 10px $black);
-$panel-editor-viz-item-shadow: 0 0 8px $dark-10;
-$panel-editor-viz-item-border: 1px solid $dark-10;
-$panel-editor-viz-item-shadow-hover: 0 0 4px $blue-light;
-$panel-editor-viz-item-border-hover: 1px solid $blue-light;
-$panel-editor-viz-item-bg: $input-bg;
-$panel-editor-tabs-line-color: #e3e3e3;
-
-$panel-editor-viz-item-bg-hover: color.adjust($blue-base, $lightness: -46%);
-
-$panel-grid-placeholder-bg: color.adjust(#1f60c4, $lightness: -30%);
-$panel-grid-placeholder-shadow: 0 0 4px #3274d9;
-
-// logs
-$logs-color-unknown: $gray-2;
-
-// toggle-group
-$button-toggle-group-btn-active-bg: linear-gradient(90deg, #eb7b18, #d44a3a);
-$button-toggle-group-btn-active-shadow: inset 0 0 4px $black;
-$button-toggle-group-btn-separator-border: 1px solid $dark-2;
-
-$vertical-resize-handle-bg: $dark-10;
-$vertical-resize-handle-dots: $gray-1;
-$vertical-resize-handle-dots-hover: $gray-2;
-
-// Calendar
-$calendar-bg-days: $input-bg;
-$calendar-bg-now: $dark-10;
diff --git a/public/sass/_variables.light.generated.scss b/public/sass/_variables.light.generated.scss
index 7d4fac1eb15..1e317ffa7ab 100644
--- a/public/sass/_variables.light.generated.scss
+++ b/public/sass/_variables.light.generated.scss
@@ -14,42 +14,20 @@
$theme-name: light;
-$colors-action-hover: rgba(36, 41, 46, 0.12);
-$colors-action-selected: rgba(36, 41, 46, 0.08);
-
// New Colors
// -------------------------
-$blue-light: #1F62E0;
$blue-base: #3871DC;
-$blue-shade: rgb(44, 90, 176);
$red-base: #E0226E;
-$red-shade: rgb(179, 27, 88);
$green-base: #1B855E;
-$green-shade: rgb(21, 106, 75);
-$orange-dark: #ff780a;
-
-$gray98: #f7f8fa;
-$gray95: #e9edf2;
-$gray85: #c7d0d9;
-$gray70: #9fa7b3;
-$gray60: #7b8087;
-$gray33: #464c54;
-$gray25: #2c3235;
-$gray15: #202226;
-$gray10: #141619;
-$gray05: #0b0c0e;
// Grays
// -------------------------
$black: #000000;
-$dark-1: #141414;
$dark-2: #161719;
-$dark-4: #212124;
$dark-10: #424345;
$gray-1: #555555;
$gray-2: #8e8e8e;
-$gray-3: #b3b3b3;
$gray-4: #d8d9da;
$gray-5: #ececec;
$gray-6: #f4f5f8;
@@ -57,95 +35,45 @@ $gray-7: #fbfbfb;
$white: #ffffff;
-$layer0: #F4F5F5;
-$layer1: #FFFFFF;
$layer2: #F4F5F5;
-$divider: rgba(36, 41, 46, 0.12);
-$border0: rgba(36, 41, 46, 0.12);
-$border1: rgba(36, 41, 46, 0.30);
-
// Accent colors
// -------------------------
$blue: #1F62E0;
$red: $red-base;
$yellow: #ecbb13;
-$orange: #eb7b18;
$purple: #9933cc;
-$variable: #1F62E0;
-
-$brand-primary: #eb7b18;
-$brand-success: #1B855E;
-$brand-warning: #FF9900;
-$brand-danger: #E0226E;
-
-$query-red: #CF0E5B;
-$query-green: #0A764E;
-$query-purple: #fe85fc;
-$query-orange: #eb7b18;
-
-// Status colors
-// -------------------------
-$online: #0A764E;
-$warn: #B5510D;
-$critical: #CF0E5B;
-
// Scaffolding
// -------------------------
$body-bg: #F4F5F5;
-$page-bg: #F4F5F5;
-$dashboard-bg: #F4F5F5;
$text-color: rgba(36, 41, 46, 1);
-$text-color-strong: #000000;
-$text-color-semi-weak: rgba(36, 41, 46, 0.75);
$text-color-weak: rgba(36, 41, 46, 0.75);
-$text-color-faint: rgba(36, 41, 46, 0.64);
$text-color-emphasis: #000000;
-$text-blue: #1F62E0;
-
-$text-shadow-faint: none;
-
-// gradients
-$brand-gradient-horizontal: linear-gradient(90deg, #FF8833 0%, #F53E4C 100%);
-$brand-gradient-vertical: linear-gradient(0.01deg, #F53E4C -31.2%, #FF8833 113.07%);
// Links
// -------------------------
$link-color: rgba(36, 41, 46, 1);
$link-color-disabled: rgba(36, 41, 46, 0.64);
$link-hover-color: #000000;
-$external-link-color: #1F62E0;
// Typography
// -------------------------
-$headings-color: rgba(36, 41, 46, 1);
-$abbr-border-color: $gray-2 !default;
$text-muted: $text-color-weak;
-$hr-border-color: $gray-4 !default;
-
// Panel
// -------------------------
$panel-bg: #FFFFFF;
-$panel-border: 1px solid rgba(36, 41, 46, 0.12);
-$panel-header-hover-bg: rgba(36, 41, 46, 0.12);
-$panel-box-shadow: none;
-$panel-corner: $panel-bg;
// Page header
$page-header-bg: #F4F5F5;
$page-header-shadow: inset 0px -3px 10px $gray-6;
$page-header-border-color: #F4F5F5;
-$divider-border-color: $gray-2;
-
// Graphite Target Editor
$tight-form-func-bg: #F4F5F5;
-$tight-form-func-highlight-bg: #eff0f0;
-$modal-backdrop-bg: #FFFFFF;
$code-tag-bg: $gray-6;
$code-tag-border: $gray-4;
@@ -156,76 +84,32 @@ $card-shadow: none;
// Lists
$list-item-bg: $gray-7;
-$list-item-hover-bg: $gray-6;
-$list-item-shadow: $card-shadow;
$empty-list-cta-bg: $gray-6;
// Scrollbars
$scrollbarBackground: $gray-4;
$scrollbarBackground2: $gray-4;
-$scrollbarBorder: $gray-7;
// Tables
// -------------------------
$table-bg-accent: #F4F5F5;
-$table-border: rgba(36, 41, 46, 0.30);
-$table-bg-odd: rgb(249, 249, 249);
-$table-bg-hover: rgb(242, 242, 242);
// Buttons
// -------------------------
-$btn-secondary-bg: $gray-5;
-$btn-secondary-bg-hl: $gray-4;
-
-$btn-primary-bg: $blue-base;
-$btn-primary-bg-hl: $blue-shade;
-
-$btn-success-bg: $green-base;
-$btn-success-bg-hl: $green-shade;
-
-$btn-danger-bg: $red-base;
-$btn-danger-bg-hl: $red-shade;
-
-$btn-inverse-bg: $gray-5;
$btn-inverse-bg-hl: $gray-4;
-$btn-inverse-text-color: $gray-1;
-$btn-inverse-text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
-
-$btn-link-color: $gray-1;
-
-$iconContainerBackground: $white;
$btn-divider-left: $gray-4;
$btn-divider-right: $gray-7;
$btn-drag-image: '../img/grab_light.svg';
-$navbar-btn-gicon-brightness: brightness(1.5);
-
-$btn-active-box-shadow: 0px 0px 4px rgba(234, 161, 51, 0.6);
-
// Forms
// -------------------------
$input-bg: #FFFFFF;
-$input-bg-disabled: rgba(36, 41, 46, 0.04);
$input-color: rgba(36, 41, 46, 1);
$input-border-color: rgba(36, 41, 46, 0.30);
-$input-box-shadow: none;
-$input-border-focus: #5794f2;
-$input-box-shadow-focus: #5794f2;
-$input-color-placeholder: rgba(36, 41, 46, 0.64);
-$input-label-bg: #F4F5F5;
-$input-color-select-arrow: #7b8087;
-
-// search
-$search-shadow: 0 1px 5px 0 $gray-5;
-
-// Typeahead
-$typeahead-shadow: 0 5px 10px 0 $gray-5;
-$typeahead-selected-bg: $gray-6;
-$typeahead-selected-color: $yellow;
// Dropdowns
// -------------------------
@@ -233,7 +117,6 @@ $dropdownBackground: #FFFFFF;
$dropdownBorder: rgba(36, 41, 46, 0.12);
$dropdownDividerTop: rgba(36, 41, 46, 0.12);
$dropdownDividerBottom: rgba(36, 41, 46, 0.12);
-$dropdownShadow: 0px 13px 20px 1px rgba(24, 26, 27, 0.18);
$dropdownLinkColor: $dark-2;
$dropdownLinkColorHover: $link-color;
@@ -241,42 +124,14 @@ $dropdownLinkColorActive: $link-color;
$dropdownLinkBackgroundHover: $gray-6;
-// Horizontal forms & lists
-// -------------------------
-$horizontalComponentOffset: 180px;
-
-// Navbar
-// -------------------------
-$navbarHeight: 52px;
-$navbarBorder: 1px solid $gray-5;
-
-// Sidemenu
-// -------------------------
-$side-menu-bg: #202226;
-$side-menu-border: 1px solid #2c3235;
-$side-menu-bg-mobile: rgba(0, 0, 0, 0); //$gray-6;
-$side-menu-item-hover-bg: #2c3235;
-$side-menu-shadow: 5px 0px 10px -5px $gray-1;
-$side-menu-link-color: $gray-4;
-$side-menu-icon-color: #9fa7b3;
-$side-menu-header-color: #e9edf2;
-
// Menu dropdowns
// -------------------------
$menu-dropdown-bg: #FFFFFF;
$menu-dropdown-hover-bg: rgba(36, 41, 46, 0.12);
$menu-dropdown-shadow: 0px 13px 20px 1px rgba(24, 26, 27, 0.18);
-// Tabs
-// -------------------------
-$tab-border-color: $gray-5;
-
// Form states and alerts
// -------------------------
-$warning-text-color: #B5510D;
-$error-text-color: #CF0E5B;
-$success-text-color: #0A764E;
-
$alert-error-bg: #E0226E;
$alert-success-bg: #1B855E;
$alert-warning-bg: #FF9900;
@@ -285,9 +140,6 @@ $alert-info-bg: #FF9900;
// Tooltips and popovers
$tooltipBackground: #F4F5F5;
$tooltipColor: rgba(36, 41, 46, 1);
-$tooltipArrowColor: #F4F5F5;
-$tooltipBackgroundError: #E0226E;
-$tooltipShadow: 0px 4px 8px rgba(24, 26, 27, 0.2);
$popover-bg: #FFFFFF;
$popover-color: rgba(36, 41, 46, 1);
@@ -297,63 +149,19 @@ $popover-shadow: 0px 13px 20px 1px rgba(24, 26, 27, 0.18);
$graph-tooltip-bg: $gray-5;
-$tooltipArrowWidth: 5px;
$tooltipLinkColor: color.adjust($tooltipColor, $lightness: 5%);
$tooltipExternalLinkColor: #6E9FFF;
-$popover-error-bg: $btn-danger-bg;
+$popover-error-bg: $red-base;
$popover-help-bg: $tooltipBackground;
$popover-help-color: $tooltipColor;
-$popover-code-bg: #FFFFFF;
-$popover-code-boxshadow: 0 0 5px $gray60;
-
// images
$checkboxImageUrl: '../img/checkbox_white.png';
// info box
$info-box-border-color: $blue-base;
-// footer
-$footer-link-color: $gray-1;
-$footer-link-hover: $dark-2;
-
-// json explorer
-$json-explorer-default-color: black;
-$json-explorer-string-color: green;
-$json-explorer-number-color: $blue-base;
-$json-explorer-boolean-color: $red-base;
-$json-explorer-null-color: #855a00;
-$json-explorer-undefined-color: rgb(202, 11, 105);
-$json-explorer-function-color: #ff20ed;
-$json-explorer-rotate-time: 100ms;
-$json-explorer-toggler-opacity: 0.6;
-$json-explorer-bracket-color: $blue-base;
-$json-explorer-key-color: #00008b;
-$json-explorer-url-color: $blue-base;
-
-// Changelog and diff
-// -------------------------
-$diff-label-bg: rgba(36, 41, 46, 0.12);
-$diff-label-fg: $gray-2;
-
-$diff-arrow-color: $dark-2;
-$diff-group-bg: #F4F5F5;
-
-$diff-json-bg: #F4F5F5;
-$diff-json-fg: rgba(36, 41, 46, 1);
-
-$diff-json-added: $blue-shade;
-$diff-json-deleted: $red-shade;
-
-$diff-json-old: #5a372a;
-$diff-json-new: #664e33;
-
-$diff-json-changed-fg: $gray-7;
-$diff-json-changed-num: $gray-4;
-
-$diff-json-icon: $gray-4;
-
//Switch Slider
// -------------------------
$switch-bg: $white;
@@ -365,39 +173,6 @@ $switch-slider-shadow: 0 0 3px $dark-2;
//Checkbox
// -------------------------
$checkbox-bg: $gray-6;
-$checkbox-border: 1px solid $gray-3;
+$checkbox-border: 1px solid #b3b3b3;
$checkbox-checked-bg: linear-gradient(0deg, #ff9830, #e55400);
$checkbox-color: $gray-7;
-
-//Panel Edit
-// -------------------------
-$panel-editor-shadow: 0px 0px 8px $gray-3;
-$panel-editor-side-menu-shadow: drop-shadow(0 0 2px $gray-3);
-$panel-editor-viz-item-shadow: 0 0 4px $gray-3;
-$panel-editor-viz-item-border: 1px solid $gray-3;
-$panel-editor-viz-item-shadow-hover: 0 0 4px $blue-light;
-$panel-editor-viz-item-border-hover: 1px solid $blue-light;
-$panel-editor-viz-item-bg: $card-background;
-$panel-editor-tabs-line-color: $dark-2;
-
-$panel-editor-viz-item-bg-hover: color.adjust($blue-base, $lightness: 45%);
-
-$panel-grid-placeholder-bg: color.adjust(#5794f2, $lightness: 30%);
-$panel-grid-placeholder-shadow: 0 0 4px #5794f2;
-
-// logs
-$logs-color-unknown: $gray-5;
-
-// toggle-group
-$button-toggle-group-btn-active-bg: $brand-primary;
-$button-toggle-group-btn-active-shadow: inset 0 0 4px $white;
-$button-toggle-group-btn-separator-border: 1px solid $gray-6;
-
-$vertical-resize-handle-bg: $gray-4;
-$vertical-resize-handle-dots: $gray-3;
-$vertical-resize-handle-dots-hover: $gray-2;
-
-// Calendar
-$calendar-bg-days: $white;
-$calendar-bg-now: $gray-6;
-
diff --git a/scripts/drone/events/release.star b/scripts/drone/events/release.star
index 9f7a5563e4a..22819a28ec8 100644
--- a/scripts/drone/events/release.star
+++ b/scripts/drone/events/release.star
@@ -112,22 +112,7 @@ def publish_artifacts_step():
"PRERELEASE_BUCKET": from_secret("prerelease_bucket"),
},
"commands": [
- "./bin/build artifacts packages --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET}",
- ],
- "depends_on": ["compile-build-cmd"],
- }
-
-def publish_static_assets_step():
- return {
- "name": "publish-static-assets",
- "image": images["publish"],
- "environment": {
- "GCP_KEY": from_secret(gcp_grafanauploads_base64),
- "PRERELEASE_BUCKET": from_secret("prerelease_bucket"),
- "STATIC_ASSET_EDITIONS": from_secret("static_asset_editions"),
- },
- "commands": [
- "./bin/build artifacts static-assets --tag ${DRONE_TAG} --static-asset-editions=grafana-oss",
+ "./bin/build artifacts packages --artifacts-editions=oss --tag $${DRONE_TAG} --src-bucket $${PRERELEASE_BUCKET}",
],
"depends_on": ["compile-build-cmd"],
}
@@ -163,9 +148,8 @@ def publish_artifacts_pipelines(mode):
steps = [
compile_build_cmd(),
publish_artifacts_step(),
- publish_static_assets_step(),
publish_storybook_step(),
- release_pr_step(depends_on = ["publish-artifacts", "publish-static-assets"]),
+ release_pr_step(depends_on = ["publish-artifacts"]),
]
return [
diff --git a/scripts/drone/pipelines/publish_images.star b/scripts/drone/pipelines/publish_images.star
index 6fbcb7c475c..9ed847a22fb 100644
--- a/scripts/drone/pipelines/publish_images.star
+++ b/scripts/drone/pipelines/publish_images.star
@@ -31,43 +31,44 @@ def publish_image_public_step():
"""
command = """
bash -c '
+ IMAGE_TAG=$(echo "$${TAG}" | sed -e "s/+/-/g")
debug=
if [[ -n $${DRY_RUN} ]]; then debug=echo; fi
docker login -u $${DOCKER_USER} -p $${DOCKER_PASSWORD}
# Push the grafana-image-tags images
- $$debug docker push grafana/grafana-image-tags:$${TAG}-amd64
- $$debug docker push grafana/grafana-image-tags:$${TAG}-arm64
- $$debug docker push grafana/grafana-image-tags:$${TAG}-armv7
- $$debug docker push grafana/grafana-image-tags:$${TAG}-ubuntu-amd64
- $$debug docker push grafana/grafana-image-tags:$${TAG}-ubuntu-arm64
- $$debug docker push grafana/grafana-image-tags:$${TAG}-ubuntu-armv7
+ $$debug docker push grafana/grafana-image-tags:$${IMAGE_TAG}-amd64
+ $$debug docker push grafana/grafana-image-tags:$${IMAGE_TAG}-arm64
+ $$debug docker push grafana/grafana-image-tags:$${IMAGE_TAG}-armv7
+ $$debug docker push grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-amd64
+ $$debug docker push grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-arm64
+ $$debug docker push grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-armv7
# Create the grafana manifests
$$debug docker manifest create grafana/grafana:${TAG} \
- grafana/grafana-image-tags:$${TAG}-amd64 \
- grafana/grafana-image-tags:$${TAG}-arm64 \
- grafana/grafana-image-tags:$${TAG}-armv7
+ grafana/grafana-image-tags:$${IMAGE_TAG}-amd64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-arm64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-armv7
$$debug docker manifest create grafana/grafana:${TAG}-ubuntu \
- grafana/grafana-image-tags:$${TAG}-ubuntu-amd64 \
- grafana/grafana-image-tags:$${TAG}-ubuntu-arm64 \
- grafana/grafana-image-tags:$${TAG}-ubuntu-armv7
+ grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-amd64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-arm64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-armv7
# Push the grafana manifests
- $$debug docker manifest push grafana/grafana:$${TAG}
- $$debug docker manifest push grafana/grafana:$${TAG}-ubuntu
+ $$debug docker manifest push grafana/grafana:$${IMAGE_TAG}
+ $$debug docker manifest push grafana/grafana:$${IMAGE_TAG}-ubuntu
# if LATEST is set, then also create & push latest
if [[ -n $${LATEST} ]]; then
$$debug docker manifest create grafana/grafana:latest \
- grafana/grafana-image-tags:$${TAG}-amd64 \
- grafana/grafana-image-tags:$${TAG}-arm64 \
- grafana/grafana-image-tags:$${TAG}-armv7
+ grafana/grafana-image-tags:$${IMAGE_TAG}-amd64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-arm64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-armv7
$$debug docker manifest create grafana/grafana:latest-ubuntu \
- grafana/grafana-image-tags:$${TAG}-ubuntu-amd64 \
- grafana/grafana-image-tags:$${TAG}-ubuntu-arm64 \
- grafana/grafana-image-tags:$${TAG}-ubuntu-armv7
+ grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-amd64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-arm64 \
+ grafana/grafana-image-tags:$${IMAGE_TAG}-ubuntu-armv7
$$debug docker manifest push grafana/grafana:latest
$$debug docker manifest push grafana/grafana:latest-ubuntu
diff --git a/scripts/drone/pipelines/shellcheck.star b/scripts/drone/pipelines/shellcheck.star
index 7f56c553976..4b00a9314d9 100644
--- a/scripts/drone/pipelines/shellcheck.star
+++ b/scripts/drone/pipelines/shellcheck.star
@@ -2,7 +2,6 @@
This module returns a Drone step and pipeline for linting with shellcheck.
"""
-load("scripts/drone/steps/lib.star", "compile_build_cmd")
load(
"scripts/drone/utils/images.star",
"images",
@@ -39,7 +38,6 @@ def shellcheck_step():
def shellcheck_pipeline():
environment = {"EDITION": "oss"}
steps = [
- compile_build_cmd(),
shellcheck_step(),
]
return pipeline(
diff --git a/scripts/drone/pipelines/verify_starlark.star b/scripts/drone/pipelines/verify_starlark.star
index 9e058893d7b..e939cd598ef 100644
--- a/scripts/drone/pipelines/verify_starlark.star
+++ b/scripts/drone/pipelines/verify_starlark.star
@@ -4,7 +4,6 @@ This module returns a Drone pipeline that verifies all Starlark files are linted
load(
"scripts/drone/steps/lib.star",
- "compile_build_cmd",
"identify_runner_step",
"lint_starlark_step",
)
@@ -17,7 +16,6 @@ def verify_starlark(trigger, ver_mode):
environment = {"EDITION": "oss"}
steps = [
identify_runner_step(),
- compile_build_cmd(),
lint_starlark_step(),
]
return pipeline(
diff --git a/scripts/drone/steps/lib.star b/scripts/drone/steps/lib.star
index 500b3ab0305..903d23bfb02 100644
--- a/scripts/drone/steps/lib.star
+++ b/scripts/drone/steps/lib.star
@@ -156,9 +156,7 @@ def lint_starlark_step():
"go install github.com/bazelbuild/buildtools/buildifier@latest",
"buildifier --lint=warn -mode=check -r .",
],
- "depends_on": [
- "compile-build-cmd",
- ],
+ "depends_on": [],
}
def enterprise_downstream_step(ver_mode):