diff --git a/public/app/features/dashboard-scene/scene/DashboardControls.tsx b/public/app/features/dashboard-scene/scene/DashboardControls.tsx index 6981995bd3a..d2b80340dfc 100644 --- a/public/app/features/dashboard-scene/scene/DashboardControls.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardControls.tsx @@ -19,6 +19,7 @@ import { } from '@grafana/scenes'; import { Box, Button, useStyles2 } from '@grafana/ui'; import { playlistSrv } from 'app/features/playlist/PlaylistSrv'; +import { ContextualNavigationPaneToggle } from 'app/features/scopes/dashboards/ContextualNavigationPaneToggle'; import { PanelEditControls } from '../panel-edit/PanelEditControls'; import { getDashboardSceneFor } from '../utils/utils'; @@ -172,6 +173,9 @@ function DashboardControlsRenderer({ model }: SceneComponentProps )} + {config.featureToggles.scopeFilters && !editPanel && ( + + )} {!hideVariableControls && ( <> @@ -287,5 +291,9 @@ function getStyles(theme: GrafanaTheme2) { flexWrap: 'wrap', marginLeft: 'auto', }), + contextualNavToggle: css({ + display: 'inline-flex', + margin: theme.spacing(0, 1, 1, 0), + }), }; } diff --git a/public/app/features/scopes/dashboards/ContextualNavigationPaneToggle.tsx b/public/app/features/scopes/dashboards/ContextualNavigationPaneToggle.tsx new file mode 100644 index 00000000000..a87b8cc9df5 --- /dev/null +++ b/public/app/features/scopes/dashboards/ContextualNavigationPaneToggle.tsx @@ -0,0 +1,46 @@ +import { t } from '@grafana/i18n'; +import { useScopes } from '@grafana/runtime'; +import { ToolbarButton } from '@grafana/ui'; + +import { useScopesServices } from '../ScopesContextProvider'; + +interface Props { + className?: string; + hideWhenOpen?: boolean; +} + +export function ContextualNavigationPaneToggle({ className, hideWhenOpen }: Props) { + const scopes = useScopes(); + const services = useScopesServices(); + + if (!scopes || !services) { + return; + } + + const { scopesDashboardsService } = services; + const { readOnly, drawerOpened } = scopes.state; + + if (hideWhenOpen && drawerOpened) { + return null; + } + + const dashboardsIconLabel = readOnly + ? t('scopes.dashboards.toggle.disabled', 'Suggested dashboards list is disabled due to read only mode') + : drawerOpened + ? t('scopes.dashboards.toggle.collapse', 'Collapse suggested dashboards list') + : t('scopes.dashboards.toggle.expand', 'Expand suggested dashboards list'); + + return ( +
+ +
+ ); +} diff --git a/public/app/features/scopes/dashboards/ScopesDashboards.tsx b/public/app/features/scopes/dashboards/ScopesDashboards.tsx index 7a2fc7832f3..640eb6290b1 100644 --- a/public/app/features/scopes/dashboards/ScopesDashboards.tsx +++ b/public/app/features/scopes/dashboards/ScopesDashboards.tsx @@ -1,4 +1,4 @@ -import { css, cx } from '@emotion/css'; +import { css } from '@emotion/css'; import { useObservable } from 'react-use'; import { Observable } from 'rxjs'; @@ -34,22 +34,22 @@ export function ScopesDashboards() { if (!loading) { if (forScopeNames.length === 0) { return ( -
- No scopes selected +
+ + +
+ No scopes selected +
); } else if (dashboards.length === 0 && scopeNavigations.length === 0) { return ( -
- - No dashboards or links found for the selected scopes - +
+
+ + No dashboards or links found for the selected scopes + +
); } @@ -94,13 +94,14 @@ export function ScopesDashboards() { const getStyles = (theme: GrafanaTheme2) => { return { container: css({ - backgroundColor: theme.colors.background.primary, + backgroundColor: theme.colors.background.canvas, borderRight: `1px solid ${theme.colors.border.weak}`, display: 'flex', flexDirection: 'column', height: '100%', gap: theme.spacing(1), - padding: theme.spacing(2), + padding: theme.spacing(0, 2), + margin: theme.spacing(2, 0), width: theme.spacing(37.5), }), noResultsContainer: css({ diff --git a/public/app/features/scopes/dashboards/ScopesDashboardsTreeSearch.tsx b/public/app/features/scopes/dashboards/ScopesDashboardsTreeSearch.tsx index 3afb7a1368e..9235ff5eb61 100644 --- a/public/app/features/scopes/dashboards/ScopesDashboardsTreeSearch.tsx +++ b/public/app/features/scopes/dashboards/ScopesDashboardsTreeSearch.tsx @@ -6,6 +6,8 @@ import { GrafanaTheme2 } from '@grafana/data'; import { t } from '@grafana/i18n'; import { FilterInput, useStyles2 } from '@grafana/ui'; +import { ContextualNavigationPaneToggle } from './ContextualNavigationPaneToggle'; + export interface ScopesDashboardsTreeSearchProps { disabled: boolean; query: string; @@ -42,6 +44,7 @@ export function ScopesDashboardsTreeSearch({ disabled, query, onChange }: Scopes data-testid="scopes-dashboards-search" onChange={(value) => setInputState({ value, dirty: true })} /> +
); } @@ -49,6 +52,8 @@ export function ScopesDashboardsTreeSearch({ disabled, query, onChange }: Scopes const getStyles = (theme: GrafanaTheme2) => { return { container: css({ + display: 'flex', + gap: theme.spacing(1), flex: '0 1 auto', }), }; diff --git a/public/app/features/scopes/selector/ScopesSelector.tsx b/public/app/features/scopes/selector/ScopesSelector.tsx index bc89c748814..8c67fa202de 100644 --- a/public/app/features/scopes/selector/ScopesSelector.tsx +++ b/public/app/features/scopes/selector/ScopesSelector.tsx @@ -6,7 +6,7 @@ import { Observable } from 'rxjs'; import { GrafanaTheme2 } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { useScopes } from '@grafana/runtime'; -import { Button, Drawer, ErrorBoundary, ErrorWithStack, IconButton, Spinner, Text, useStyles2 } from '@grafana/ui'; +import { Button, Drawer, ErrorBoundary, ErrorWithStack, Spinner, Text, useStyles2 } from '@grafana/ui'; import { getModKey } from 'app/core/utils/browser'; import { useScopesServices } from '../ScopesContextProvider'; @@ -54,8 +54,8 @@ export const ScopesSelector = () => { tree, scopes: scopesMap, } = selectorServiceState; - const { scopesService, scopesSelectorService, scopesDashboardsService } = services; - const { readOnly, drawerOpened, loading } = scopes.state; + const { scopesService, scopesSelectorService } = services; + const { readOnly, loading } = scopes.state; const { open, removeAllScopes, @@ -70,24 +70,8 @@ export const ScopesSelector = () => { const recentScopes = getRecentScopes(); - const dashboardsIconLabel = readOnly - ? t('scopes.dashboards.toggle.disabled', 'Suggested dashboards list is disabled due to read only mode') - : drawerOpened - ? t('scopes.dashboards.toggle.collapse', 'Collapse suggested dashboards list') - : t('scopes.dashboards.toggle.expand', 'Expand suggested dashboards list'); - return ( <> - - { it('Shows a proper message when no scopes are selected', async () => { await toggleDashboards(); expectNoDashboardsNoScopes(); - expectNoDashboardsSearch(); }); it('Does not show the input when there are no dashboards found for scope', async () => { await updateScopes(scopesService, ['cloud']); await toggleDashboards(); expectNoDashboardsForScope(); - expectNoDashboardsSearch(); }); it('Shows the input and a message when there are no dashboards found for filter', async () => {