forked from grafana/grafana
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Scenes/ShareModal: Implement public dashboard tab (grafana#76837)
Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
- Loading branch information
1 parent
c506da5
commit d018095
Showing
19 changed files
with
520 additions
and
124 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
public/app/features/dashboard-scene/sharing/public-dashboards/ConfigPublicDashboard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { css } from '@emotion/css'; | ||
import React from 'react'; | ||
|
||
import { GrafanaTheme2 } from '@grafana/data'; | ||
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'; | ||
|
||
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 { dashboardRef } = model.useState(); | ||
const dashboard = dashboardRef.resolve(); | ||
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: 'Revoke public URL', | ||
icon: 'trash-alt', | ||
confirmText: 'Revoke public URL', | ||
body: ( | ||
<p className={styles.description}> | ||
Are you sure you want to revoke this URL? The dashboard will no longer be public. | ||
</p> | ||
), | ||
onDismiss: () => { | ||
dashboard.showModal(new ShareModal({ dashboardRef, activeTab: 'Public Dashboard' })); | ||
}, | ||
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, | ||
}), | ||
}); |
30 changes: 30 additions & 0 deletions
30
public/app/features/dashboard-scene/sharing/public-dashboards/ConfirmModal.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import React from 'react'; | ||
|
||
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; | ||
import { ConfirmModal as ConfirmModalComponent, ConfirmModalProps } from '@grafana/ui'; | ||
|
||
import { ModalSceneObjectLike } from '../types'; | ||
|
||
interface ConfirmModalState extends ConfirmModalProps, SceneObjectState {} | ||
|
||
export class ConfirmModal extends SceneObjectBase<ConfirmModalState> implements ModalSceneObjectLike { | ||
static Component = ConfirmModalRenderer; | ||
|
||
constructor(state: ConfirmModalState) { | ||
super({ | ||
confirmVariant: 'destructive', | ||
dismissText: 'Cancel', | ||
dismissVariant: 'secondary', | ||
icon: 'exclamation-triangle', | ||
confirmButtonVariant: 'destructive', | ||
...state, | ||
}); | ||
} | ||
|
||
onDismiss() {} | ||
} | ||
|
||
function ConfirmModalRenderer({ model }: SceneComponentProps<ConfirmModal>) { | ||
const props = model.useState(); | ||
return <ConfirmModalComponent {...props} />; | ||
} |
22 changes: 22 additions & 0 deletions
22
public/app/features/dashboard-scene/sharing/public-dashboards/CreatePublicDashboard.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import React from 'react'; | ||
|
||
import { SceneComponentProps } from '@grafana/scenes'; | ||
import { CreatePublicDashboardBase } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/CreatePublicDashboard/CreatePublicDashboard'; | ||
|
||
import { SharePublicDashboardTab } from './SharePublicDashboardTab'; | ||
import { useUnsupportedDatasources } from './hooks'; | ||
|
||
export function CreatePublicDashboard({ model }: SceneComponentProps<SharePublicDashboardTab>) { | ||
const { dashboardRef } = model.useState(); | ||
const dashboard = dashboardRef.resolve(); | ||
const unsupportedDataSources = useUnsupportedDatasources(dashboard); | ||
const hasTemplateVariables = (dashboard.state.$variables?.state.variables.length ?? 0) > 0; | ||
|
||
return ( | ||
<CreatePublicDashboardBase | ||
dashboard={dashboard} | ||
unsupportedDatasources={unsupportedDataSources} | ||
unsupportedTemplateVariables={hasTemplateVariables} | ||
/> | ||
); | ||
} |
43 changes: 43 additions & 0 deletions
43
public/app/features/dashboard-scene/sharing/public-dashboards/SharePublicDashboardTab.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import React from 'react'; | ||
|
||
import { SceneComponentProps, SceneObjectBase, SceneObjectRef } from '@grafana/scenes'; | ||
import { t } from 'app/core/internationalization'; | ||
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 { DashboardScene } from '../../scene/DashboardScene'; | ||
import { SceneShareTabState } from '../types'; | ||
|
||
import { ConfigPublicDashboard } from './ConfigPublicDashboard'; | ||
import { CreatePublicDashboard } from './CreatePublicDashboard'; | ||
|
||
export interface SharePublicDashboardTabState extends SceneShareTabState { | ||
dashboardRef: SceneObjectRef<DashboardScene>; | ||
} | ||
|
||
export class SharePublicDashboardTab extends SceneObjectBase<SharePublicDashboardTabState> { | ||
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( | ||
model.state.dashboardRef.resolve().state.uid! | ||
); | ||
|
||
return ( | ||
<> | ||
{isGetLoading ? ( | ||
<Loader /> | ||
) : !publicDashboardPersisted(publicDashboard) ? ( | ||
<CreatePublicDashboard model={model} /> | ||
) : ( | ||
<ConfigPublicDashboard model={model} publicDashboard={publicDashboard} isGetLoading={isGetLoading} /> | ||
)} | ||
</> | ||
); | ||
} |
14 changes: 14 additions & 0 deletions
14
public/app/features/dashboard-scene/sharing/public-dashboards/hooks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { useAsync } from 'react-use'; | ||
|
||
import { DashboardScene } from '../../scene/DashboardScene'; | ||
|
||
import { getPanelDatasourceTypes, getUnsupportedDashboardDatasources } from './utils'; | ||
|
||
export function useUnsupportedDatasources(dashboard: DashboardScene) { | ||
const { value: unsupportedDataSources } = useAsync(async () => { | ||
const types = getPanelDatasourceTypes(dashboard); | ||
return getUnsupportedDashboardDatasources(types); | ||
}, []); | ||
|
||
return unsupportedDataSources; | ||
} |
102 changes: 102 additions & 0 deletions
102
public/app/features/dashboard-scene/sharing/public-dashboards/utils.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
import { DataSourceWithBackend } from '@grafana/runtime'; | ||
import { | ||
SceneGridItemLike, | ||
VizPanel, | ||
SceneGridItem, | ||
SceneQueryRunner, | ||
SceneDataTransformer, | ||
SceneGridLayout, | ||
SceneGridRow, | ||
} from '@grafana/scenes'; | ||
import { supportedDatasources } from 'app/features/dashboard/components/ShareModal/SharePublicDashboard/SupportedPubdashDatasources'; | ||
import { getDatasourceSrv } from 'app/features/plugins/datasource_srv'; | ||
|
||
import { DashboardScene } from '../../scene/DashboardScene'; | ||
import { LibraryVizPanel } from '../../scene/LibraryVizPanel'; | ||
import { PanelRepeaterGridItem } from '../../scene/PanelRepeaterGridItem'; | ||
|
||
export const getUnsupportedDashboardDatasources = async (types: string[]): Promise<string[]> => { | ||
let unsupportedDS = new Set<string>(); | ||
|
||
for (const type of types) { | ||
if (!supportedDatasources.has(type)) { | ||
unsupportedDS.add(type); | ||
} else { | ||
const ds = await getDatasourceSrv().get(type); | ||
if (!(ds instanceof DataSourceWithBackend)) { | ||
unsupportedDS.add(type); | ||
} | ||
} | ||
} | ||
|
||
return Array.from(unsupportedDS); | ||
}; | ||
|
||
export function getPanelDatasourceTypes(scene: DashboardScene): string[] { | ||
const types = new Set<string>(); | ||
|
||
const body = scene.state.body; | ||
if (!(body instanceof SceneGridLayout)) { | ||
return []; | ||
} | ||
|
||
for (const child of body.state.children) { | ||
if (child instanceof SceneGridItem) { | ||
const ts = panelDatasourceTypes(child); | ||
for (const t of ts) { | ||
types.add(t); | ||
} | ||
} | ||
|
||
if (child instanceof SceneGridRow) { | ||
const ts = rowTypes(child); | ||
for (const t of ts) { | ||
types.add(t); | ||
} | ||
} | ||
} | ||
|
||
return Array.from(types).sort(); | ||
} | ||
|
||
function rowTypes(gridRow: SceneGridRow) { | ||
const types = new Set(gridRow.state.children.map((c) => panelDatasourceTypes(c)).flat()); | ||
return types; | ||
} | ||
|
||
function panelDatasourceTypes(gridItem: SceneGridItemLike) { | ||
let vizPanel: VizPanel | undefined; | ||
if (gridItem instanceof SceneGridItem) { | ||
if (gridItem.state.body instanceof LibraryVizPanel) { | ||
vizPanel = gridItem.state.body.state.panel; | ||
} else if (gridItem.state.body instanceof VizPanel) { | ||
vizPanel = gridItem.state.body; | ||
} else { | ||
throw new Error('SceneGridItem body expected to be VizPanel'); | ||
} | ||
} else if (gridItem instanceof PanelRepeaterGridItem) { | ||
vizPanel = gridItem.state.source; | ||
} | ||
|
||
if (!vizPanel) { | ||
throw new Error('Unsupported grid item type'); | ||
} | ||
const dataProvider = vizPanel.state.$data; | ||
const types = new Set<string>(); | ||
if (dataProvider instanceof SceneQueryRunner) { | ||
for (const q of dataProvider.state.queries) { | ||
types.add(q.datasource?.type ?? ''); | ||
} | ||
} | ||
|
||
if (dataProvider instanceof SceneDataTransformer) { | ||
const panelData = dataProvider.state.$data; | ||
if (panelData instanceof SceneQueryRunner) { | ||
for (const q of panelData.state.queries) { | ||
types.add(q.datasource?.type ?? ''); | ||
} | ||
} | ||
} | ||
|
||
return Array.from(types); | ||
} |
Empty file.
Oops, something went wrong.