mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 16:15:42 -06:00
LibraryPanels: adds View panel in dashboard modal (#33517)
This commit is contained in:
parent
45c763a76b
commit
e8707944a3
@ -1,4 +1,4 @@
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, useState } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { GrafanaRouteComponentProps } from '../../core/navigation/types';
|
||||
@ -6,6 +6,8 @@ import { StoreState } from '../../types';
|
||||
import { getNavModel } from '../../core/selectors/navModel';
|
||||
import Page from '../../core/components/Page/Page';
|
||||
import { LibraryPanelsSearch } from './components/LibraryPanelsSearch/LibraryPanelsSearch';
|
||||
import { LibraryPanelDTO } from './types';
|
||||
import { OpenLibraryPanelModal } from './components/OpenLibraryPanelModal/OpenLibraryPanelModal';
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'library-panels'),
|
||||
@ -18,15 +20,16 @@ interface OwnProps extends GrafanaRouteComponentProps {}
|
||||
type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export const LibraryPanelsPage: FC<Props> = ({ navModel }) => {
|
||||
const [selected, setSelected] = useState<LibraryPanelDTO | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents>
|
||||
<LibraryPanelsSearch onClick={noop} showSecondaryActions showSort showFilter />
|
||||
<LibraryPanelsSearch onClick={setSelected} showSecondaryActions showSort showFilter />
|
||||
{selected ? <OpenLibraryPanelModal onDismiss={() => setSelected(undefined)} libraryPanel={selected} /> : null}
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
function noop() {}
|
||||
|
||||
export default connect(mapStateToProps)(LibraryPanelsPage);
|
||||
|
@ -1,17 +1,10 @@
|
||||
import { DispatchResult, LibraryPanelDTO } from '../../types';
|
||||
import { getLibraryPanelConnectedDashboards } from '../../state/api';
|
||||
import { getBackendSrv } from '../../../../core/services/backend_srv';
|
||||
import { getConnectedDashboards as apiGetConnectedDashboards } from '../../state/api';
|
||||
import { searchCompleted } from './reducer';
|
||||
|
||||
export function getConnectedDashboards(libraryPanel: LibraryPanelDTO): DispatchResult {
|
||||
return async function (dispatch) {
|
||||
const connectedDashboards = await getLibraryPanelConnectedDashboards(libraryPanel.uid);
|
||||
if (!connectedDashboards.length) {
|
||||
dispatch(searchCompleted({ dashboards: [] }));
|
||||
return;
|
||||
}
|
||||
|
||||
const dashboards = await getBackendSrv().search({ dashboardIds: connectedDashboards });
|
||||
const dashboards = await apiGetConnectedDashboards(libraryPanel.uid);
|
||||
dispatch(searchCompleted({ dashboards }));
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
import React, { MouseEvent, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { css } from '@emotion/css';
|
||||
import { AsyncSelect, Button, Modal, useStyles2 } from '@grafana/ui';
|
||||
import { GrafanaThemeV2, SelectableValue, urlUtil } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
|
||||
import { LibraryPanelDTO } from '../../types';
|
||||
import { DashboardSearchHit } from '../../../search/types';
|
||||
import { getConnectedDashboards, getLibraryPanelConnectedDashboards } from '../../state/api';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
export interface OpenLibraryPanelModalProps {
|
||||
onDismiss: () => void;
|
||||
libraryPanel: LibraryPanelDTO;
|
||||
}
|
||||
|
||||
export function OpenLibraryPanelModal({ libraryPanel, onDismiss }: OpenLibraryPanelModalProps): JSX.Element {
|
||||
const styles = useStyles2(getStyles);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [connected, setConnected] = useState(0);
|
||||
const [option, setOption] = useState<SelectableValue<DashboardSearchHit> | undefined>(undefined);
|
||||
useEffect(() => {
|
||||
const getConnected = async () => {
|
||||
const connectedDashboards = await getLibraryPanelConnectedDashboards(libraryPanel.uid);
|
||||
setConnected(connectedDashboards.length);
|
||||
};
|
||||
getConnected();
|
||||
}, [libraryPanel.uid]);
|
||||
const loadOptions = useCallback(
|
||||
(searchString: string) => loadOptionsAsync(libraryPanel.uid, searchString, setLoading),
|
||||
[libraryPanel.uid]
|
||||
);
|
||||
const debouncedLoadOptions = useMemo(() => debounce(loadOptions, 300, { leading: true, trailing: true }), [
|
||||
loadOptions,
|
||||
]);
|
||||
const onViewPanel = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
locationService.push(urlUtil.renderUrl(`/d/${option?.value?.uid}`, {}));
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal title="View panel in dashboard" onDismiss={onDismiss} onClickBackdrop={onDismiss} isOpen>
|
||||
<div className={styles.container}>
|
||||
{connected === 0 ? (
|
||||
<span>Panel is not linked to a dashboard. Add the panel to a dashboard and retry.</span>
|
||||
) : null}
|
||||
{connected > 0 ? (
|
||||
<>
|
||||
<p>
|
||||
This panel is being used in <strong>{connected} dashboards</strong>.Please choose which dashboard to view
|
||||
the panel in:
|
||||
</p>
|
||||
<AsyncSelect
|
||||
isClearable
|
||||
isLoading={loading}
|
||||
defaultOptions={true}
|
||||
loadOptions={debouncedLoadOptions}
|
||||
onChange={setOption}
|
||||
placeholder="Start typing to search for dashboard"
|
||||
noOptionsMessage="No dashboards found"
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<Modal.ButtonRow>
|
||||
<Button variant="secondary" onClick={onDismiss} fill="outline">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onViewPanel} disabled={!Boolean(option)}>
|
||||
{option ? `View panel in ${option?.label}...` : 'View panel in dashboard...'}
|
||||
</Button>
|
||||
</Modal.ButtonRow>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
async function loadOptionsAsync(uid: string, searchString: string, setLoading: (loading: boolean) => void) {
|
||||
setLoading(true);
|
||||
const searchHits = await getConnectedDashboards(uid);
|
||||
const options = searchHits
|
||||
.filter((d) => d.title.toLowerCase().includes(searchString.toLowerCase()))
|
||||
.map((d) => ({ label: d.title, value: d }));
|
||||
setLoading(false);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
function getStyles(theme: GrafanaThemeV2) {
|
||||
return {
|
||||
container: css``,
|
||||
};
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { Button, Icon, Input, Modal, useStyles } from '@grafana/ui';
|
||||
import { useAsync, useDebounce } from 'react-use';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
import { usePanelSave } from '../../utils/usePanelSave';
|
||||
import { getLibraryPanelConnectedDashboards } from '../../state/api';
|
||||
import { getConnectedDashboards } from '../../state/api';
|
||||
import { PanelModelWithLibraryPanel } from '../../types';
|
||||
import { getModalStyles } from '../../styles';
|
||||
|
||||
@ -25,20 +24,14 @@ export const SaveLibraryPanelModal: React.FC<Props> = ({
|
||||
onDiscard,
|
||||
}) => {
|
||||
const [searchString, setSearchString] = useState('');
|
||||
const connectedDashboardsState = useAsync(async () => {
|
||||
const connectedDashboards = await getLibraryPanelConnectedDashboards(panel.libraryPanel.uid);
|
||||
return connectedDashboards;
|
||||
}, []);
|
||||
|
||||
const dashState = useAsync(async () => {
|
||||
const connectedDashboards = connectedDashboardsState.value;
|
||||
if (connectedDashboards && connectedDashboards.length > 0) {
|
||||
const dashboardDTOs = await getBackendSrv().search({ dashboardIds: connectedDashboards });
|
||||
return dashboardDTOs.map((dash) => dash.title);
|
||||
const searchHits = await getConnectedDashboards(panel.libraryPanel.uid);
|
||||
if (searchHits.length > 0) {
|
||||
return searchHits.map((dash) => dash.title);
|
||||
}
|
||||
|
||||
return [];
|
||||
}, [connectedDashboardsState.value]);
|
||||
}, [panel.libraryPanel.uid]);
|
||||
|
||||
const [filteredDashboards, setFilteredDashboards] = useState<string[]>([]);
|
||||
useDebounce(
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
import { LibraryPanelDTO, LibraryPanelSearchResult, PanelModelWithLibraryPanel } from '../types';
|
||||
import { DashboardSearchHit } from '../../search/types';
|
||||
import { getBackendSrv } from '../../../core/services/backend_srv';
|
||||
|
||||
export interface GetLibraryPanelsOptions {
|
||||
searchString?: string;
|
||||
@ -68,3 +69,13 @@ export async function getLibraryPanelConnectedDashboards(libraryPanelUid: string
|
||||
const { result } = await getBackendSrv().get(`/api/library-panels/${libraryPanelUid}/dashboards`);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getConnectedDashboards(uid: string): Promise<DashboardSearchHit[]> {
|
||||
const dashboardIds = await getLibraryPanelConnectedDashboards(uid);
|
||||
if (dashboardIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const searchHits = await getBackendSrv().search({ dashboardIds });
|
||||
return searchHits;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user