mirror of
https://github.com/getredash/redash.git
synced 2025-12-19 17:37:19 -05:00
219 lines
7.5 KiB
JavaScript
219 lines
7.5 KiB
JavaScript
import { startsWith, get } from "lodash";
|
|
import React from "react";
|
|
import PropTypes from "prop-types";
|
|
import cx from "classnames";
|
|
import Tooltip from "antd/lib/tooltip";
|
|
import Drawer from "antd/lib/drawer";
|
|
import CloseOutlinedIcon from "@ant-design/icons/CloseOutlined";
|
|
import BigMessage from "@/components/BigMessage";
|
|
import DynamicComponent from "@/components/DynamicComponent";
|
|
|
|
import "./HelpTrigger.less";
|
|
|
|
const DOMAIN = "https://redash.io";
|
|
const HELP_PATH = "/help";
|
|
const IFRAME_TIMEOUT = 20000;
|
|
const IFRAME_URL_UPDATE_MESSAGE = "iframe_url";
|
|
|
|
export const TYPES = {
|
|
HOME: ["", "Help"],
|
|
VALUE_SOURCE_OPTIONS: ["/user-guide/querying/query-parameters#Value-Source-Options", "Guide: Value Source Options"],
|
|
SHARE_DASHBOARD: ["/user-guide/dashboards/sharing-dashboards", "Guide: Sharing and Embedding Dashboards"],
|
|
AUTHENTICATION_OPTIONS: ["/user-guide/users/authentication-options", "Guide: Authentication Options"],
|
|
USAGE_DATA_SHARING: ["/open-source/admin-guide/usage-data", "Help: Anonymous Usage Data Sharing"],
|
|
DS_ATHENA: ["/data-sources/amazon-athena-setup", "Guide: Help Setting up Amazon Athena"],
|
|
DS_BIGQUERY: ["/data-sources/bigquery-setup", "Guide: Help Setting up BigQuery"],
|
|
DS_URL: ["/data-sources/querying-urls", "Guide: Help Setting up URL"],
|
|
DS_MONGODB: ["/data-sources/mongodb-setup", "Guide: Help Setting up MongoDB"],
|
|
DS_GOOGLE_SPREADSHEETS: ["/data-sources/querying-a-google-spreadsheet", "Guide: Help Setting up Google Spreadsheets"],
|
|
DS_GOOGLE_ANALYTICS: ["/data-sources/google-analytics-setup", "Guide: Help Setting up Google Analytics"],
|
|
DS_AXIBASETSD: ["/data-sources/axibase-time-series-database", "Guide: Help Setting up Axibase Time Series"],
|
|
DS_RESULTS: ["/user-guide/querying/query-results-data-source", "Guide: Help Setting up Query Results"],
|
|
ALERT_SETUP: ["/user-guide/alerts/setting-up-an-alert", "Guide: Setting Up a New Alert"],
|
|
MAIL_CONFIG: ["/open-source/setup/#Mail-Configuration", "Guide: Mail Configuration"],
|
|
ALERT_NOTIF_TEMPLATE_GUIDE: ["/user-guide/alerts/custom-alert-notifications", "Guide: Custom Alerts Notifications"],
|
|
FAVORITES: ["/user-guide/querying/favorites-tagging/#Favorites", "Guide: Favorites"],
|
|
MANAGE_PERMISSIONS: [
|
|
"/user-guide/querying/writing-queries#Managing-Query-Permissions",
|
|
"Guide: Managing Query Permissions",
|
|
],
|
|
NUMBER_FORMAT_SPECS: ["/user-guide/visualizations/formatting-numbers", "Formatting Numbers"],
|
|
};
|
|
|
|
export default class HelpTrigger extends React.Component {
|
|
static propTypes = {
|
|
type: PropTypes.oneOf(Object.keys(TYPES)),
|
|
href: PropTypes.string,
|
|
title: PropTypes.node,
|
|
className: PropTypes.string,
|
|
showTooltip: PropTypes.bool,
|
|
children: PropTypes.node,
|
|
};
|
|
|
|
static defaultProps = {
|
|
type: null,
|
|
href: null,
|
|
title: null,
|
|
className: null,
|
|
showTooltip: true,
|
|
children: <i className="fa fa-question-circle" />,
|
|
};
|
|
|
|
iframeRef = React.createRef();
|
|
|
|
iframeLoadingTimeout = null;
|
|
|
|
state = {
|
|
visible: false,
|
|
loading: false,
|
|
error: false,
|
|
currentUrl: null,
|
|
};
|
|
|
|
componentDidMount() {
|
|
window.addEventListener("message", this.onPostMessageReceived, false);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
window.removeEventListener("message", this.onPostMessageReceived);
|
|
clearTimeout(this.iframeLoadingTimeout);
|
|
}
|
|
|
|
loadIframe = url => {
|
|
clearTimeout(this.iframeLoadingTimeout);
|
|
this.setState({ loading: true, error: false });
|
|
|
|
this.iframeRef.current.src = url;
|
|
this.iframeLoadingTimeout = setTimeout(() => {
|
|
this.setState({ error: url, loading: false });
|
|
}, IFRAME_TIMEOUT); // safety
|
|
};
|
|
|
|
onIframeLoaded = () => {
|
|
this.setState({ loading: false });
|
|
clearTimeout(this.iframeLoadingTimeout);
|
|
};
|
|
|
|
onPostMessageReceived = event => {
|
|
if (!startsWith(event.origin, DOMAIN)) {
|
|
return;
|
|
}
|
|
|
|
const { type, message: currentUrl } = event.data || {};
|
|
if (type !== IFRAME_URL_UPDATE_MESSAGE) {
|
|
return;
|
|
}
|
|
|
|
this.setState({ currentUrl });
|
|
};
|
|
|
|
getUrl = () => {
|
|
const helpTriggerType = get(TYPES, this.props.type);
|
|
return helpTriggerType ? DOMAIN + HELP_PATH + helpTriggerType[0] : this.props.href;
|
|
};
|
|
|
|
openDrawer = () => {
|
|
this.setState({ visible: true });
|
|
// wait for drawer animation to complete so there's no animation jank
|
|
setTimeout(() => this.loadIframe(this.getUrl()), 300);
|
|
};
|
|
|
|
closeDrawer = event => {
|
|
if (event) {
|
|
event.preventDefault();
|
|
}
|
|
this.setState({ visible: false });
|
|
this.setState({ visible: false, currentUrl: null });
|
|
};
|
|
|
|
render() {
|
|
const tooltip = get(TYPES, `${this.props.type}[1]`, this.props.title);
|
|
const className = cx("help-trigger", this.props.className);
|
|
const url = this.state.currentUrl;
|
|
|
|
const isAllowedDomain = startsWith(url || this.getUrl(), DOMAIN);
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<Tooltip
|
|
title={
|
|
this.props.showTooltip ? (
|
|
<>
|
|
{tooltip}
|
|
{!isAllowedDomain && <i className="fa fa-external-link" style={{ marginLeft: 5 }} />}
|
|
</>
|
|
) : null
|
|
}>
|
|
{isAllowedDomain ? (
|
|
<a onClick={this.openDrawer} className={className}>
|
|
{this.props.children}
|
|
</a>
|
|
) : (
|
|
<a href={url || this.getUrl()} className={className} rel="noopener noreferrer" target="_blank">
|
|
{this.props.children}
|
|
</a>
|
|
)}
|
|
</Tooltip>
|
|
<Drawer
|
|
placement="right"
|
|
closable={false}
|
|
onClose={this.closeDrawer}
|
|
visible={this.state.visible}
|
|
className="help-drawer"
|
|
destroyOnClose
|
|
width={400}>
|
|
<div className="drawer-wrapper">
|
|
<div className="drawer-menu">
|
|
{url && (
|
|
<Tooltip title="Open page in a new window" placement="left">
|
|
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
|
<a href={url} target="_blank">
|
|
<i className="fa fa-external-link" />
|
|
</a>
|
|
</Tooltip>
|
|
)}
|
|
<Tooltip title="Close" placement="bottom">
|
|
<a onClick={this.closeDrawer}>
|
|
<CloseOutlinedIcon />
|
|
</a>
|
|
</Tooltip>
|
|
</div>
|
|
|
|
{/* iframe */}
|
|
{!this.state.error && (
|
|
<iframe
|
|
ref={this.iframeRef}
|
|
title="Redash Help"
|
|
src="about:blank"
|
|
className={cx({ ready: !this.state.loading })}
|
|
onLoad={this.onIframeLoaded}
|
|
/>
|
|
)}
|
|
|
|
{/* loading indicator */}
|
|
{this.state.loading && (
|
|
<BigMessage icon="fa-spinner fa-2x fa-pulse" message="Loading..." className="help-message" />
|
|
)}
|
|
|
|
{/* error message */}
|
|
{this.state.error && (
|
|
<BigMessage icon="fa-exclamation-circle" className="help-message">
|
|
Something went wrong.
|
|
<br />
|
|
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
|
<a href={this.state.error} target="_blank" rel="noopener">
|
|
Click here
|
|
</a>{" "}
|
|
to open the page in a new window.
|
|
</BigMessage>
|
|
)}
|
|
</div>
|
|
|
|
{/* extra content */}
|
|
<DynamicComponent name="HelpDrawerExtraContent" onLeave={this.closeDrawer} openPageUrl={this.loadIframe} />
|
|
</Drawer>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
}
|