Scopes: move scope dashboard list toggle to canvas/page (#115131)
* Scopes: move scope dashboard list toggle to canvas/page * Updates * Updates * Fix test * Update
This commit is contained in:
@@ -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<DashboardContr
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{config.featureToggles.scopeFilters && !editPanel && (
|
||||
<ContextualNavigationPaneToggle className={styles.contextualNavToggle} hideWhenOpen={true} />
|
||||
)}
|
||||
{!hideVariableControls && (
|
||||
<>
|
||||
<VariableControls dashboard={dashboard} />
|
||||
@@ -287,5 +291,9 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
flexWrap: 'wrap',
|
||||
marginLeft: 'auto',
|
||||
}),
|
||||
contextualNavToggle: css({
|
||||
display: 'inline-flex',
|
||||
margin: theme.spacing(0, 1, 1, 0),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className={className}>
|
||||
<ToolbarButton
|
||||
icon="web-section-alt"
|
||||
aria-label={dashboardsIconLabel}
|
||||
tooltip={dashboardsIconLabel}
|
||||
data-testid="scopes-dashboards-expand"
|
||||
disabled={readOnly}
|
||||
onClick={scopesDashboardsService.toggleDrawer}
|
||||
variant={'canvas'}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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 (
|
||||
<div
|
||||
className={cx(styles.container, styles.noResultsContainer)}
|
||||
data-testid="scopes-dashboards-notFoundNoScopes"
|
||||
>
|
||||
<Trans i18nKey="scopes.dashboards.noResultsNoScopes">No scopes selected</Trans>
|
||||
<div className={styles.container} data-testid="scopes-dashboards-container">
|
||||
<ScopesDashboardsTreeSearch disabled={loading} query={searchQuery} onChange={changeSearchQuery} />
|
||||
|
||||
<div className={styles.noResultsContainer} data-testid="scopes-dashboards-notFoundNoScopes">
|
||||
<Trans i18nKey="scopes.dashboards.noResultsNoScopes">No scopes selected</Trans>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (dashboards.length === 0 && scopeNavigations.length === 0) {
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.container, styles.noResultsContainer)}
|
||||
data-testid="scopes-dashboards-notFoundForScope"
|
||||
>
|
||||
<Trans i18nKey="scopes.dashboards.noResultsForScopes">
|
||||
No dashboards or links found for the selected scopes
|
||||
</Trans>
|
||||
<div className={styles.container} data-testid="scopes-dashboards-container">
|
||||
<div className={styles.noResultsContainer} data-testid="scopes-dashboards-notFoundForScope">
|
||||
<Trans i18nKey="scopes.dashboards.noResultsForScopes">
|
||||
No dashboards or links found for the selected scopes
|
||||
</Trans>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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 })}
|
||||
/>
|
||||
<ContextualNavigationPaneToggle />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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',
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<>
|
||||
<IconButton
|
||||
name="web-section-alt"
|
||||
className={styles.dashboards}
|
||||
aria-label={dashboardsIconLabel}
|
||||
tooltip={dashboardsIconLabel}
|
||||
data-testid="scopes-dashboards-expand"
|
||||
disabled={readOnly}
|
||||
onClick={scopesDashboardsService.toggleDrawer}
|
||||
/>
|
||||
|
||||
<ScopesInput
|
||||
nodes={nodes}
|
||||
scopes={scopesMap}
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
expectNoDashboardsForFilter,
|
||||
expectNoDashboardsForScope,
|
||||
expectNoDashboardsNoScopes,
|
||||
expectNoDashboardsSearch,
|
||||
} from './utils/assertions';
|
||||
import {
|
||||
alternativeDashboardWithRootFolder,
|
||||
@@ -304,14 +303,12 @@ describe('Dashboards list', () => {
|
||||
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 () => {
|
||||
|
||||
Reference in New Issue
Block a user