Provisioning: Disable editing options for read only repositories (#111547)

This commit is contained in:
Alex Khomenko
2025-09-24 16:22:43 +00:00
committed by GitHub
parent 0ec3110d41
commit c59aff3bb9
5 changed files with 53 additions and 20 deletions
@@ -28,6 +28,18 @@ jest.mock('app/features/dashboard/utils/dashboard', () => ({
onAddLibraryPanel: jest.fn(),
}));
jest.mock('app/features/provisioning/hooks/useGetResourceRepositoryView', () => ({
useGetResourceRepositoryView: jest.fn(() => ({
isReadOnlyRepo: false,
isInstanceManaged: false,
isLoading: false,
})),
}));
const mockUseGetResourceRepositoryView = jest.mocked(
require('app/features/provisioning/hooks/useGetResourceRepositoryView').useGetResourceRepositoryView
);
function setup(options?: Partial<Props>) {
const props = {
dashboard: createDashboardModelFixture(defaultDashboard),
@@ -40,6 +52,12 @@ function setup(options?: Partial<Props>) {
beforeEach(() => {
jest.clearAllMocks();
// Reset the mock to default state
mockUseGetResourceRepositoryView.mockReturnValue({
isReadOnlyRepo: false,
isInstanceManaged: false,
isLoading: false,
});
});
it('renders page with correct title for an empty dashboard', () => {
@@ -117,3 +135,18 @@ it('renders page without Add Widget button when feature flag is disabled', () =>
expect(screen.getByRole('button', { name: 'Add library panel' })).toBeInTheDocument();
expect(screen.queryByRole('button', { name: 'Add widget' })).not.toBeInTheDocument();
});
it('renders with buttons disabled when repository is read-only', () => {
// Mock the hook to return read-only repository
mockUseGetResourceRepositoryView.mockReturnValue({
isReadOnlyRepo: true,
isInstanceManaged: false,
isLoading: false,
});
setup({ canCreate: true });
expect(screen.getByRole('button', { name: 'Add visualization' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Import dashboard' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'Add library panel' })).toBeDisabled();
});
@@ -14,6 +14,7 @@ import {
import { buildPanelEditScene } from 'app/features/dashboard-scene/panel-edit/PanelEditor';
import { DashboardScene } from 'app/features/dashboard-scene/scene/DashboardScene';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
import { useGetResourceRepositoryView } from 'app/features/provisioning/hooks/useGetResourceRepositoryView';
import { useDispatch, useSelector } from 'app/types/store';
import { setInitialDatasource } from '../state/reducers';
@@ -28,6 +29,11 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
const dispatch = useDispatch();
const initialDatasource = useSelector((state) => state.dashboard.initialDatasource);
// Get repository information to check if it's read-only
const { isReadOnlyRepo } = useGetResourceRepositoryView({
folderName: dashboard instanceof DashboardScene ? dashboard.state.meta.folderUid : dashboard.meta.folderUid,
});
const onAddVisualization = () => {
let id;
if (dashboard instanceof DashboardScene) {
@@ -77,7 +83,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
icon="plus"
data-testid={selectors.pages.AddDashboard.itemButton('Create new panel button')}
onClick={onAddVisualization}
disabled={!canCreate}
disabled={!canCreate || isReadOnlyRepo}
>
<Trans i18nKey="dashboard.empty.add-visualization-button">Add visualization</Trans>
</Button>
@@ -101,7 +107,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
fill="outline"
data-testid={selectors.pages.AddDashboard.itemButton('Add a panel from the panel library button')}
onClick={onAddLibraryPanel}
disabled={!canCreate || isProvisioned}
disabled={!canCreate || isProvisioned || isReadOnlyRepo}
>
<Trans i18nKey="dashboard.empty.add-library-panel-button">Add library panel</Trans>
</Button>
@@ -131,7 +137,7 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => {
DashboardInteractions.emptyDashboardButtonClicked({ item: 'import_dashboard' });
onImportDashboard();
}}
disabled={!canCreate}
disabled={!canCreate || isReadOnlyRepo}
>
<Trans i18nKey="dashboard.empty.import-dashboard-button">Import dashboard</Trans>
</Button>
@@ -1,7 +1,7 @@
import { GrafanaEdition } from '@grafana/data/internal';
import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Box, Text, TextLink } from '@grafana/ui';
import { Alert, Stack, Text, TextLink } from '@grafana/ui';
import { Repository } from 'app/api/clients/provisioning/v0alpha1';
import { Page } from 'app/core/components/Page/Page';
@@ -24,8 +24,10 @@ export default function GettingStartedPage({ items }: Props) {
}}
>
<Page.Contents>
<Banner />
<GettingStarted items={items} />
<Stack direction="column" gap={3}>
<Banner />
<GettingStarted items={items} />
</Stack>
</Page.Contents>
</Page>
);
@@ -39,15 +41,7 @@ function Banner() {
}
return (
<Box
display="flex"
backgroundColor={'info'}
borderRadius="default"
paddingY={2}
paddingX={2}
marginBottom={3}
alignItems="stretch"
>
<Alert severity="info" title={''}>
<Text>
<Trans i18nKey={'provisioning.banner.message'}>
This feature is currently under active development. For the best experience and latest improvements, we
@@ -58,6 +52,6 @@ function Banner() {
of Grafana.
</Trans>
</Text>
</Box>
</Alert>
);
}
@@ -25,11 +25,11 @@ export const getReadOnlyTooltipText = ({ isLocal = false }) => {
return isLocal
? t(
'provisioning.read-only-local-tooltip',
'This folder is read-only and provisioned through file provisioning. To make any changes in the folder, update the connected file repository. To modify the folder settings go to Administration > Provisioning > Repositories.'
'This resource is read-only and provisioned through file provisioning. To make any changes, update the connected repository. To modify the settings go to Administration > Provisioning > Repositories.'
)
: t(
'provisioning.read-only-remote-tooltip',
'This folder is read-only and provisioned through Git. To make any changes in the folder, update the connected repository. To modify the folder settings go to Administration > Provisioning > Repositories.'
'This resource is read-only and provisioned through Git. To make any changes, update the connected repository. To modify the settings go to Administration > Provisioning > Repositories.'
);
};
+2 -2
View File
@@ -11526,8 +11526,8 @@
"subtitle": "Use this option if you want to sync and manage your entire Grafana instance through external storage."
}
},
"read-only-local-tooltip": "This folder is read-only and provisioned through file provisioning. To make any changes in the folder, update the connected file repository. To modify the folder settings go to Administration > Provisioning > Repositories.",
"read-only-remote-tooltip": "This folder is read-only and provisioned through Git. To make any changes in the folder, update the connected repository. To modify the folder settings go to Administration > Provisioning > Repositories.",
"read-only-local-tooltip": "This resource is read-only and provisioned through file provisioning. To make any changes, update the connected repository. To modify the settings go to Administration > Provisioning > Repositories.",
"read-only-remote-tooltip": "This resource is read-only and provisioned through Git. To make any changes, update the connected repository. To modify the settings go to Administration > Provisioning > Repositories.",
"recent-jobs": {
"active-jobs": "active jobs",
"column-action": "Action",