Commit fcaff011 by Alexander Zobnin Committed by GitHub

ShareModal: able to extend tabs (#22537)

* ShareModal: use generic tab type

* ShareModal: able to extend with custom tabs

* ShareModal: able to extend dash/panel tabs separately

* grafana-ui: ModalTabContent component
parent 9731ea9b
import React from 'react';
import { IconType } from '../Icon/types';
import { Icon } from '../Icon/Icon';
interface Props {
icon?: IconType;
iconClass?: string;
}
export const ModalTabContent: React.FC<Props> = ({ icon, iconClass, children }) => {
let iconElem;
const showIcon = icon || iconClass;
if (iconClass) {
iconElem = <i className={iconClass}></i>;
}
if (icon) {
iconElem = <Icon name={icon} />;
}
return (
<div className="share-modal-body">
<div className="share-modal-header">
{showIcon && <div className="share-modal-big-icon">{iconElem}</div>}
<div className="share-modal-content">{children}</div>
</div>
</div>
);
};
...@@ -48,6 +48,7 @@ export { QueryField } from './QueryField/QueryField'; ...@@ -48,6 +48,7 @@ export { QueryField } from './QueryField/QueryField';
export { Modal } from './Modal/Modal'; export { Modal } from './Modal/Modal';
export { ModalHeader } from './Modal/ModalHeader'; export { ModalHeader } from './Modal/ModalHeader';
export { ModalTabsHeader } from './Modal/ModalTabsHeader'; export { ModalTabsHeader } from './Modal/ModalTabsHeader';
export { ModalTabContent } from './Modal/ModalTabContent';
export { ModalsProvider, ModalRoot, ModalsController } from './Modal/ModalsContext'; export { ModalsProvider, ModalRoot, ModalsController } from './Modal/ModalsContext';
// Renderless // Renderless
......
...@@ -15,7 +15,7 @@ import { ILocationService, IRootScopeService, ITimeoutService } from 'angular'; ...@@ -15,7 +15,7 @@ import { ILocationService, IRootScopeService, ITimeoutService } from 'angular';
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl'; import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
import { getLocationSrv } from '@grafana/runtime'; import { getLocationSrv } from '@grafana/runtime';
import { DashboardModel } from '../../features/dashboard/state'; import { DashboardModel } from '../../features/dashboard/state';
import { ShareModal } from 'app/features/dashboard/components/ShareModal/ShareModal'; import { ShareModal } from 'app/features/dashboard/components/ShareModal';
import { SaveDashboardModalProxy } from '../../features/dashboard/components/SaveDashboard/SaveDashboardModalProxy'; import { SaveDashboardModalProxy } from '../../features/dashboard/components/SaveDashboard/SaveDashboardModalProxy';
export class KeybindingSrv { export class KeybindingSrv {
......
...@@ -15,7 +15,7 @@ import { updateLocation } from 'app/core/actions'; ...@@ -15,7 +15,7 @@ import { updateLocation } from 'app/core/actions';
// Types // Types
import { DashboardModel } from '../../state'; import { DashboardModel } from '../../state';
import { CoreEvents, StoreState } from 'app/types'; import { CoreEvents, StoreState } from 'app/types';
import { ShareModal } from '../ShareModal/ShareModal'; import { ShareModal } from 'app/features/dashboard/components/ShareModal';
import { SaveDashboardModalProxy } from 'app/features/dashboard/components/SaveDashboard/SaveDashboardModalProxy'; import { SaveDashboardModalProxy } from 'app/features/dashboard/components/SaveDashboard/SaveDashboardModalProxy';
export interface OwnProps { export interface OwnProps {
......
...@@ -13,7 +13,7 @@ const themeOptions: Array<SelectableValue<string>> = [ ...@@ -13,7 +13,7 @@ const themeOptions: Array<SelectableValue<string>> = [
]; ];
export interface Props { export interface Props {
dashboard?: DashboardModel; dashboard: DashboardModel;
panel?: PanelModel; panel?: PanelModel;
} }
......
import React, { PureComponent } from 'react'; import React from 'react';
import { Modal, ModalTabsHeader, TabContent } from '@grafana/ui'; import { Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
import { ShareLink } from './ShareLink'; import { ShareLink } from './ShareLink';
import { ShareSnapshot } from './ShareSnapshot'; import { ShareSnapshot } from './ShareSnapshot';
import { ShareExport } from './ShareExport'; import { ShareExport } from './ShareExport';
import { ShareEmbed } from './ShareEmbed'; import { ShareEmbed } from './ShareEmbed';
import { ShareModalTabModel } from './types';
const shareModalTabs = [ const shareCommonTabs: ShareModalTabModel[] = [
{ label: 'Link', value: 'link' }, { label: 'Link', value: 'link', component: ShareLink },
{ label: 'Embed', value: 'embed' }, { label: 'Snapshot', value: 'snapshot', component: ShareSnapshot },
{ label: 'Snapshot', value: 'snapshot' },
{ label: 'Export', value: 'export' },
]; ];
// prettier-ignore
const shareDashboardTabs: ShareModalTabModel[] = [
{ label: 'Export', value: 'export', component: ShareExport },
];
// prettier-ignore
const sharePanelTabs: ShareModalTabModel[] = [
{ label: 'Embed', value: 'embed', component: ShareEmbed },
];
const customDashboardTabs: ShareModalTabModel[] = [];
const customPanelTabs: ShareModalTabModel[] = [];
export function addDashboardShareTab(tab: ShareModalTabModel) {
customDashboardTabs.push(tab);
}
export function addPanelShareTab(tab: ShareModalTabModel) {
customPanelTabs.push(tab);
}
function getInitialState(props: Props): State {
const tabs = getTabs(props);
return {
tabs,
activeTab: tabs[0].value,
};
}
function getTabs(props: Props) {
const { panel } = props;
const tabs = [...shareCommonTabs];
if (panel) {
tabs.push(...sharePanelTabs);
tabs.push(...customPanelTabs);
} else {
tabs.push(...shareDashboardTabs);
tabs.push(...customDashboardTabs);
}
return tabs;
}
interface Props { interface Props {
dashboard: DashboardModel; dashboard: DashboardModel;
panel?: PanelModel; panel?: PanelModel;
...@@ -21,64 +64,60 @@ interface Props { ...@@ -21,64 +64,60 @@ interface Props {
} }
interface State { interface State {
tab: string; tabs: ShareModalTabModel[];
activeTab: string;
} }
function getInitialState(): State { export class ShareModal extends React.Component<Props, State> {
return {
tab: shareModalTabs[0].value,
};
}
export class ShareModal extends PureComponent<Props, State> {
constructor(props: Props) { constructor(props: Props) {
super(props); super(props);
this.state = getInitialState(); this.state = getInitialState(props);
} }
onDismiss = () => { onDismiss = () => {
this.setState(getInitialState()); this.setState(getInitialState(this.props));
this.props.onDismiss(); this.props.onDismiss();
}; };
onSelectTab = (t: any) => { onSelectTab = (t: any) => {
this.setState({ tab: t.value }); this.setState({ activeTab: t.value });
}; };
getTabs() { getTabs() {
const { panel } = this.props; return getTabs(this.props);
}
// Filter tabs for dashboard/panel share modal getActiveTab() {
return shareModalTabs.filter(t => { const { tabs, activeTab } = this.state;
if (panel) { return tabs.find(t => t.value === activeTab);
return t.value !== 'export';
}
return t.value !== 'embed';
});
} }
renderTitle() { renderTitle() {
const { panel } = this.props; const { panel } = this.props;
const { tab } = this.state; const { activeTab } = this.state;
const title = panel ? 'Share Panel' : 'Share'; const title = panel ? 'Share Panel' : 'Share';
const tabs = this.getTabs(); const tabs = this.getTabs();
return ( return (
<ModalTabsHeader title={title} icon="share-square-o" tabs={tabs} activeTab={tab} onChangeTab={this.onSelectTab} /> <ModalTabsHeader
title={title}
icon="share-square-o"
tabs={tabs}
activeTab={activeTab}
onChangeTab={this.onSelectTab}
/>
); );
} }
render() { render() {
const { dashboard, panel } = this.props; const { dashboard, panel } = this.props;
const { tab } = this.state; const activeTabModel = this.getActiveTab();
const ActiveTab = activeTabModel?.component;
return ( return (
<Modal isOpen={true} title={this.renderTitle()} onDismiss={this.onDismiss}> <Modal isOpen={true} title={this.renderTitle()} onDismiss={this.onDismiss}>
<TabContent> <TabContent>
{tab === 'link' && <ShareLink dashboard={dashboard} panel={panel} />} <ActiveTab dashboard={dashboard} panel={panel} onDismiss={this.onDismiss} />
{tab === 'embed' && panel && <ShareEmbed dashboard={dashboard} panel={panel} />}
{tab === 'snapshot' && <ShareSnapshot dashboard={dashboard} panel={panel} onDismiss={this.onDismiss} />}
{tab === 'export' && !panel && <ShareExport dashboard={dashboard} panel={panel} onDismiss={this.onDismiss} />}
</TabContent> </TabContent>
</Modal> </Modal>
); );
......
export { ShareModal, addDashboardShareTab, addPanelShareTab } from './ShareModal';
export * from './types';
import React from 'react';
import { PanelModel } from '@grafana/data';
import { DashboardModel } from 'app/features/dashboard/state';
export interface ShareModalTabProps {
dashboard: DashboardModel;
panel?: PanelModel;
onDismiss?(): void;
}
export type ShareModalTab = React.ComponentType<ShareModalTabProps>;
export interface ShareModalTabModel {
label: string;
value: string;
component: ShareModalTab;
}
...@@ -20,7 +20,7 @@ import templateSrv from 'app/features/templating/template_srv'; ...@@ -20,7 +20,7 @@ import templateSrv from 'app/features/templating/template_srv';
import { LS_PANEL_COPY_KEY, PANEL_BORDER } from 'app/core/constants'; import { LS_PANEL_COPY_KEY, PANEL_BORDER } from 'app/core/constants';
import { CoreEvents } from 'app/types'; import { CoreEvents } from 'app/types';
import { ShareModal } from 'app/features/dashboard/components/ShareModal/ShareModal'; import { ShareModal } from 'app/features/dashboard/components/ShareModal';
export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => { export const removePanel = (dashboard: DashboardModel, panel: PanelModel, ask: boolean) => {
// confirm deletion // confirm deletion
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment