Share: Remove new share drawer feature flag (#111048)

This commit is contained in:
Ezequiel Victorero 2025-09-16 10:57:49 -03:00 committed by GitHub
parent 0baacd8d5a
commit 6811cc1aa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 240 additions and 804 deletions

View File

@ -56,7 +56,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `newPDFRendering` | New implementation for the dashboard-to-PDF rendering | Yes | | `newPDFRendering` | New implementation for the dashboard-to-PDF rendering | Yes |
| `tlsMemcached` | Use TLS-enabled memcached in the enterprise caching feature | Yes | | `tlsMemcached` | Use TLS-enabled memcached in the enterprise caching feature | Yes |
| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes | | `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes |
| `newDashboardSharingComponent` | Enables the new sharing drawer design | Yes |
| `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | | | `pluginProxyPreserveTrailingSlash` | Preserve plugin proxy trailing slash. | |
| `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | Yes | | `azureMonitorPrometheusExemplars` | Allows configuration of Azure Monitor as a data source that can provide Prometheus exemplars | Yes |
| `pinNavItems` | Enables pinning of nav items | Yes | | `pinNavItems` | Enables pinning of nav items | Yes |

View File

@ -5,7 +5,6 @@ const DASHBOARD_UID = 'ZqZnVvFZz';
test.use({ test.use({
featureToggles: { featureToggles: {
scenes: true, scenes: true,
newDashboardSharingComponent: true,
sharingDashboardImage: true, // Enable the export image feature sharingDashboardImage: true, // Enable the export image feature
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
}, },

View File

@ -2,8 +2,8 @@ import { test, expect } from '@grafana/plugin-e2e';
test.use({ test.use({
featureToggles: { featureToggles: {
newDashboardSharingComponent: false,
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
dashboardScene: false, // this test is for the old sharing modal only used when scenes is turned off
}, },
}); });
@ -18,10 +18,10 @@ test.describe(
let dashboardPage = await gotoDashboardPage({ uid: 'ZqZnVvFZz' }); let dashboardPage = await gotoDashboardPage({ uid: 'ZqZnVvFZz' });
// Open sharing modal // Open sharing modal
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click(); await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.shareButton).click();
// Select public dashboards tab // Select public dashboards tab
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click(); await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public dashboard')).click();
// Create button should be disabled // Create button should be disabled
await expect( await expect(
@ -120,10 +120,10 @@ test.describe(
).toBeVisible(); ).toBeVisible();
// Open sharing modal // Open sharing modal
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click(); await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.shareButton).click();
// Select public dashboards tab // Select public dashboards tab
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click(); await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public dashboard')).click();
await expect( await expect(
dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput) dashboardPage.getByGrafanaSelector(selectors.pages.ShareDashboardModal.PublicDashboard.CopyUrlInput)
@ -171,10 +171,10 @@ test.describe(
dashboardPage = await gotoDashboardPage({ uid: 'ZqZnVvFZz' }); dashboardPage = await gotoDashboardPage({ uid: 'ZqZnVvFZz' });
// Open sharing modal // Open sharing modal
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click(); await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.shareButton).click();
// Select public dashboards tab // Select public dashboards tab
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click(); await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public dashboard')).click();
// Save url before disabling public dashboard // Save url before disabling public dashboard
copyUrlInput = dashboardPage.getByGrafanaSelector( copyUrlInput = dashboardPage.getByGrafanaSelector(

View File

@ -2,8 +2,8 @@ import { test, expect } from '@grafana/plugin-e2e';
test.use({ test.use({
featureToggles: { featureToggles: {
newDashboardSharingComponent: false,
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
dashboardScene: false, // this test is for the old sharing modal only used when scenes is turned off
}, },
}); });
@ -23,10 +23,10 @@ test.describe(
}); });
// Open sharing modal // Open sharing modal
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click(); await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.shareButton).click();
// Select public dashboards tab // Select public dashboards tab
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public Dashboard')).click(); await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Public dashboard')).click();
// Warning Alert dashboard cannot be made public because it has template variables // Warning Alert dashboard cannot be made public because it has template variables
await expect( await expect(

View File

@ -3,7 +3,6 @@ import { test, expect } from '@grafana/plugin-e2e';
test.use({ test.use({
featureToggles: { featureToggles: {
scenes: true, scenes: true,
newDashboardSharingComponent: true,
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
}, },
}); });

View File

@ -3,7 +3,6 @@ import { test, expect } from '@grafana/plugin-e2e';
test.use({ test.use({
featureToggles: { featureToggles: {
scenes: true, scenes: true,
newDashboardSharingComponent: true,
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
}, },
}); });

View File

@ -5,7 +5,6 @@ import { SnapshotCreateResponse } from '../../public/app/features/dashboard/serv
test.use({ test.use({
featureToggles: { featureToggles: {
scenes: true, scenes: true,
newDashboardSharingComponent: true,
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
}, },
}); });

View File

@ -4,8 +4,8 @@ const DASHBOARD_UID = 'ZqZnVvFZz';
test.use({ test.use({
featureToggles: { featureToggles: {
newDashboardSharingComponent: false, // Use legacy sharing component for this test
kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true', kubernetesDashboards: process.env.KUBERNETES_DASHBOARDS === 'true',
dashboardScene: false, // this test is for the old sharing modal only used when scenes is turned off
}, },
}); });
@ -29,7 +29,7 @@ test.describe(
]; ];
// Open the sharing modal // Open the sharing modal
await dashboardPage.getByGrafanaSelector(selectors.components.NavToolbar.shareDashboard).click(); await dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.DashNav.shareButton).click();
// Select the snapshot tab // Select the snapshot tab
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Snapshot')).click(); await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Snapshot')).click();
@ -54,9 +54,6 @@ test.describe(
const snapshotKey = getSnapshotKey(snapshotUrl); const snapshotKey = getSnapshotKey(snapshotUrl);
await page.goto(`/dashboard/snapshot/${snapshotKey}`); await page.goto(`/dashboard/snapshot/${snapshotKey}`);
// Validate the dashboard controls are rendered
await expect(dashboardPage.getByGrafanaSelector(selectors.pages.Dashboard.Controls)).toBeVisible();
// Validate the panels are rendered // Validate the panels are rendered
for (const title of panelsToCheck) { for (const title of panelsToCheck) {
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title))).toBeVisible(); await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title(title))).toBeVisible();

View File

@ -3,6 +3,7 @@ import { e2e } from '../utils';
describe('Public dashboards', () => { describe('Public dashboards', () => {
beforeEach(() => { beforeEach(() => {
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
cy.setLocalStorage('grafana.featureToggles', 'dashboardScene=false'); // this test is for the old sharing modal only used when scenes is turned off
}); });
it('Create a public dashboard', () => { it('Create a public dashboard', () => {

View File

@ -3,6 +3,7 @@ import { e2e } from '../utils';
describe('Create a public dashboard with template variables shows a template variable warning', () => { describe('Create a public dashboard with template variables shows a template variable warning', () => {
beforeEach(() => { beforeEach(() => {
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD')); e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
cy.setLocalStorage('grafana.featureToggles', 'dashboardScene=false'); // this test is for the old sharing modal only used when scenes is turned off
}); });
it('Create a public dashboard with template variables shows a template variable warning', () => { it('Create a public dashboard with template variables shows a template variable warning', () => {

View File

@ -553,11 +553,6 @@ export interface FeatureToggles {
*/ */
logsExploreTableDefaultVisualization?: boolean; logsExploreTableDefaultVisualization?: boolean;
/** /**
* Enables the new sharing drawer design
* @default true
*/
newDashboardSharingComponent?: boolean;
/**
* Enables the new alert list view design * Enables the new alert list view design
*/ */
alertingListViewV2?: boolean; alertingListViewV2?: boolean;

View File

@ -947,14 +947,6 @@ var (
Owner: grafanaObservabilityLogsSquad, Owner: grafanaObservabilityLogsSquad,
FrontendOnly: true, FrontendOnly: true,
}, },
{
Name: "newDashboardSharingComponent",
Description: "Enables the new sharing drawer design",
Stage: FeatureStageGeneralAvailability,
Owner: grafanaSharingSquad,
FrontendOnly: true,
Expression: "true", // enabled by default
},
{ {
Name: "alertingListViewV2", Name: "alertingListViewV2",
Description: "Enables the new alert list view design", Description: "Enables the new alert list view design",

View File

@ -123,7 +123,6 @@ grafanaManagedRecordingRules,experimental,@grafana/alerting-squad,false,false,fa
queryLibrary,privatePreview,@grafana/sharing-squad,false,false,false queryLibrary,privatePreview,@grafana/sharing-squad,false,false,false
savedQueries,preview,@grafana/sharing-squad,false,false,false savedQueries,preview,@grafana/sharing-squad,false,false,false
logsExploreTableDefaultVisualization,experimental,@grafana/observability-logs,false,false,true logsExploreTableDefaultVisualization,experimental,@grafana/observability-logs,false,false,true
newDashboardSharingComponent,GA,@grafana/sharing-squad,false,false,true
alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true
alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false
preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false preserveDashboardStateWhenNavigating,experimental,@grafana/dashboards-squad,false,false,false

1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
123 queryLibrary privatePreview @grafana/sharing-squad false false false
124 savedQueries preview @grafana/sharing-squad false false false
125 logsExploreTableDefaultVisualization experimental @grafana/observability-logs false false true
newDashboardSharingComponent GA @grafana/sharing-squad false false true
126 alertingListViewV2 privatePreview @grafana/alerting-squad false false true
127 alertingDisableSendAlertsExternal experimental @grafana/alerting-squad false false false
128 preserveDashboardStateWhenNavigating experimental @grafana/dashboards-squad false false false

View File

@ -503,10 +503,6 @@ const (
// Sets the logs table as default visualisation in logs explore // Sets the logs table as default visualisation in logs explore
FlagLogsExploreTableDefaultVisualization = "logsExploreTableDefaultVisualization" FlagLogsExploreTableDefaultVisualization = "logsExploreTableDefaultVisualization"
// FlagNewDashboardSharingComponent
// Enables the new sharing drawer design
FlagNewDashboardSharingComponent = "newDashboardSharingComponent"
// FlagAlertingListViewV2 // FlagAlertingListViewV2
// Enables the new alert list view design // Enables the new alert list view design
FlagAlertingListViewV2 = "alertingListViewV2" FlagAlertingListViewV2 = "alertingListViewV2"

View File

@ -2410,7 +2410,8 @@
"metadata": { "metadata": {
"name": "newDashboardSharingComponent", "name": "newDashboardSharingComponent",
"resourceVersion": "1753448760331", "resourceVersion": "1753448760331",
"creationTimestamp": "2024-05-03T15:02:18Z" "creationTimestamp": "2024-05-03T15:02:18Z",
"deletionTimestamp": "2025-09-12T17:27:39Z"
}, },
"spec": { "spec": {
"description": "Enables the new sharing drawer design", "description": "Enables the new sharing drawer design",

View File

@ -1,6 +1,4 @@
import { t } from '@grafana/i18n'; import { t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
// Maps the ID of the nav item to a translated phrase to later pass to <Trans /> // Maps the ID of the nav item to a translated phrase to later pass to <Trans />
// Because the navigation content is dynamic (defined in the backend), we can not use // Because the navigation content is dynamic (defined in the backend), we can not use
// the normal inline message definition method. // the normal inline message definition method.
@ -46,9 +44,7 @@ export function getNavTitle(navId: string | undefined) {
case 'reports': case 'reports':
return t('nav.reporting.title', 'Reporting'); return t('nav.reporting.title', 'Reporting');
case 'dashboards/public': case 'dashboards/public':
return config.featureToggles.newDashboardSharingComponent return t('nav.shared-dashboard.title', 'Shared dashboards');
? t('nav.shared-dashboard.title', 'Shared dashboards')
: t('nav.public.title', 'Public dashboards');
case 'dashboards/recently-deleted': case 'dashboards/recently-deleted':
return t('nav.recently-deleted.title', 'Recently deleted'); return t('nav.recently-deleted.title', 'Recently deleted');
case 'dashboards/new': case 'dashboards/new':
@ -218,9 +214,7 @@ export function getNavSubTitle(navId: string | undefined) {
'Interactive, publically available, point-in-time representations of dashboards and panels' 'Interactive, publically available, point-in-time representations of dashboards and panels'
); );
case 'dashboards/public': case 'dashboards/public':
return config.featureToggles.newDashboardSharingComponent t('nav.shared-dashboard.subtitle', "Manage your organization's externally shared dashboards");
? t('nav.shared-dashboard.subtitle', "Manage your organization's externally shared dashboards")
: undefined;
case 'dashboards/library-panels': case 'dashboards/library-panels':
return t('nav.library-panels.subtitle', 'Reusable panels that can be added to multiple dashboards'); return t('nav.library-panels.subtitle', 'Reusable panels that can be added to multiple dashboards');
case 'dashboards/recently-deleted': case 'dashboards/recently-deleted':

View File

@ -30,11 +30,7 @@ const selectors = e2eSelectors.pages.UserListPage;
const PublicDashboardsTab = ({ view, setView }: { view: TabView | null; setView: (v: TabView | null) => void }) => { const PublicDashboardsTab = ({ view, setView }: { view: TabView | null; setView: (v: TabView | null) => void }) => {
return ( return (
<Tab <Tab
label={ label={t('users-access-list.tabs.shared-dashboard-users-tab-title', 'Shared dashboard users')}
config.featureToggles.newDashboardSharingComponent
? t('users-access-list.tabs.shared-dashboard-users-tab-title', 'Shared dashboard users')
: t('users-access-list.tabs.public-dashboard-users-tab-title', 'Public dashboard users')
}
active={view === TabView.PUBLIC_DASHBOARDS} active={view === TabView.PUBLIC_DASHBOARDS}
onChangeTab={() => setView(TabView.PUBLIC_DASHBOARDS)} onChangeTab={() => setView(TabView.PUBLIC_DASHBOARDS)}
data-testid={selectors.tabs.publicDashboardsUsers} data-testid={selectors.tabs.publicDashboardsUsers}

View File

@ -3,7 +3,6 @@ import { css, cx } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Button, LoadingPlaceholder, Modal, ModalsController, useStyles2 } from '@grafana/ui'; import { Button, LoadingPlaceholder, Modal, ModalsController, useStyles2 } from '@grafana/ui';
import { import {
generatePublicDashboardConfigUrl, generatePublicDashboardConfigUrl,
@ -22,11 +21,7 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
<Modal <Modal
className={styles.modal} className={styles.modal}
isOpen isOpen
title={ title={t('public-dashboard-users-access-list.modal.shared-dashboard-modal-title', 'Shared dashboards')}
config.featureToggles.newDashboardSharingComponent
? t('public-dashboard-users-access-list.modal.shared-dashboard-modal-title', 'Shared dashboards')
: t('public-dashboard-users-access-list.modal.dashboard-modal-title', 'Public dashboards')
}
onDismiss={onDismiss} onDismiss={onDismiss}
> >
{isLoading ? ( {isLoading ? (
@ -47,15 +42,7 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
href={generatePublicDashboardUrl(dash.publicDashboardAccessToken)} href={generatePublicDashboardUrl(dash.publicDashboardAccessToken)}
onClick={onDismiss} onClick={onDismiss}
> >
{config.featureToggles.newDashboardSharingComponent ? ( <Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.external-link">External link</Trans>
<Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.external-link">
External link
</Trans>
) : (
<Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.public-dashboard-link">
Public dashboard URL
</Trans>
)}
</a> </a>
<span className={styles.urlsDivider}>{'•'}</span> <span className={styles.urlsDivider}>{'•'}</span>
<a <a
@ -63,15 +50,9 @@ export const DashboardsListModal = ({ email, onDismiss }: { email: string; onDis
href={generatePublicDashboardConfigUrl(dash.dashboardUid, dash.slug)} href={generatePublicDashboardConfigUrl(dash.dashboardUid, dash.slug)}
onClick={onDismiss} onClick={onDismiss}
> >
{config.featureToggles.newDashboardSharingComponent ? ( <Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.sharing-setting">
<Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.sharing-setting"> Sharing settings
Sharing settings </Trans>
</Trans>
) : (
<Trans i18nKey="public-dashboard-users-access-list.dashboard-modal.public-dashboard-setting">
Public dashboard settings
</Trans>
)}
</a> </a>
</div> </div>
<hr className={styles.divider} /> <hr className={styles.divider} />

View File

@ -2,7 +2,6 @@ import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Button, Modal, ModalsController, useStyles2 } from '@grafana/ui'; import { Button, Modal, ModalsController, useStyles2 } from '@grafana/ui';
import { SessionUser } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils'; import { SessionUser } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
@ -30,21 +29,12 @@ const DeleteUserModal = ({ user, hideModal }: { user: SessionUser; hideModal: ()
</Trans> </Trans>
</p> </p>
<p className={styles.description}> <p className={styles.description}>
{config.featureToggles.newDashboardSharingComponent ? ( <Trans
<Trans i18nKey="public-dashboard-users-access-list.delete-user-shared-dashboards-modal.revoke-user-access-modal-desc-line2"
i18nKey="public-dashboard-users-access-list.delete-user-shared-dashboards-modal.revoke-user-access-modal-desc-line2" shouldUnescape
shouldUnescape >
> This action will immediately revoke {{ email: user.email }}&apos;s access to all shared dashboards.
This action will immediately revoke {{ email: user.email }}&apos;s access to all shared dashboards. </Trans>
</Trans>
) : (
<Trans
i18nKey="public-dashboard-users-access-list.delete-user-modal.revoke-user-access-modal-desc-line2"
shouldUnescape
>
This action will immediately revoke {{ email: user.email }}&apos;s access to all public dashboards.
</Trans>
)}
</p> </p>
<Modal.ButtonRow> <Modal.ButtonRow>
<Button type="button" variant="secondary" onClick={hideModal} fill="outline"> <Button type="button" variant="secondary" onClick={hideModal} fill="outline">

View File

@ -1,4 +1,3 @@
import { config } from '@grafana/runtime';
import { SceneObjectUrlSyncHandler, SceneObjectUrlValues, VizPanel } from '@grafana/scenes'; import { SceneObjectUrlSyncHandler, SceneObjectUrlValues, VizPanel } from '@grafana/scenes';
import { contextSrv } from 'app/core/core'; import { contextSrv } from 'app/core/core';
import { KioskMode } from 'app/types/dashboard'; import { KioskMode } from 'app/types/dashboard';
@ -6,7 +5,6 @@ import { KioskMode } from 'app/types/dashboard';
import { buildPanelEditScene } from '../panel-edit/PanelEditor'; import { buildPanelEditScene } from '../panel-edit/PanelEditor';
import { createDashboardEditViewFor } from '../settings/utils'; import { createDashboardEditViewFor } from '../settings/utils';
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
import { ShareModal } from '../sharing/ShareModal';
import { findEditPanel, getLibraryPanelBehavior } from '../utils/utils'; import { findEditPanel, getLibraryPanelBehavior } from '../utils/utils';
import { DashboardScene, DashboardSceneState } from './DashboardScene'; import { DashboardScene, DashboardSceneState } from './DashboardScene';
@ -102,13 +100,9 @@ export class DashboardSceneUrlSync implements SceneObjectUrlSyncHandler {
if (typeof values.shareView === 'string') { if (typeof values.shareView === 'string') {
update.shareView = values.shareView; update.shareView = values.shareView;
update.overlay = config.featureToggles.newDashboardSharingComponent update.overlay = new ShareDrawer({
? new ShareDrawer({ shareView: values.shareView,
shareView: values.shareView, });
})
: new ShareModal({
activeTab: values.shareView,
});
} else if (shareView && values.shareView === null) { } else if (shareView && values.shareView === null) {
update.overlay = undefined; update.overlay = undefined;
update.shareView = undefined; update.shareView = undefined;

View File

@ -4,7 +4,7 @@ import { TestProvider } from 'test/helpers/TestProvider';
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock'; import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
import { selectors } from '@grafana/e2e-selectors'; import { selectors } from '@grafana/e2e-selectors';
import { LocationServiceProvider, config, locationService } from '@grafana/runtime'; import { LocationServiceProvider, locationService } from '@grafana/runtime';
import { SceneQueryRunner, SceneTimeRange, UrlSyncContextProvider, VizPanel } from '@grafana/scenes'; import { SceneQueryRunner, SceneTimeRange, UrlSyncContextProvider, VizPanel } from '@grafana/scenes';
import { playlistSrv } from 'app/features/playlist/PlaylistSrv'; import { playlistSrv } from 'app/features/playlist/PlaylistSrv';
import { DashboardMeta } from 'app/types/dashboard'; import { DashboardMeta } from 'app/types/dashboard';
@ -145,25 +145,14 @@ describe('NavToolbarActions', () => {
}); });
describe('Given new sharing button', () => { describe('Given new sharing button', () => {
it('Should show old share button when newDashboardSharingComponent FF is disabled', async () => { it('Should show new share button', async () => {
setup();
expect(await screen.findByText('Share')).toBeInTheDocument();
const newShareButton = screen.queryByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container);
expect(newShareButton).not.toBeInTheDocument();
const newExportButton = screen.queryByRole('button', { name: /export dashboard/i });
expect(newExportButton).not.toBeInTheDocument();
});
it('Should show new share button when newDashboardSharingComponent FF is enabled', async () => {
config.featureToggles.newDashboardSharingComponent = true;
setup(); setup();
expect(await screen.queryByTestId(selectors.pages.Dashboard.DashNav.shareButton)).not.toBeInTheDocument(); expect(await screen.queryByTestId(selectors.pages.Dashboard.DashNav.shareButton)).not.toBeInTheDocument();
const newShareButton = screen.getByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container); const newShareButton = screen.getByTestId(selectors.pages.Dashboard.DashNav.newShareButton.container);
expect(newShareButton).toBeInTheDocument(); expect(newShareButton).toBeInTheDocument();
}); });
it('Should show new export button when newDashboardSharingComponent FF is enabled', async () => { it('Should show new export button', async () => {
config.featureToggles.newDashboardSharingComponent = true;
setup(); setup();
const newExportButton = screen.getByRole('button', { name: /export dashboard/i }); const newExportButton = screen.getByRole('button', { name: /export dashboard/i });
expect(newExportButton).toBeInTheDocument(); expect(newExportButton).toBeInTheDocument();

View File

@ -26,7 +26,6 @@ import { useGetResourceRepositoryView } from 'app/features/provisioning/hooks/us
import { getReadOnlyTooltipText } from 'app/features/provisioning/utils/repository'; import { getReadOnlyTooltipText } from 'app/features/provisioning/utils/repository';
import { useSelector } from 'app/types/store'; import { useSelector } from 'app/types/store';
import { shareDashboardType } from '../../dashboard/components/ShareModal/utils';
import { selectFolderRepository } from '../../provisioning/utils/selectors'; import { selectFolderRepository } from '../../provisioning/utils/selectors';
import { PanelEditor, buildPanelEditScene } from '../panel-edit/PanelEditor'; import { PanelEditor, buildPanelEditScene } from '../panel-edit/PanelEditor';
import ExportButton from '../sharing/ExportButton/ExportButton'; import ExportButton from '../sharing/ExportButton/ExportButton';
@ -47,7 +46,7 @@ interface Props {
} }
export const NavToolbarActions = memo<Props>(({ dashboard }) => { export const NavToolbarActions = memo<Props>(({ dashboard }) => {
const hasNewToolbar = config.featureToggles.dashboardNewLayouts && config.featureToggles.newDashboardSharingComponent; const hasNewToolbar = config.featureToggles.dashboardNewLayouts;
return hasNewToolbar ? ( return hasNewToolbar ? (
<AppChromeUpdate <AppChromeUpdate
@ -322,26 +321,6 @@ export function ToolbarActions({ dashboard }: Props) {
}); });
const showShareButton = uid && !isEditing && !meta.isSnapshot && !isPlaying; const showShareButton = uid && !isEditing && !meta.isSnapshot && !isPlaying;
toolbarActions.push({
group: 'main-buttons',
condition: !config.featureToggles.newDashboardSharingComponent && showShareButton,
render: () => (
<Button
key="share-dashboard-button"
tooltip={t('dashboard.toolbar.share.tooltip', 'Share dashboard')}
size="sm"
className={styles.buttonWithExtraMargin}
fill="outline"
onClick={() => {
DashboardInteractions.toolbarShareClick();
locationService.partial({ shareView: shareDashboardType.link });
}}
data-testid={selectors.components.NavToolbar.shareDashboard}
>
<Trans i18nKey="dashboard.toolbar.share.label">Share</Trans>
</Button>
),
});
toolbarActions.push({ toolbarActions.push({
group: 'main-buttons', group: 'main-buttons',
@ -358,7 +337,7 @@ export function ToolbarActions({ dashboard }: Props) {
} }
key="edit" key="edit"
className={styles.buttonWithExtraMargin} className={styles.buttonWithExtraMargin}
variant={config.featureToggles.newDashboardSharingComponent ? 'secondary' : 'primary'} variant={'secondary'}
size="sm" size="sm"
data-testid={selectors.components.NavToolbar.editDashboard.editButton} data-testid={selectors.components.NavToolbar.editDashboard.editButton}
disabled={isReadOnlyRepo} disabled={isReadOnlyRepo}
@ -391,13 +370,13 @@ export function ToolbarActions({ dashboard }: Props) {
toolbarActions.push({ toolbarActions.push({
group: 'new-share-dashboard-buttons', group: 'new-share-dashboard-buttons',
condition: config.featureToggles.newDashboardSharingComponent && showShareButton, condition: showShareButton,
render: () => <ExportButton key="new-export-dashboard-button" dashboard={dashboard} />, render: () => <ExportButton key="new-export-dashboard-button" dashboard={dashboard} />,
}); });
toolbarActions.push({ toolbarActions.push({
group: 'new-share-dashboard-buttons', group: 'new-share-dashboard-buttons',
condition: config.featureToggles.newDashboardSharingComponent && showShareButton, condition: showShareButton,
render: () => <ShareButton key="new-share-dashboard-button" dashboard={dashboard} />, render: () => <ShareButton key="new-share-dashboard-button" dashboard={dashboard} />,
}); });

View File

@ -534,7 +534,7 @@ describe('panelMenuBehavior', () => {
expect(menu.state.items?.find((i) => i.text === 'Remove')).toBeDefined(); expect(menu.state.items?.find((i) => i.text === 'Remove')).toBeDefined();
const moreMenu = menu.state.items?.find((i) => i.text === 'More...')?.subMenu; const moreMenu = menu.state.items?.find((i) => i.text === 'More...')?.subMenu;
expect(moreMenu?.find((i) => i.text === 'Duplicate')).toBeDefined(); expect(moreMenu?.find((i) => i.text === 'Duplicate')).toBeDefined();
expect(moreMenu?.find((i) => i.text === 'Create library panel')).toBeDefined(); expect(moreMenu?.find((i) => i.text === 'New library panel')).toBeDefined();
}); });
it('should only contain explore when embedded', async () => { it('should only contain explore when embedded', async () => {

View File

@ -35,7 +35,6 @@ import { ShowConfirmModalEvent } from 'app/types/events';
import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer'; import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer';
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
import { ShareModal } from '../sharing/ShareModal';
import { isRepeatCloneOrChildOf } from '../utils/clone'; import { isRepeatCloneOrChildOf } from '../utils/clone';
import { DashboardInteractions } from '../utils/interactions'; import { DashboardInteractions } from '../utils/interactions';
import { getEditPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders'; import { getEditPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
@ -109,90 +108,79 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
}); });
} }
if (config.featureToggles.newDashboardSharingComponent) { const subMenu: PanelMenuItem[] = [];
const subMenu: PanelMenuItem[] = []; subMenu.push({
text: t('share-panel.menu.share-link-title', 'Share link'),
iconClassName: 'link',
shortcut: 'p u',
onClick: () => {
DashboardInteractions.sharingCategoryClicked({
item: shareDashboardType.link,
shareResource: getTrackingSource(panel?.getRef()),
});
const drawer = new ShareDrawer({
shareView: shareDashboardType.link,
panelRef: panel.getRef(),
});
dashboard.showModal(drawer);
},
});
subMenu.push({
text: t('share-panel.menu.share-embed-title', 'Share embed'),
iconClassName: 'arrow',
shortcut: 'p e',
onClick: () => {
DashboardInteractions.sharingCategoryClicked({
item: shareDashboardType.embed,
shareResource: getTrackingSource(panel.getRef()),
});
const drawer = new ShareDrawer({
shareView: shareDashboardType.embed,
panelRef: panel.getRef(),
});
dashboard.showModal(drawer);
},
});
if (
contextSrv.isSignedIn &&
config.snapshotEnabled &&
contextSrv.hasPermission(AccessControlAction.SnapshotsCreate)
) {
subMenu.push({ subMenu.push({
text: t('share-panel.menu.share-link-title', 'Share link'), text: t('share-panel.menu.share-snapshot-title', 'Share snapshot'),
iconClassName: 'link', iconClassName: 'camera',
shortcut: 'p u', shortcut: 'p s',
onClick: () => { onClick: () => {
DashboardInteractions.sharingCategoryClicked({ DashboardInteractions.sharingCategoryClicked({
item: shareDashboardType.link, item: shareDashboardType.snapshot,
shareResource: getTrackingSource(panel?.getRef()),
});
const drawer = new ShareDrawer({
shareView: shareDashboardType.link,
panelRef: panel.getRef(),
});
dashboard.showModal(drawer);
},
});
subMenu.push({
text: t('share-panel.menu.share-embed-title', 'Share embed'),
iconClassName: 'arrow',
shortcut: 'p e',
onClick: () => {
DashboardInteractions.sharingCategoryClicked({
item: shareDashboardType.embed,
shareResource: getTrackingSource(panel.getRef()), shareResource: getTrackingSource(panel.getRef()),
}); });
const drawer = new ShareDrawer({ const drawer = new ShareDrawer({
shareView: shareDashboardType.embed, shareView: shareDashboardType.snapshot,
panelRef: panel.getRef(), panelRef: panel.getRef(),
}); });
dashboard.showModal(drawer); dashboard.showModal(drawer);
}, },
}); });
if (
contextSrv.isSignedIn &&
config.snapshotEnabled &&
contextSrv.hasPermission(AccessControlAction.SnapshotsCreate)
) {
subMenu.push({
text: t('share-panel.menu.share-snapshot-title', 'Share snapshot'),
iconClassName: 'camera',
shortcut: 'p s',
onClick: () => {
DashboardInteractions.sharingCategoryClicked({
item: shareDashboardType.snapshot,
shareResource: getTrackingSource(panel.getRef()),
});
const drawer = new ShareDrawer({
shareView: shareDashboardType.snapshot,
panelRef: panel.getRef(),
});
dashboard.showModal(drawer);
},
});
}
items.push({
type: 'submenu',
text: t('panel.header-menu.share', 'Share'),
iconClassName: 'share-alt',
subMenu,
onClick: (e) => {
e.preventDefault();
},
});
} else {
items.push({
text: t('panel.header-menu.share', 'Share'),
iconClassName: 'share-alt',
onClick: () => {
dashboard.showModal(new ShareModal({ panelRef: panel.getRef() }));
},
shortcut: 'p s',
});
} }
items.push({
type: 'submenu',
text: t('panel.header-menu.share', 'Share'),
iconClassName: 'share-alt',
subMenu,
onClick: (e) => {
e.preventDefault();
},
});
if (dashboard.state.isEditing && !isReadOnlyRepeat && !isEditingPanel) { if (dashboard.state.isEditing && !isReadOnlyRepeat && !isEditingPanel) {
moreSubMenu.push({ moreSubMenu.push({
text: t('panel.header-menu.duplicate', `Duplicate`), text: t('panel.header-menu.duplicate', `Duplicate`),
@ -236,32 +224,18 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
}, },
}); });
} else { } else {
if (config.featureToggles.newDashboardSharingComponent) { moreSubMenu.push({
moreSubMenu.push({ text: t('share-panel.menu.new-library-panel-title', 'New library panel'),
text: t('share-panel.menu.new-library-panel-title', 'New library panel'), iconClassName: 'plus-square',
iconClassName: 'plus-square', onClick: () => {
onClick: () => { const drawer = new ShareDrawer({
const drawer = new ShareDrawer({ shareView: shareDashboardType.libraryPanel,
shareView: shareDashboardType.libraryPanel, panelRef: panel.getRef(),
panelRef: panel.getRef(), });
});
dashboard.showModal(drawer); dashboard.showModal(drawer);
}, },
}); });
} else {
moreSubMenu.push({
text: t('panel.header-menu.create-library-panel', `Create library panel`),
onClick: () => {
dashboard.showModal(
new ShareModal({
panelRef: panel.getRef(),
activeTab: shareDashboardType.libraryPanel,
})
);
},
});
}
} }
} }

View File

@ -10,7 +10,6 @@ import { AccessControlAction } from 'app/types/accessControl';
import { shareDashboardType } from '../../dashboard/components/ShareModal/utils'; import { shareDashboardType } from '../../dashboard/components/ShareModal/utils';
import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer'; import { PanelInspectDrawer } from '../inspect/PanelInspectDrawer';
import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer'; import { ShareDrawer } from '../sharing/ShareDrawer/ShareDrawer';
import { ShareModal } from '../sharing/ShareModal';
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph'; import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
import { findVizPanelByPathId } from '../utils/pathId'; import { findVizPanelByPathId } from '../utils/pathId';
import { getEditPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders'; import { getEditPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
@ -59,52 +58,43 @@ export function setupKeyboardShortcuts(scene: DashboardScene) {
}); });
// Panel share // Panel share
if (config.featureToggles.newDashboardSharingComponent) { keybindings.addBinding({
keybindings.addBinding({ key: 'p u',
key: 'p u', onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => { const drawer = new ShareDrawer({
const drawer = new ShareDrawer({ shareView: shareDashboardType.link,
shareView: shareDashboardType.link, panelRef: vizPanel.getRef(),
panelRef: vizPanel.getRef(),
});
scene.showModal(drawer);
}),
});
keybindings.addBinding({
key: 'p e',
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
const drawer = new ShareDrawer({
shareView: shareDashboardType.embed,
panelRef: vizPanel.getRef(),
});
scene.showModal(drawer);
}),
});
if (
contextSrv.isSignedIn &&
config.snapshotEnabled &&
contextSrv.hasPermission(AccessControlAction.SnapshotsCreate)
) {
keybindings.addBinding({
key: 'p s',
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
const drawer = new ShareDrawer({
shareView: shareDashboardType.snapshot,
panelRef: vizPanel.getRef(),
});
scene.showModal(drawer);
}),
}); });
}
} else { scene.showModal(drawer);
}),
});
keybindings.addBinding({
key: 'p e',
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
const drawer = new ShareDrawer({
shareView: shareDashboardType.embed,
panelRef: vizPanel.getRef(),
});
scene.showModal(drawer);
}),
});
if (
contextSrv.isSignedIn &&
config.snapshotEnabled &&
contextSrv.hasPermission(AccessControlAction.SnapshotsCreate)
) {
keybindings.addBinding({ keybindings.addBinding({
key: 'p s', key: 'p s',
onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => { onTrigger: withFocusedPanel(scene, async (vizPanel: VizPanel) => {
scene.showModal(new ShareModal({ panelRef: vizPanel.getRef() })); const drawer = new ShareDrawer({
shareView: shareDashboardType.snapshot,
panelRef: vizPanel.getRef(),
});
scene.showModal(drawer);
}), }),
}); });
} }

View File

@ -4,7 +4,7 @@ import { render } from 'test/test-utils';
import { getDefaultTimeRange, LoadingState } from '@grafana/data'; import { getDefaultTimeRange, LoadingState } from '@grafana/data';
import { getPanelPlugin } from '@grafana/data/test'; import { getPanelPlugin } from '@grafana/data/test';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { config, setPluginImportUtils } from '@grafana/runtime'; import { setPluginImportUtils } from '@grafana/runtime';
import { import {
CustomVariable, CustomVariable,
SceneQueryRunner, SceneQueryRunner,
@ -31,7 +31,6 @@ setPluginImportUtils({
}); });
beforeEach(() => { beforeEach(() => {
config.featureToggles.newDashboardSharingComponent = true;
jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true); jest.spyOn(contextSrv, 'hasPermission').mockReturnValue(true);
jest.spyOn(contextSrv, 'hasRole').mockReturnValue(true); jest.spyOn(contextSrv, 'hasRole').mockReturnValue(true);
}); });

View File

@ -1,5 +1,4 @@
import { t } from '@grafana/i18n'; import { t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes'; import { SceneComponentProps, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen'; import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen';
import { ShareLibraryPanel } from 'app/features/dashboard/components/ShareModal/ShareLibraryPanel'; import { ShareLibraryPanel } from 'app/features/dashboard/components/ShareModal/ShareLibraryPanel';
@ -22,9 +21,7 @@ export class ShareLibraryPanelTab extends SceneObjectBase<ShareLibraryPanelTabSt
static Component = ShareLibraryPanelTabRenderer; static Component = ShareLibraryPanelTabRenderer;
public getTabLabel() { public getTabLabel() {
return config.featureToggles.newDashboardSharingComponent return t('share-panel.drawer.new-library-panel-title', 'New library panel');
? t('share-panel.drawer.new-library-panel-title', 'New library panel')
: t('share-modal.tab-title.library-panel', 'Library panel');
} }
} }

View File

@ -1,141 +0,0 @@
import { ComponentProps } from 'react';
import { t } from '@grafana/i18n';
import { config, locationService } from '@grafana/runtime';
import { SceneComponentProps, SceneObjectBase, SceneObjectRef, SceneObjectState, VizPanel } from '@grafana/scenes';
import { Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { isPublicDashboardsEnabled } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
import { AccessControlAction } from 'app/types/accessControl';
import { getTrackingSource } from '../../dashboard/components/ShareModal/utils';
import { DashboardInteractions } from '../utils/interactions';
import { getDashboardSceneFor, isLibraryPanel } from '../utils/utils';
import { ShareExportTab } from './ShareExportTab';
import { ShareLibraryPanelTab } from './ShareLibraryPanelTab';
import { ShareLinkTab } from './ShareLinkTab';
import { SharePanelEmbedTab } from './SharePanelEmbedTab';
import { ShareSnapshotTab } from './ShareSnapshotTab';
import { SharePublicDashboardTab } from './public-dashboards/SharePublicDashboardTab';
import { ModalSceneObjectLike, SceneShareTab, SceneShareTabState } from './types';
interface ShareModalState extends SceneObjectState {
panelRef?: SceneObjectRef<VizPanel>;
tabs?: SceneShareTab[];
activeTab: string;
}
type customDashboardTabType = new (...args: SceneShareTabState[]) => SceneShareTab;
const customDashboardTabs: customDashboardTabType[] = [];
export function addDashboardShareTab(tab: customDashboardTabType) {
customDashboardTabs.push(tab);
}
/**
* Used for full dashboard share modal and the panel level share modal
*/
export class ShareModal extends SceneObjectBase<ShareModalState> implements ModalSceneObjectLike {
static Component = SharePanelModalRenderer;
constructor(state: Omit<ShareModalState, 'activeTab'> & { activeTab?: string }) {
super({
activeTab: 'link',
...state,
});
this.addActivationHandler(() => this.buildTabs(state.activeTab));
}
private buildTabs(activeTab?: string) {
const { panelRef } = this.state;
const modalRef = this.getRef();
const tabs: SceneShareTab[] = [new ShareLinkTab({ panelRef, modalRef })];
const dashboard = getDashboardSceneFor(this);
if (!panelRef) {
tabs.push(new ShareExportTab({ modalRef }));
}
if (
contextSrv.isSignedIn &&
config.snapshotEnabled &&
contextSrv.hasPermission(AccessControlAction.SnapshotsCreate)
) {
tabs.push(new ShareSnapshotTab({ panelRef, dashboardRef: dashboard.getRef(), modalRef }));
}
if (panelRef) {
tabs.push(new SharePanelEmbedTab({ panelRef }));
const panel = panelRef.resolve();
if (panel instanceof VizPanel) {
if (!isLibraryPanel(panel)) {
tabs.push(new ShareLibraryPanelTab({ panelRef, modalRef }));
}
}
}
if (!panelRef) {
tabs.push(...customDashboardTabs.map((Tab) => new Tab({ modalRef })));
if (isPublicDashboardsEnabled()) {
tabs.push(new SharePublicDashboardTab({ modalRef }));
}
}
const at = tabs.find((t) => t.tabId === activeTab);
this.setState({ activeTab: at?.tabId ?? tabs[0].tabId, tabs });
}
onDismiss = () => {
if (this.state.panelRef) {
const dashboard = getDashboardSceneFor(this);
dashboard.closeModal();
} else {
locationService.partial({ shareView: null });
}
};
onChangeTab: ComponentProps<typeof ModalTabsHeader>['onChangeTab'] = (tab) => {
DashboardInteractions.sharingCategoryClicked({
item: tab.value,
shareResource: getTrackingSource(this.state.panelRef),
});
this.setState({ activeTab: tab.value });
};
}
function SharePanelModalRenderer({ model }: SceneComponentProps<ShareModal>) {
const { panelRef, tabs, activeTab } = model.useState();
const title = panelRef ? t('share-modal.panel.title', 'Share Panel') : t('share-modal.dashboard.title', 'Share');
if (!tabs) {
return;
}
const modalTabs = tabs?.map((tab) => ({
label: tab.getTabLabel(),
value: tab.tabId,
}));
const header = (
<ModalTabsHeader
title={title}
icon="share-alt"
tabs={modalTabs}
activeTab={activeTab}
onChangeTab={model.onChangeTab}
/>
);
const currentTab = tabs.find((t) => t.tabId === activeTab);
return (
<Modal isOpen={true} title={header} onDismiss={model.onDismiss}>
<TabContent>{currentTab && <currentTab.Component model={currentTab} />}</TabContent>
</Modal>
);
}

View File

@ -1,6 +1,5 @@
import { TimeRange } from '@grafana/data'; import { TimeRange } from '@grafana/data';
import { t } from '@grafana/i18n'; import { t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes'; import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes';
import { ShareEmbed } from 'app/features/dashboard/components/ShareModal/ShareEmbed'; import { ShareEmbed } from 'app/features/dashboard/components/ShareModal/ShareEmbed';
import { buildParams, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils'; import { buildParams, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
@ -25,9 +24,7 @@ export class SharePanelEmbedTab extends SceneObjectBase<SharePanelEmbedTabState>
} }
public getTabLabel() { public getTabLabel() {
return config.featureToggles.newDashboardSharingComponent return t('share-panel.drawer.share-embed-title', 'Share embed');
? t('share-panel.drawer.share-embed-title', 'Share embed')
: t('share-modal.tab-title.panel-embed', 'Embed');
} }
} }

View File

@ -1,82 +0,0 @@
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { Trans, t } from '@grafana/i18n';
import { SceneComponentProps, sceneGraph } from '@grafana/scenes';
import { useStyles2 } from '@grafana/ui';
import { contextSrv } from 'app/core/core';
import { useDeletePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
import { ConfigPublicDashboardBase } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/ConfigPublicDashboard/ConfigPublicDashboard';
import { PublicDashboard } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
import { AccessControlAction } from 'app/types/accessControl';
import { shareDashboardType } from '../../../dashboard/components/ShareModal/utils';
import { getDashboardSceneFor } from '../../utils/utils';
import { ShareModal } from '../ShareModal';
import { ConfirmModal } from './ConfirmModal';
import { SharePublicDashboardTab } from './SharePublicDashboardTab';
import { useUnsupportedDatasources } from './hooks';
interface Props extends SceneComponentProps<SharePublicDashboardTab> {
publicDashboard?: PublicDashboard;
isGetLoading?: boolean;
}
export function ConfigPublicDashboard({ model, publicDashboard, isGetLoading }: Props) {
const styles = useStyles2(getStyles);
const hasWritePermissions = contextSrv.hasPermission(AccessControlAction.DashboardsPublicWrite);
const dashboard = getDashboardSceneFor(model);
const { isDirty } = dashboard.useState();
const [deletePublicDashboard] = useDeletePublicDashboardMutation();
const hasTemplateVariables = (dashboard.state.$variables?.state.variables.length ?? 0) > 0;
const unsupportedDataSources = useUnsupportedDatasources(dashboard);
const timeRangeState = sceneGraph.getTimeRange(model);
const timeRange = timeRangeState.useState();
return (
<ConfigPublicDashboardBase
dashboard={dashboard}
publicDashboard={publicDashboard}
unsupportedDatasources={unsupportedDataSources}
onRevoke={() => {
dashboard.showModal(
new ConfirmModal({
isOpen: true,
title: t('dashboard-scene.config-public-dashboard.title.revoke-public-url', 'Revoke public URL'),
icon: 'trash-alt',
confirmText: t(
'dashboard-scene.config-public-dashboard.confirmText.revoke-public-url',
'Revoke public URL'
),
body: (
<p className={styles.description}>
<Trans i18nKey="public-dashboard.config.revoke-body">
Are you sure you want to revoke this URL? The dashboard will no longer be public.
</Trans>
</p>
),
onDismiss: () => {
dashboard.showModal(new ShareModal({ activeTab: shareDashboardType.publicDashboard }));
},
onConfirm: () => {
deletePublicDashboard({ dashboard, dashboardUid: dashboard.state.uid!, uid: publicDashboard!.uid });
dashboard.closeModal();
},
})
);
}}
timeRange={timeRange.value}
showSaveChangesAlert={hasWritePermissions && isDirty}
hasTemplateVariables={hasTemplateVariables}
/>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
description: css({
fontSize: theme.typography.body.fontSize,
}),
});

View File

@ -1,21 +0,0 @@
import { SceneComponentProps } from '@grafana/scenes';
import { CreatePublicDashboardBase } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/CreatePublicDashboard/CreatePublicDashboard';
import { getDashboardSceneFor } from '../../utils/utils';
import { SharePublicDashboardTab } from './SharePublicDashboardTab';
import { useUnsupportedDatasources } from './hooks';
export function CreatePublicDashboard({ model }: SceneComponentProps<SharePublicDashboardTab>) {
const dashboard = getDashboardSceneFor(model);
const unsupportedDataSources = useUnsupportedDatasources(dashboard);
const hasTemplateVariables = (dashboard.state.$variables?.state.variables.length ?? 0) > 0;
return (
<CreatePublicDashboardBase
dashboard={dashboard}
unsupportedDatasources={unsupportedDataSources}
unsupportedTemplateVariables={hasTemplateVariables}
/>
);
}

View File

@ -1,39 +0,0 @@
import { t } from '@grafana/i18n';
import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes';
import { useGetPublicDashboardQuery } from 'app/features/dashboard/api/publicDashboardApi';
import { Loader } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboard';
import { publicDashboardPersisted } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SharePublicDashboardUtils';
import { shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils';
import { getDashboardSceneFor } from '../../utils/utils';
import { SceneShareTabState } from '../types';
import { ConfigPublicDashboard } from './ConfigPublicDashboard';
import { CreatePublicDashboard } from './CreatePublicDashboard';
export class SharePublicDashboardTab extends SceneObjectBase<SceneShareTabState> {
public tabId = shareDashboardType.publicDashboard;
static Component = SharePublicDashboardTabRenderer;
public getTabLabel() {
return t('share-modal.tab-title.public-dashboard', 'Public Dashboard');
}
}
function SharePublicDashboardTabRenderer({ model }: SceneComponentProps<SharePublicDashboardTab>) {
const { data: publicDashboard, isLoading: isGetLoading } = useGetPublicDashboardQuery(
getDashboardSceneFor(model).state.uid!
);
return (
<>
{isGetLoading ? (
<Loader />
) : !publicDashboardPersisted(publicDashboard) ? (
<CreatePublicDashboard model={model} />
) : (
<ConfigPublicDashboard model={model} publicDashboard={publicDashboard} isGetLoading={isGetLoading} />
)}
</>
);
}

View File

@ -64,13 +64,10 @@ export const publicDashboardApi = createApi({
}, },
async onQueryStarted({ dashboard, payload: { share } }, { dispatch, queryFulfilled }) { async onQueryStarted({ dashboard, payload: { share } }, { dispatch, queryFulfilled }) {
const { data } = await queryFulfilled; const { data } = await queryFulfilled;
let message = t('public-dashboard.sharing.success-creation', 'Dashboard is public!'); const message =
if (config.featureToggles.newDashboardSharingComponent) { share === PublicDashboardShareType.PUBLIC
message = ? t('public-dashboard.public-sharing.success-creation', 'Your dashboard is now publicly accessible')
share === PublicDashboardShareType.PUBLIC : t('public-dashboard.email-sharing.success-creation', 'Your dashboard is ready for external sharing');
? t('public-dashboard.public-sharing.success-creation', 'Your dashboard is now publicly accessible')
: t('public-dashboard.email-sharing.success-creation', 'Your dashboard is ready for external sharing');
}
dispatch(notifyApp(createSuccessNotification(message))); dispatch(notifyApp(createSuccessNotification(message)));
if (dashboard instanceof DashboardScene) { if (dashboard instanceof DashboardScene) {
@ -107,9 +104,7 @@ export const publicDashboardApi = createApi({
dispatch( dispatch(
notifyApp( notifyApp(
createSuccessNotification( createSuccessNotification(
config.featureToggles.newDashboardSharingComponent t('public-dashboard.configuration.success-update', 'Settings have been successfully updated')
? t('public-dashboard.configuration.success-update', 'Settings have been successfully updated')
: t('public-dashboard.configuration.success-update-old', 'Public dashboard updated!')
) )
) )
); );
@ -136,12 +131,9 @@ export const publicDashboardApi = createApi({
}, },
async onQueryStarted({ dashboard, payload: { isEnabled } }, { dispatch, queryFulfilled }) { async onQueryStarted({ dashboard, payload: { isEnabled } }, { dispatch, queryFulfilled }) {
const { data } = await queryFulfilled; const { data } = await queryFulfilled;
let message = t('public-dashboard.configuration.success-update-old', 'Public dashboard updated!'); const message = isEnabled
if (config.featureToggles.newDashboardSharingComponent) { ? t('public-dashboard.configuration.success-resume', 'Your dashboard access has been resumed')
message = isEnabled : t('public-dashboard.configuration.success-pause', 'Your dashboard access has been paused');
? t('public-dashboard.configuration.success-resume', 'Your dashboard access has been resumed')
: t('public-dashboard.configuration.success-pause', 'Your dashboard access has been paused');
}
dispatch(notifyApp(createSuccessNotification(message))); dispatch(notifyApp(createSuccessNotification(message)));
if (dashboard instanceof DashboardScene) { if (dashboard instanceof DashboardScene) {
@ -176,20 +168,16 @@ export const publicDashboardApi = createApi({
}, },
async onQueryStarted({ dashboard, payload: { share } }, { dispatch, queryFulfilled }) { async onQueryStarted({ dashboard, payload: { share } }, { dispatch, queryFulfilled }) {
await queryFulfilled; await queryFulfilled;
let message = t('public-dashboard.configuration.success-update-old', 'Public dashboard updated!'); const message =
share === PublicDashboardShareType.PUBLIC
if (config.featureToggles.newDashboardSharingComponent) { ? t(
message = 'public-dashboard.public-sharing.success-share-type-change',
share === PublicDashboardShareType.PUBLIC 'Dashboard access updated: Anyone with the link can now access'
? t( )
'public-dashboard.public-sharing.success-share-type-change', : t(
'Dashboard access updated: Anyone with the link can now access' 'public-dashboard.email-sharing.success-share-type-change',
) 'Dashboard access updated: Only specific people can now access with the link'
: t( );
'public-dashboard.email-sharing.success-share-type-change',
'Dashboard access updated: Only specific people can now access with the link'
);
}
dispatch(notifyApp(createSuccessNotification(message))); dispatch(notifyApp(createSuccessNotification(message)));
}, },
invalidatesTags: (result, error, { payload }) => [ invalidatesTags: (result, error, { payload }) => [
@ -250,9 +238,7 @@ export const publicDashboardApi = createApi({
dispatch( dispatch(
notifyApp( notifyApp(
createSuccessNotification( createSuccessNotification(
config.featureToggles.newDashboardSharingComponent t('public-dashboard.share.success-delete', 'Your dashboard is no longer shareable')
? t('public-dashboard.share.success-delete', 'Your dashboard is no longer shareable')
: t('public-dashboard.share.success-delete-old', 'Public dashboard deleted!')
) )
) )
); );

View File

@ -2,8 +2,7 @@ import { FormEvent, useEffect, useState } from 'react';
import { RawTimeRange, TimeRange } from '@grafana/data'; import { RawTimeRange, TimeRange } from '@grafana/data';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime'; import { Button, ClipboardButton, Field, Label, Stack, Switch, TextArea } from '@grafana/ui';
import { Button, ClipboardButton, Field, Label, Modal, Stack, Switch, TextArea } from '@grafana/ui';
import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions'; import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions';
import { ThemePicker } from './ThemePicker'; import { ThemePicker } from './ThemePicker';
@ -104,16 +103,12 @@ export function ShareEmbed({
onChange={onIframeHtmlChange} onChange={onIframeHtmlChange}
/> />
</Field> </Field>
{config.featureToggles.newDashboardSharingComponent ? ( <Stack gap={1} justifyContent={'start'}>
<Stack gap={1} justifyContent={'start'}> {clipboardButton}
{clipboardButton} <Button variant="secondary" fill="outline" onClick={onCancelClick}>
<Button variant="secondary" fill="outline" onClick={onCancelClick}> <Trans i18nKey="snapshot.share.cancel-button">Cancel</Trans>
<Trans i18nKey="snapshot.share.cancel-button">Cancel</Trans> </Button>
</Button> </Stack>
</Stack>
) : (
<Modal.ButtonRow>{clipboardButton}</Modal.ButtonRow>
)}
</> </>
); );
} }

View File

@ -1,34 +1,21 @@
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Alert } from '@grafana/ui'; import { Alert } from '@grafana/ui';
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
export const NoUpsertPermissionsAlert = ({ mode }: { mode: 'create' | 'edit' }) => { export const NoUpsertPermissionsAlert = ({ mode }: { mode: 'create' | 'edit' }) => {
const title = config.featureToggles.newDashboardSharingComponent const title = t(
? t( 'public-dashboard.share-externally.no-upsert-perm-alert-title',
'public-dashboard.share-externally.no-upsert-perm-alert-title', 'You dont have permission to {{ action }} a shared dashboard',
'You dont have permission to {{ action }} a shared dashboard', { action: mode }
{ action: mode } );
)
: t(
'public-dashboard.modal-alerts.no-upsert-perm-alert-title',
'You dont have permission to {{ action }} a public dashboard',
{ action: mode }
);
return ( return (
<Alert severity="warning" title={title} data-testid={selectors.NoUpsertPermissionsWarningAlert} bottomSpacing={0}> <Alert severity="warning" title={title} data-testid={selectors.NoUpsertPermissionsWarningAlert} bottomSpacing={0}>
{config.featureToggles.newDashboardSharingComponent ? ( <Trans i18nKey="public-dashboard.share-externally.no-upsert-perm-alert-desc">
<Trans i18nKey="public-dashboard.share-externally.no-upsert-perm-alert-desc"> Contact your admin to get permission to {{ action: mode }} shared dashboards
Contact your admin to get permission to {{ action: mode }} shared dashboards </Trans>
</Trans>
) : (
<Trans i18nKey="public-dashboard.modal-alerts.no-upsert-perm-alert-desc">
Contact your admin to get permission to {{ action: mode }} public dashboards
</Trans>
)}
</Alert> </Alert>
); );
}; };

View File

@ -4,7 +4,6 @@ import cx from 'classnames';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Alert, useStyles2 } from '@grafana/ui'; import { Alert, useStyles2 } from '@grafana/ui';
const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard; const selectors = e2eSelectors.pages.ShareDashboardModal.PublicDashboard;
@ -20,23 +19,14 @@ export const UnsupportedDataSourcesAlert = ({ unsupportedDataSources }: { unsupp
bottomSpacing={0} bottomSpacing={0}
> >
<p className={styles.unsupportedDataSourceDescription}> <p className={styles.unsupportedDataSourceDescription}>
{config.featureToggles.newDashboardSharingComponent ? ( <Trans i18nKey="public-dashboard.share-externally.unsupported-data-source-alert-desc">
<Trans i18nKey="public-dashboard.share-externally.unsupported-data-source-alert-desc"> There are data sources in this dashboard that are unsupported for shared dashboards. Panels that use these
There are data sources in this dashboard that are unsupported for shared dashboards. Panels that use these data sources may not function properly: {{ unsupportedDataSources }}.
data sources may not function properly: {{ unsupportedDataSources }}. </Trans>
</Trans>
) : (
<Trans i18nKey="public-dashboard.modal-alerts.unsupported-data-source-alert-desc">
There are data sources in this dashboard that are unsupported for public dashboards. Panels that use these
data sources may not function properly: {{ unsupportedDataSources }}.
</Trans>
)}
</p> </p>
<a <a
href={ href={
config.featureToggles.newDashboardSharingComponent 'https://grafana.com/docs/grafana/next/dashboards/share-dashboards-panels/shared-dashboards/#supported-data-sources'
? 'https://grafana.com/docs/grafana/next/dashboards/share-dashboards-panels/shared-dashboards/#supported-data-sources'
: 'https://grafana.com/docs/grafana/v11.2/dashboards/dashboard-public/#supported-data-sources'
} }
target="blank" target="blank"
className={cx('text-link', styles.unsupportedDataSourceDescription)} className={cx('text-link', styles.unsupportedDataSourceDescription)}

View File

@ -2,6 +2,7 @@ import { PureComponent } from 'react';
import * as React from 'react'; import * as React from 'react';
import { isEmptyObject, SelectableValue, VariableRefresh } from '@grafana/data'; import { isEmptyObject, SelectableValue, VariableRefresh } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { getBackendSrv } from '@grafana/runtime'; import { getBackendSrv } from '@grafana/runtime';
import { Button, ClipboardButton, Field, Input, LinkButton, Modal, Select, Spinner, Stack } from '@grafana/ui'; import { Button, ClipboardButton, Field, Input, LinkButton, Modal, Select, Spinner, Stack } from '@grafana/ui';
@ -30,6 +31,8 @@ interface State {
sharingButtonText: string; sharingButtonText: string;
} }
const selectors = e2eSelectors.pages.ShareDashboardModal.SnapshotScene;
export class ShareSnapshot extends PureComponent<Props, State> { export class ShareSnapshot extends PureComponent<Props, State> {
private dashboard: DashboardModel; private dashboard: DashboardModel;
private expireOptions: Array<SelectableValue<number>>; private expireOptions: Array<SelectableValue<number>>;
@ -280,7 +283,12 @@ export class ShareSnapshot extends PureComponent<Props, State> {
{sharingButtonText} {sharingButtonText}
</Button> </Button>
)} )}
<Button variant="primary" disabled={isLoading} onClick={this.createSnapshot()}> <Button
variant="primary"
disabled={isLoading}
onClick={this.createSnapshot()}
data-testid={selectors.PublishSnapshot}
>
<Trans i18nKey="share-modal.snapshot.local-button">Publish Snapshot</Trans> <Trans i18nKey="share-modal.snapshot.local-button">Publish Snapshot</Trans>
</Button> </Button>
</Modal.ButtonRow> </Modal.ButtonRow>
@ -297,9 +305,15 @@ export class ShareSnapshot extends PureComponent<Props, State> {
<Input <Input
id="snapshot-url-input" id="snapshot-url-input"
value={snapshotUrl} value={snapshotUrl}
data-testid={selectors.CopyUrlInput}
readOnly readOnly
addonAfter={ addonAfter={
<ClipboardButton icon="copy" variant="primary" getText={this.getSnapshotUrl}> <ClipboardButton
icon="copy"
variant="primary"
getText={this.getSnapshotUrl}
data-testid={selectors.CopyUrlButton}
>
<Trans i18nKey="share-modal.snapshot.copy-link-button">Copy</Trans> <Trans i18nKey="share-modal.snapshot.copy-link-button">Copy</Trans>
</ClipboardButton> </ClipboardButton>
} }

View File

@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { useAsync, useDebounce } from 'react-use'; import { useAsync, useDebounce } from 'react-use';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config, FetchError, isFetchError } from '@grafana/runtime'; import { FetchError, isFetchError } from '@grafana/runtime';
import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen'; import { LibraryPanel } from '@grafana/schema/dist/esm/index.gen';
import { Button, Field, Input, Modal, Stack } from '@grafana/ui'; import { Button, Field, Input, Modal, Stack } from '@grafana/ui';
import { FolderPicker } from 'app/core/components/Select/FolderPicker'; import { FolderPicker } from 'app/core/components/Select/FolderPicker';
@ -86,25 +86,14 @@ export const AddLibraryPanelContents = ({
> >
<FolderPicker onChange={(uid) => setFolderUid(uid)} value={folderUid} /> <FolderPicker onChange={(uid) => setFolderUid(uid)} value={folderUid} />
</Field> </Field>
{config.featureToggles.newDashboardSharingComponent ? ( <Stack gap={1} justifyContent={'start'}>
<Stack gap={1} justifyContent={'start'}> <Button onClick={onCreate} disabled={invalidInput}>
<Button onClick={onCreate} disabled={invalidInput}> <Trans i18nKey="share-panel.new-library-panel.create-button">Create library panel</Trans>
<Trans i18nKey="share-panel.new-library-panel.create-button">Create library panel</Trans> </Button>
</Button> <Button variant="secondary" onClick={onDismiss} fill="outline">
<Button variant="secondary" onClick={onDismiss} fill="outline"> <Trans i18nKey="share-panel.new-library-panel.cancel-button">Cancel</Trans>
<Trans i18nKey="share-panel.new-library-panel.cancel-button">Cancel</Trans> </Button>
</Button> </Stack>
</Stack>
) : (
<Modal.ButtonRow>
<Button variant="secondary" onClick={onDismiss} fill="outline">
<Trans i18nKey="library-panel.add-modal.cancel">Cancel</Trans>
</Button>
<Button onClick={onCreate} disabled={invalidInput}>
<Trans i18nKey="library-panel.add-modal.create">Create library panel</Trans>
</Button>
</Modal.ButtonRow>
)}
</> </>
); );
}; };

View File

@ -1,7 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { t } from '@grafana/i18n'; import { t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { Button, ModalsController, ButtonProps } from '@grafana/ui'; import { Button, ModalsController, ButtonProps } from '@grafana/ui';
import { useDeletePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi'; import { useDeletePublicDashboardMutation } from 'app/features/dashboard/api/publicDashboardApi';
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
@ -42,9 +41,7 @@ export const DeletePublicDashboardButton = ({
return ( return (
<ModalsController> <ModalsController>
{({ showModal, hideModal }) => { {({ showModal, hideModal }) => {
const translatedRevocationButtonText = config.featureToggles.newDashboardSharingComponent const translatedRevocationButtonText = t('shared-dashboard-list.button.revoke-button-text', 'Revoke access');
? t('shared-dashboard-list.button.revoke-button-text', 'Revoke access')
: t('public-dashboard-list.button.revoke-button-text', 'Revoke public URL');
return ( return (
<Button <Button
aria-label={translatedRevocationButtonText} aria-label={translatedRevocationButtonText}

View File

@ -2,7 +2,6 @@ import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n'; import { t } from '@grafana/i18n';
import { config } from '@grafana/runtime';
import { ConfirmModal, useStyles2 } from '@grafana/ui'; import { ConfirmModal, useStyles2 } from '@grafana/ui';
const Body = () => { const Body = () => {
@ -10,15 +9,10 @@ const Body = () => {
return ( return (
<p className={styles.description}> <p className={styles.description}>
{config.featureToggles.newDashboardSharingComponent {t(
? t( 'shared-dashboard.delete-modal.revoke-body-text',
'shared-dashboard.delete-modal.revoke-body-text', 'Are you sure you want to revoke this access? The dashboard can no longer be shared.'
'Are you sure you want to revoke this access? The dashboard can no longer be shared.' )}
)
: t(
'public-dashboard.delete-modal.revoke-body-text',
'Are you sure you want to revoke this URL? The dashboard will no longer be public.'
)}
</p> </p>
); );
}; };
@ -30,9 +24,7 @@ export const DeletePublicDashboardModal = ({
onConfirm: () => void; onConfirm: () => void;
onDismiss: () => void; onDismiss: () => void;
}) => { }) => {
const translatedRevocationModalText = config.featureToggles.newDashboardSharingComponent const translatedRevocationModalText = t('shared-dashboard.delete-modal.revoke-title', 'Revoke access');
? t('shared-dashboard.delete-modal.revoke-title', 'Revoke access')
: t('public-dashboard.delete-modal.revoke-title', 'Revoke public URL');
return ( return (
<ConfirmModal <ConfirmModal
isOpen isOpen

View File

@ -5,7 +5,7 @@ import { useMedia } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src'; import { selectors as e2eSelectors } from '@grafana/e2e-selectors/src';
import { Trans, t } from '@grafana/i18n'; import { Trans, t } from '@grafana/i18n';
import { config, reportInteraction } from '@grafana/runtime'; import { reportInteraction } from '@grafana/runtime';
import { import {
Card, Card,
EmptyState, EmptyState,
@ -57,11 +57,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
const CardActions = useMemo(() => (isMobile ? Card.Actions : Card.SecondaryActions), [isMobile]); const CardActions = useMemo(() => (isMobile ? Card.Actions : Card.SecondaryActions), [isMobile]);
const isNewSharingComponentEnabled = config.featureToggles.newDashboardSharingComponent; const translatedPauseSharingText = t('shared-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause access');
const translatedPauseSharingText = isNewSharingComponentEnabled
? t('shared-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause access')
: t('public-dashboard-list.toggle.pause-sharing-toggle-text', 'Pause sharing');
return ( return (
<Card className={styles.card} href={`/d/${pd.dashboardUid}`}> <Card className={styles.card} href={`/d/${pd.dashboardUid}`}>
<Card.Heading className={styles.heading}> <Card.Heading className={styles.heading}>
@ -91,11 +87,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
color={theme.colors.warning.text} color={theme.colors.warning.text}
href={generatePublicDashboardUrl(pd.accessToken)} href={generatePublicDashboardUrl(pd.accessToken)}
key="public-dashboard-url" key="public-dashboard-url"
tooltip={ tooltip={t('shared-dashboard-list.button.view-button-tooltip', 'View shared dashboard')}
isNewSharingComponentEnabled
? t('shared-dashboard-list.button.view-button-tooltip', 'View shared dashboard')
: t('public-dashboard-list.button.view-button-tooltip', 'View public dashboard')
}
data-testid={selectors.ListItem.linkButton} data-testid={selectors.ListItem.linkButton}
/> />
<LinkButton <LinkButton
@ -105,11 +97,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
color={theme.colors.warning.text} color={theme.colors.warning.text}
href={generatePublicDashboardConfigUrl(pd.dashboardUid, pd.slug)} href={generatePublicDashboardConfigUrl(pd.dashboardUid, pd.slug)}
key="public-dashboard-config-url" key="public-dashboard-config-url"
tooltip={ tooltip={t('shared-dashboard-list.button.config-button-tooltip', 'Configure shared dashboard')}
isNewSharingComponentEnabled
? t('shared-dashboard-list.button.config-button-tooltip', 'Configure shared dashboard')
: t('public-dashboard-list.button.config-button-tooltip', 'Configure public dashboard')
}
data-testid={selectors.ListItem.configButton} data-testid={selectors.ListItem.configButton}
/> />
{hasWritePermissions && ( {hasWritePermissions && (
@ -118,11 +106,7 @@ const PublicDashboardCard = ({ pd }: { pd: PublicDashboardListResponse }) => {
icon="trash-alt" icon="trash-alt"
variant="secondary" variant="secondary"
publicDashboard={pd} publicDashboard={pd}
tooltip={ tooltip={t('shared-dashboard-list.button.revoke-button-tooltip', 'Revoke access')}
isNewSharingComponentEnabled
? t('shared-dashboard-list.button.revoke-button-tooltip', 'Revoke access')
: t('public-dashboard-list.button.revoke-button-tooltip', 'Revoke public dashboard URL')
}
loader={<Spinner />} loader={<Spinner />}
data-testid={selectors.ListItem.trashcanButton} data-testid={selectors.ListItem.trashcanButton}
/> />
@ -144,43 +128,23 @@ export const PublicDashboardListTable = () => {
{!isLoading && !isError && !!paginatedPublicDashboards && ( {!isLoading && !isError && !!paginatedPublicDashboards && (
<div> <div>
{paginatedPublicDashboards.publicDashboards.length === 0 ? ( {paginatedPublicDashboards.publicDashboards.length === 0 ? (
config.featureToggles.newDashboardSharingComponent ? ( <EmptyState
<EmptyState variant="call-to-action"
variant="call-to-action" message={t(
message={t( 'shared-dashboard-list.empty-state.message',
'shared-dashboard-list.empty-state.message', "You haven't created any shared dashboards yet"
"You haven't created any shared dashboards yet" )}
)} >
> <Trans i18nKey="shared-dashboard-list.empty-state.more-info">
<Trans i18nKey="shared-dashboard-list.empty-state.more-info"> Create a shared dashboard from any existing dashboard through the <b>Share</b> modal.{' '}
Create a shared dashboard from any existing dashboard through the <b>Share</b> modal.{' '} <TextLink
<TextLink external
external href="https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/shared-dashboards"
href="https://grafana.com/docs/grafana/latest/dashboards/share-dashboards-panels/shared-dashboards" >
> Learn more
Learn more </TextLink>
</TextLink> </Trans>
</Trans> </EmptyState>
</EmptyState>
) : (
<EmptyState
variant="call-to-action"
message={t(
'public-dashboard-list.empty-state.message',
"You haven't created any public dashboards yet"
)}
>
<Trans i18nKey="public-dashboard-list.empty-state.more-info">
Create a public dashboard from any existing dashboard through the <b>Share</b> modal.{' '}
<TextLink
external
href="https://grafana.com/docs/grafana/latest/dashboards/dashboard-public/#make-a-dashboard-public"
>
Learn more
</TextLink>
</Trans>
</EmptyState>
)
) : ( ) : (
<> <>
<ul className={styles.list}> <ul className={styles.list}>

View File

@ -5407,10 +5407,6 @@
"save-dashboard-short": "Save", "save-dashboard-short": "Save",
"save-library-panel": "Save library panel", "save-library-panel": "Save library panel",
"settings": "Dashboard settings", "settings": "Dashboard settings",
"share": {
"label": "Share",
"tooltip": "Share dashboard"
},
"share-button": "Share", "share-button": "Share",
"unlink-library-panel": "Unlink library panel", "unlink-library-panel": "Unlink library panel",
"unmark-favorite": "Unmark as favorite" "unmark-favorite": "Unmark as favorite"
@ -5663,14 +5659,6 @@
} }
} }
}, },
"config-public-dashboard": {
"confirmText": {
"revoke-public-url": "Revoke public URL"
},
"title": {
"revoke-public-url": "Revoke public URL"
}
},
"constant-variable-form": { "constant-variable-form": {
"constant-options": "Constant options", "constant-options": "Constant options",
"label-value": "Value", "label-value": "Value",
@ -9273,8 +9261,6 @@
}, },
"library-panel": { "library-panel": {
"add-modal": { "add-modal": {
"cancel": "Cancel",
"create": "Create library panel",
"error": "Library panel with this name already exists", "error": "Library panel with this name already exists",
"folder": "Save in folder", "folder": "Save in folder",
"folder-description": "Library panel permissions are derived from the folder permissions", "folder-description": "Library panel permissions are derived from the folder permissions",
@ -10343,9 +10329,6 @@
"profiles": { "profiles": {
"title": "Profiles" "title": "Profiles"
}, },
"public": {
"title": "Public dashboards"
},
"recently-deleted": { "recently-deleted": {
"subtitle": "Any items listed here for more than 30 days will be automatically deleted.", "subtitle": "Any items listed here for more than 30 days will be automatically deleted.",
"title": "Recently deleted" "title": "Recently deleted"
@ -11765,7 +11748,6 @@
"email-share-type-option-label": "Only specified people", "email-share-type-option-label": "Only specified people",
"pause-sharing-dashboard-label": "Pause sharing dashboard", "pause-sharing-dashboard-label": "Pause sharing dashboard",
"public-share-type-option-label": "Anyone with a link", "public-share-type-option-label": "Anyone with a link",
"revoke-body": "Are you sure you want to revoke this URL? The dashboard will no longer be public.",
"revoke-public-URL-button": "Revoke public URL", "revoke-public-URL-button": "Revoke public URL",
"revoke-public-URL-button-title": "Revoke public URL", "revoke-public-URL-button-title": "Revoke public URL",
"settings-title": "Settings" "settings-title": "Settings"
@ -11779,7 +11761,6 @@
"success-pause": "Your dashboard access has been paused", "success-pause": "Your dashboard access has been paused",
"success-resume": "Your dashboard access has been resumed", "success-resume": "Your dashboard access has been resumed",
"success-update": "Settings have been successfully updated", "success-update": "Settings have been successfully updated",
"success-update-old": "Public dashboard updated!",
"time-range-label": "Time range", "time-range-label": "Time range",
"time-range-tooltip": "The shared dashboard uses the default time range settings of the dashboard" "time-range-tooltip": "The shared dashboard uses the default time range settings of the dashboard"
}, },
@ -11788,10 +11769,6 @@
"unsupported-features-desc": "Currently, we dont support template variables or frontend data sources", "unsupported-features-desc": "Currently, we dont support template variables or frontend data sources",
"welcome-title": "Welcome to public dashboards!" "welcome-title": "Welcome to public dashboards!"
}, },
"delete-modal": {
"revoke-body-text": "Are you sure you want to revoke this URL? The dashboard will no longer be public.",
"revoke-title": "Revoke public URL"
},
"email-sharing": { "email-sharing": {
"accept-button": "Accept", "accept-button": "Accept",
"alert-text": "Sharing dashboards by email is billed per user for the duration of the 30-day token, regardless of how many dashboards are shared. Billing stops after 30 days unless you renew the token.", "alert-text": "Sharing dashboards by email is billed per user for the duration of the 30-day token, regardless of how many dashboards are shared. Billing stops after 30 days unless you renew the token.",
@ -11821,11 +11798,8 @@
"success-share-type-change": "Dashboard access updated: Only specific people can now access with the link" "success-share-type-change": "Dashboard access updated: Only specific people can now access with the link"
}, },
"modal-alerts": { "modal-alerts": {
"no-upsert-perm-alert-desc": "Contact your admin to get permission to {{action}} public dashboards",
"no-upsert-perm-alert-title": "You dont have permission to {{ action }} a public dashboard",
"save-dashboard-changes-alert-title": "Please save your dashboard changes before updating the public configuration", "save-dashboard-changes-alert-title": "Please save your dashboard changes before updating the public configuration",
"unsupport-data-source-alert-readmore-link": "Read more about supported data sources", "unsupport-data-source-alert-readmore-link": "Read more about supported data sources",
"unsupported-data-source-alert-desc": "There are data sources in this dashboard that are unsupported for public dashboards. Panels that use these data sources may not function properly: {{unsupportedDataSources}}.",
"unsupported-data-source-alert-title": "Unsupported data sources", "unsupported-data-source-alert-title": "Unsupported data sources",
"unsupported-template-variable-alert-desc": "This public dashboard may not work since it uses template variables", "unsupported-template-variable-alert-desc": "This public dashboard may not work since it uses template variables",
"unsupported-template-variable-alert-title": "Template variables are not supported" "unsupported-template-variable-alert-title": "Template variables are not supported"
@ -11859,8 +11833,7 @@
"time-range-text": "Time range = " "time-range-text": "Time range = "
}, },
"share": { "share": {
"success-delete": "Your dashboard is no longer shareable", "success-delete": "Your dashboard is no longer shareable"
"success-delete-old": "Public dashboard deleted!"
}, },
"share-configuration": { "share-configuration": {
"share-type-label": "Link access" "share-type-label": "Link access"
@ -11879,24 +11852,6 @@
"revoke-access-button": "Revoke access", "revoke-access-button": "Revoke access",
"revoke-access-description": "Are you sure you want to revoke this access? The dashboard can no longer be shared.", "revoke-access-description": "Are you sure you want to revoke this access? The dashboard can no longer be shared.",
"unsupported-data-source-alert-desc": "There are data sources in this dashboard that are unsupported for shared dashboards. Panels that use these data sources may not function properly: {{unsupportedDataSources}}." "unsupported-data-source-alert-desc": "There are data sources in this dashboard that are unsupported for shared dashboards. Panels that use these data sources may not function properly: {{unsupportedDataSources}}."
},
"sharing": {
"success-creation": "Dashboard is public!"
}
},
"public-dashboard-list": {
"button": {
"config-button-tooltip": "Configure public dashboard",
"revoke-button-text": "Revoke public URL",
"revoke-button-tooltip": "Revoke public dashboard URL",
"view-button-tooltip": "View public dashboard"
},
"empty-state": {
"message": "You haven't created any public dashboards yet",
"more-info": "Create a public dashboard from any existing dashboard through the <1>Share</1> modal. <4>Learn more</4>"
},
"toggle": {
"pause-sharing-toggle-text": "Pause sharing"
} }
}, },
"public-dashboard-users-access-list": { "public-dashboard-users-access-list": {
@ -11904,8 +11859,6 @@
"external-link": "External link", "external-link": "External link",
"loading-text": "Loading...", "loading-text": "Loading...",
"open-dashboard-list-text": "Open dashboards list", "open-dashboard-list-text": "Open dashboards list",
"public-dashboard-link": "Public dashboard URL",
"public-dashboard-setting": "Public dashboard settings",
"sharing-setting": "Sharing settings" "sharing-setting": "Sharing settings"
}, },
"delete-user-modal": { "delete-user-modal": {
@ -11913,14 +11866,12 @@
"delete-user-cancel-button": "Cancel", "delete-user-cancel-button": "Cancel",
"delete-user-revoke-access-button": "Revoke access", "delete-user-revoke-access-button": "Revoke access",
"revoke-access-title": "Revoke access", "revoke-access-title": "Revoke access",
"revoke-user-access-modal-desc-line1": "Are you sure you want to revoke access for {{email}}?", "revoke-user-access-modal-desc-line1": "Are you sure you want to revoke access for {{email}}?"
"revoke-user-access-modal-desc-line2": "This action will immediately revoke {{email}}&apos;s access to all public dashboards."
}, },
"delete-user-shared-dashboards-modal": { "delete-user-shared-dashboards-modal": {
"revoke-user-access-modal-desc-line2": "This action will immediately revoke {{email}}&apos;s access to all shared dashboards." "revoke-user-access-modal-desc-line2": "This action will immediately revoke {{email}}&apos;s access to all shared dashboards."
}, },
"modal": { "modal": {
"dashboard-modal-title": "Public dashboards",
"shared-dashboard-modal-title": "Shared dashboards" "shared-dashboard-modal-title": "Shared dashboards"
}, },
"table-body": { "table-body": {
@ -12422,8 +12373,6 @@
"export": "Export", "export": "Export",
"library-panel": "Library panel", "library-panel": "Library panel",
"link": "Link", "link": "Link",
"panel-embed": "Embed",
"public-dashboard": "Public Dashboard",
"public-dashboard-title": "Public dashboard", "public-dashboard-title": "Public dashboard",
"snapshot": "Snapshot" "snapshot": "Snapshot"
}, },
@ -14019,7 +13968,6 @@
}, },
"users-access-list": { "users-access-list": {
"tabs": { "tabs": {
"public-dashboard-users-tab-title": "Public dashboard users",
"shared-dashboard-users-tab-title": "Shared dashboard users" "shared-dashboard-users-tab-title": "Shared dashboard users"
} }
}, },