mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Support bodyScrolling (#91888)
* Progress * Fix dashboards pane * almost working * add hook to get scopesDashboardsScene state * check whether it's enabled when considering opened state * add height to container * Update * revert change * Make it work when bodyScrolling is disabled * Last tweaks * Update scenes * Updating * Fix * fix tests * fix lint issues * fix lint issues --------- Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
@@ -12,12 +12,9 @@ describe('Dashboards', () => {
|
|||||||
e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
||||||
|
|
||||||
// scroll to the bottom
|
// scroll to the bottom
|
||||||
e2e.pages.Dashboard.DashNav.navV2()
|
cy.get('#page-scrollbar').scrollTo('bottom', {
|
||||||
.parent()
|
timeout: 5 * 1000,
|
||||||
.parent() // Note, this will probably fail when we change the custom scrollbars
|
});
|
||||||
.scrollTo('bottom', {
|
|
||||||
timeout: 5 * 1000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// The last panel should be visible...
|
// The last panel should be visible...
|
||||||
e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
||||||
|
|||||||
@@ -12,12 +12,9 @@ describe('Dashboards', () => {
|
|||||||
e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
||||||
|
|
||||||
// scroll to the bottom
|
// scroll to the bottom
|
||||||
e2e.pages.Dashboard.DashNav.scrollContainer()
|
cy.get('#page-scrollbar').scrollTo('bottom', {
|
||||||
.children()
|
timeout: 5 * 1000,
|
||||||
.first()
|
});
|
||||||
.scrollTo('bottom', {
|
|
||||||
timeout: 5 * 1000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// The last panel should be visible...
|
// The last panel should be visible...
|
||||||
e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
||||||
@@ -30,6 +27,6 @@ describe('Dashboards', () => {
|
|||||||
// And the last panel should still be visible!
|
// And the last panel should still be visible!
|
||||||
// TODO: investigate scroll to on navigating back
|
// TODO: investigate scroll to on navigating back
|
||||||
// e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
// e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
||||||
e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
// e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -266,7 +266,7 @@
|
|||||||
"@grafana/prometheus": "workspace:*",
|
"@grafana/prometheus": "workspace:*",
|
||||||
"@grafana/runtime": "workspace:*",
|
"@grafana/runtime": "workspace:*",
|
||||||
"@grafana/saga-icons": "workspace:*",
|
"@grafana/saga-icons": "workspace:*",
|
||||||
"@grafana/scenes": "5.7.4",
|
"@grafana/scenes": "^5.8.0",
|
||||||
"@grafana/schema": "workspace:*",
|
"@grafana/schema": "workspace:*",
|
||||||
"@grafana/sql": "workspace:*",
|
"@grafana/sql": "workspace:*",
|
||||||
"@grafana/ui": "workspace:*",
|
"@grafana/ui": "workspace:*",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ jest.mock('@grafana/runtime', () => ({
|
|||||||
...jest.requireActual('@grafana/runtime'),
|
...jest.requireActual('@grafana/runtime'),
|
||||||
setPluginExtensionGetter: jest.fn(),
|
setPluginExtensionGetter: jest.fn(),
|
||||||
getPluginLinkExtensions: jest.fn(),
|
getPluginLinkExtensions: jest.fn(),
|
||||||
|
useChromeHeaderHeight: jest.fn().mockReturnValue(80),
|
||||||
getBackendSrv: () => {
|
getBackendSrv: () => {
|
||||||
return {
|
return {
|
||||||
get: jest.fn().mockResolvedValue({ dashboard: simpleDashboard, meta: { url: '' } }),
|
get: jest.fn().mockResolvedValue({ dashboard: simpleDashboard, meta: { url: '' } }),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
import { GrafanaTheme2, VariableHide } from '@grafana/data';
|
import { GrafanaTheme2, VariableHide } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
@@ -119,7 +119,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
|||||||
const { variableControls, refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } =
|
const { variableControls, refreshPicker, timePicker, hideTimeControls, hideVariableControls, hideLinksControls } =
|
||||||
model.useState();
|
model.useState();
|
||||||
const dashboard = getDashboardSceneFor(model);
|
const dashboard = getDashboardSceneFor(model);
|
||||||
const { links, meta, editPanel } = dashboard.useState();
|
const { links, editPanel } = dashboard.useState();
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const showDebugger = location.search.includes('scene-debugger');
|
const showDebugger = location.search.includes('scene-debugger');
|
||||||
|
|
||||||
@@ -128,10 +128,7 @@ function DashboardControlsRenderer({ model }: SceneComponentProps<DashboardContr
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div data-testid={selectors.pages.Dashboard.Controls} className={styles.controls}>
|
||||||
data-testid={selectors.pages.Dashboard.Controls}
|
|
||||||
className={cx(styles.controls, meta.isEmbedded && styles.embedded)}
|
|
||||||
>
|
|
||||||
<Stack grow={1} wrap={'wrap'}>
|
<Stack grow={1} wrap={'wrap'}>
|
||||||
{!hideVariableControls && variableControls.map((c) => <c.Component model={c} key={c.state.key} />)}
|
{!hideVariableControls && variableControls.map((c) => <c.Component model={c} key={c.state.key} />)}
|
||||||
<Box grow={1} />
|
<Box grow={1} />
|
||||||
@@ -159,18 +156,12 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
flexWrap: 'nowrap',
|
flexWrap: 'nowrap',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
background: theme.colors.background.canvas,
|
|
||||||
zIndex: theme.zIndex.activePanel,
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
flexDirection: 'column-reverse',
|
flexDirection: 'column-reverse',
|
||||||
alignItems: 'stretch',
|
alignItems: 'stretch',
|
||||||
},
|
},
|
||||||
[theme.breakpoints.up('sm')]: {
|
|
||||||
position: 'sticky',
|
|
||||||
top: 0,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
embedded: css({
|
embedded: css({
|
||||||
background: 'unset',
|
background: 'unset',
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { Provider } from 'react-redux';
|
import { render } from 'test/test-utils';
|
||||||
import { Router } from 'react-router';
|
|
||||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
|
||||||
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { locationService } from '@grafana/runtime';
|
|
||||||
import { GrafanaContext } from 'app/core/context/GrafanaContext';
|
|
||||||
import { configureStore } from 'app/store/configureStore';
|
|
||||||
|
|
||||||
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
import { transformSaveModelToScene } from '../serialization/transformSaveModelToScene';
|
||||||
|
|
||||||
|
jest.mock('@grafana/runtime', () => ({
|
||||||
|
...jest.requireActual('@grafana/runtime'),
|
||||||
|
useChromeHeaderHeight: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('DashboardSceneRenderer', () => {
|
describe('DashboardSceneRenderer', () => {
|
||||||
it('should render Not Found notice when dashboard is not found', async () => {
|
it('should render Not Found notice when dashboard is not found', async () => {
|
||||||
const scene = transformSaveModelToScene({
|
const scene = transformSaveModelToScene({
|
||||||
@@ -46,18 +46,7 @@ describe('DashboardSceneRenderer', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const store = configureStore({});
|
render(<scene.Component model={scene} />);
|
||||||
const context = getGrafanaContextMock();
|
|
||||||
|
|
||||||
render(
|
|
||||||
<GrafanaContext.Provider value={context}>
|
|
||||||
<Provider store={store}>
|
|
||||||
<Router history={locationService.getHistory()}>
|
|
||||||
<scene.Component model={scene} />
|
|
||||||
</Router>
|
|
||||||
</Provider>
|
|
||||||
</GrafanaContext.Provider>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(await screen.findByTestId(selectors.components.EntityNotFound.container)).toBeInTheDocument();
|
expect(await screen.findByTestId(selectors.components.EntityNotFound.container)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useMedia } from 'react-use';
|
|
||||||
|
|
||||||
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
|
import { GrafanaTheme2, PageLayoutType } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { useChromeHeaderHeight } from '@grafana/runtime';
|
||||||
import { config } from '@grafana/runtime';
|
|
||||||
import { SceneComponentProps } from '@grafana/scenes';
|
import { SceneComponentProps } from '@grafana/scenes';
|
||||||
import { CustomScrollbar, useStyles2, useTheme2 } from '@grafana/ui';
|
import { useStyles2 } from '@grafana/ui';
|
||||||
|
import NativeScrollbar from 'app/core/components/NativeScrollbar';
|
||||||
import { Page } from 'app/core/components/Page/Page';
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound';
|
import { EntityNotFound } from 'app/core/components/PageNotFound/EntityNotFound';
|
||||||
import { getNavModel } from 'app/core/selectors/navModel';
|
import { getNavModel } from 'app/core/selectors/navModel';
|
||||||
@@ -18,7 +17,8 @@ import { NavToolbarActions } from './NavToolbarActions';
|
|||||||
|
|
||||||
export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
|
export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardScene>) {
|
||||||
const { controls, overlay, editview, editPanel, isEmpty, meta } = model.useState();
|
const { controls, overlay, editview, editPanel, isEmpty, meta } = model.useState();
|
||||||
const styles = useStyles2(getStyles);
|
const headerHeight = useChromeHeaderHeight();
|
||||||
|
const styles = useStyles2(getStyles, headerHeight);
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navIndex = useSelector((state) => state.navIndex);
|
const navIndex = useSelector((state) => state.navIndex);
|
||||||
const pageNav = model.getPageNav(location, navIndex);
|
const pageNav = model.getPageNav(location, navIndex);
|
||||||
@@ -59,57 +59,43 @@ export function DashboardSceneRenderer({ model }: SceneComponentProps<DashboardS
|
|||||||
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
<Page navModel={navModel} pageNav={pageNav} layout={PageLayoutType.Custom}>
|
||||||
{editPanel && <editPanel.Component model={editPanel} />}
|
{editPanel && <editPanel.Component model={editPanel} />}
|
||||||
{!editPanel && (
|
{!editPanel && (
|
||||||
<div className={cx(styles.pageContainer, hasControls && styles.pageContainerWithControls)}>
|
<NativeScrollbar divId="page-scrollbar" autoHeightMin={'100%'}>
|
||||||
<NavToolbarActions dashboard={model} />
|
<div className={cx(styles.pageContainer, hasControls && styles.pageContainerWithControls)}>
|
||||||
{controls && (
|
<NavToolbarActions dashboard={model} />
|
||||||
<div className={styles.controlsWrapper}>
|
{controls && (
|
||||||
<controls.Component model={controls} />
|
<div className={styles.controlsWrapper}>
|
||||||
</div>
|
<controls.Component model={controls} />
|
||||||
)}
|
</div>
|
||||||
<PanelsContainer
|
)}
|
||||||
// This id is used by the image renderer to scroll through the dashboard
|
|
||||||
id="page-scrollbar"
|
|
||||||
className={styles.panelsContainer}
|
|
||||||
testId={selectors.pages.Dashboard.DashNav.scrollContainer}
|
|
||||||
>
|
|
||||||
<div className={cx(styles.canvasContent)}>{body}</div>
|
<div className={cx(styles.canvasContent)}>{body}</div>
|
||||||
</PanelsContainer>
|
</div>
|
||||||
</div>
|
</NativeScrollbar>
|
||||||
)}
|
)}
|
||||||
{overlay && <overlay.Component model={overlay} />}
|
{overlay && <overlay.Component model={overlay} />}
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStyles(theme: GrafanaTheme2) {
|
function getStyles(theme: GrafanaTheme2, headerHeight: number | undefined) {
|
||||||
return {
|
return {
|
||||||
pageContainer: css(
|
pageContainer: css({
|
||||||
{
|
display: 'grid',
|
||||||
display: 'grid',
|
gridTemplateAreas: `
|
||||||
gridTemplateAreas: `
|
|
||||||
"panels"`,
|
"panels"`,
|
||||||
gridTemplateColumns: `1fr`,
|
gridTemplateColumns: `1fr`,
|
||||||
gridTemplateRows: '1fr',
|
gridTemplateRows: '1fr',
|
||||||
height: '100%',
|
flexGrow: 1,
|
||||||
[theme.breakpoints.down('sm')]: {
|
[theme.breakpoints.down('sm')]: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
},
|
|
||||||
},
|
},
|
||||||
config.featureToggles.bodyScrolling && {
|
}),
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
}
|
|
||||||
),
|
|
||||||
pageContainerWithControls: css({
|
pageContainerWithControls: css({
|
||||||
gridTemplateAreas: `
|
gridTemplateAreas: `
|
||||||
"controls"
|
"controls"
|
||||||
"panels"`,
|
"panels"`,
|
||||||
gridTemplateRows: 'auto 1fr',
|
gridTemplateRows: 'auto 1fr',
|
||||||
}),
|
}),
|
||||||
panelsContainer: css({
|
|
||||||
gridArea: 'panels',
|
|
||||||
}),
|
|
||||||
controlsWrapper: css({
|
controlsWrapper: css({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
@@ -119,6 +105,13 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
':empty': {
|
':empty': {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
},
|
},
|
||||||
|
// Make controls sticky on larger screens (> mobile)
|
||||||
|
[theme.breakpoints.up('md')]: {
|
||||||
|
position: 'sticky',
|
||||||
|
zIndex: theme.zIndex.activePanel,
|
||||||
|
background: theme.colors.background.canvas,
|
||||||
|
top: headerHeight,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
canvasContent: css({
|
canvasContent: css({
|
||||||
label: 'canvas-content',
|
label: 'canvas-content',
|
||||||
@@ -126,7 +119,9 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
padding: theme.spacing(0, 2),
|
padding: theme.spacing(0, 2),
|
||||||
flexBasis: '100%',
|
flexBasis: '100%',
|
||||||
|
gridArea: 'panels',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
minWidth: 0,
|
||||||
}),
|
}),
|
||||||
body: css({
|
body: css({
|
||||||
label: 'body',
|
label: 'body',
|
||||||
@@ -141,34 +136,3 @@ function getStyles(theme: GrafanaTheme2) {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PanelsContainerProps {
|
|
||||||
id: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
testId?: string;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Removes the scrollbar on mobile and uses a custom scrollbar on desktop
|
|
||||||
*/
|
|
||||||
const PanelsContainer = ({ id, children, className, testId }: PanelsContainerProps) => {
|
|
||||||
const theme = useTheme2();
|
|
||||||
const isMobile = useMedia(`(max-width: ${theme.breakpoints.values.sm}px)`);
|
|
||||||
const styles = useStyles2(() => ({
|
|
||||||
nonScrollable: css({
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
return isMobile ? (
|
|
||||||
<div id={id} className={cx(className, styles.nonScrollable)} data-testid={testId}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<CustomScrollbar divId={id} autoHeightMin={'100%'} className={className} testId={testId}>
|
|
||||||
{children}
|
|
||||||
</CustomScrollbar>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { act, cleanup, waitFor } from '@testing-library/react';
|
import { act, cleanup, waitFor } from '@testing-library/react';
|
||||||
import userEvents from '@testing-library/user-event';
|
import userEvents from '@testing-library/user-event';
|
||||||
|
|
||||||
import { config, locationService } from '@grafana/runtime';
|
import { getPanelPlugin } from '@grafana/data/test/__mocks__/pluginMocks';
|
||||||
|
import { config, locationService, setPluginImportUtils } from '@grafana/runtime';
|
||||||
import { sceneGraph } from '@grafana/scenes';
|
import { sceneGraph } from '@grafana/scenes';
|
||||||
import { getDashboardAPI, setDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
import { getDashboardAPI, setDashboardAPI } from 'app/features/dashboard/api/dashboard_api';
|
||||||
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
|
||||||
@@ -62,16 +63,30 @@ import { getClosestScopesFacade } from './utils';
|
|||||||
jest.mock('@grafana/runtime', () => ({
|
jest.mock('@grafana/runtime', () => ({
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
...jest.requireActual('@grafana/runtime'),
|
...jest.requireActual('@grafana/runtime'),
|
||||||
|
useChromeHeaderHeight: jest.fn(),
|
||||||
getBackendSrv: () => ({
|
getBackendSrv: () => ({
|
||||||
get: getMock,
|
get: getMock,
|
||||||
}),
|
}),
|
||||||
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
usePluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const panelPlugin = getPanelPlugin({
|
||||||
|
id: 'table',
|
||||||
|
skipDataQuery: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
config.panels['table'] = panelPlugin.meta;
|
||||||
|
|
||||||
|
setPluginImportUtils({
|
||||||
|
importPanelPlugin: (id: string) => Promise.resolve(panelPlugin),
|
||||||
|
getPanelPluginFromCache: (id: string) => undefined,
|
||||||
|
});
|
||||||
|
|
||||||
describe('Scopes', () => {
|
describe('Scopes', () => {
|
||||||
describe('Feature flag off', () => {
|
describe('Feature flag off', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
config.featureToggles.scopeFilters = false;
|
config.featureToggles.scopeFilters = false;
|
||||||
|
config.featureToggles.groupByVariable = true;
|
||||||
|
|
||||||
initializeScopes();
|
initializeScopes();
|
||||||
});
|
});
|
||||||
@@ -88,6 +103,7 @@ describe('Scopes', () => {
|
|||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
config.featureToggles.scopeFilters = true;
|
config.featureToggles.scopeFilters = true;
|
||||||
|
config.featureToggles.groupByVariable = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -3739,9 +3739,9 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@grafana/scenes@npm:5.7.4":
|
"@grafana/scenes@npm:^5.8.0":
|
||||||
version: 5.7.4
|
version: 5.8.0
|
||||||
resolution: "@grafana/scenes@npm:5.7.4"
|
resolution: "@grafana/scenes@npm:5.8.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@grafana/e2e-selectors": "npm:^11.0.0"
|
"@grafana/e2e-selectors": "npm:^11.0.0"
|
||||||
"@leeoniya/ufuzzy": "npm:^1.0.14"
|
"@leeoniya/ufuzzy": "npm:^1.0.14"
|
||||||
@@ -3756,7 +3756,7 @@ __metadata:
|
|||||||
"@grafana/ui": ">=10.4"
|
"@grafana/ui": ">=10.4"
|
||||||
react: ^18.0.0
|
react: ^18.0.0
|
||||||
react-dom: ^18.0.0
|
react-dom: ^18.0.0
|
||||||
checksum: 10/e78f0d215e8a7a591689ce0b4672732bbf22ab38964e908cbf328125c855bcbf9569945dc2731a114e1a7244d2b7c7da98a1df2ac1ec4883c8201e0ab68de75f
|
checksum: 10/1c550dd5256371de0849ae64d167c4a9dbd99be0c03f15116ac213d3a9e2ec4b5248331086ca9d6616b5ad8f2be5be503772ac38a853e32da57f485ef3addb41
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -18163,7 +18163,7 @@ __metadata:
|
|||||||
"@grafana/prometheus": "workspace:*"
|
"@grafana/prometheus": "workspace:*"
|
||||||
"@grafana/runtime": "workspace:*"
|
"@grafana/runtime": "workspace:*"
|
||||||
"@grafana/saga-icons": "workspace:*"
|
"@grafana/saga-icons": "workspace:*"
|
||||||
"@grafana/scenes": "npm:5.7.4"
|
"@grafana/scenes": "npm:^5.8.0"
|
||||||
"@grafana/schema": "workspace:*"
|
"@grafana/schema": "workspace:*"
|
||||||
"@grafana/sql": "workspace:*"
|
"@grafana/sql": "workspace:*"
|
||||||
"@grafana/tsconfig": "npm:^2.0.0"
|
"@grafana/tsconfig": "npm:^2.0.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user