Skip to content
Snippets Groups Projects
Commit a426d238 authored by Tom Robinson's avatar Tom Robinson
Browse files

Consolidate visualization logic + show title when dash cards error + new error treatment

parent a2161bf9
No related branches found
No related tags found
No related merge requests found
import React, { Component, PropTypes } from "react";
export default ComposedComponent => class extends Component {
static displayName = "AbsoluteContainer["+(ComposedComponent.displayName || ComposedComponent.name)+"]";
render() {
const { className, style, width, height } = this.props;
return (
<div className={className} style={{ position: "relative", ...style }}>
<div style={{ position: "absolute", top: 0, left: 0, width: width, height: height }}>
{ width != null && height != null &&
<ComposedComponent
width={width}
height={height}
{...this.props}
/>
}
</div>
</div>
);
}
}
......@@ -4,7 +4,6 @@ import ReactDOM from "react-dom";
import visualizations from "metabase/visualizations";
import Visualization from "metabase/visualizations/components/Visualization.jsx";
import LoadingSpinner from "metabase/components/LoadingSpinner.jsx";
import Icon from "metabase/components/Icon.jsx";
......@@ -73,8 +72,9 @@ export default class DashCard extends Component {
}
}
renderCard(CardVisualization) {
render() {
const { dashcard, cardData, isEditing, onAddSeries, onRemove } = this.props;
const cards = [dashcard.card].concat(dashcard.series || []);
const series = cards
.map(card => ({
......@@ -85,28 +85,25 @@ export default class DashCard extends Component {
const errors = series.map(s => s.error).filter(e => e);
const error = errors[0] || this.state.error;
let errorMessage;
if (error) {
let message;
if (error.data) {
message = error.data.message;
errorMessage = error.data.message;
} else if (error.status === 503) {
message = "I'm sorry, the server timed out while asking your question."
errorMessage = "I'm sorry, the server timed out while asking your question."
} else if (typeof error === "string") {
message = error;
errorMessage = error;
} else {
message = "Oh snap! Something went wrong loading this card :sad:";
errorMessage = "Oh snap! Something went wrong loading this card :sad:";
}
return (
<div className="p1 text-centered flex-full flex flex-column layout-centered">
<h2 className="text-normal text-grey-2">{message}</h2>
</div>
);
}
if (series.length > 0 && _.every(series, (s) => s.data)) {
return (
const CardVisualization = visualizations.get(series[0].card.display);
return (
<div className={"Card bordered rounded flex flex-column " + cx({ "Card--recent": dashcard.isAdded })}>
<Visualization
className="flex-full"
error={errorMessage}
series={series}
isDashboard={true}
isEditing={isEditing}
......@@ -114,24 +111,6 @@ export default class DashCard extends Component {
actionButtons={isEditing ? <DashCardActionButtons series={series} visualization={CardVisualization} onRemove={onRemove} onAddSeries={onAddSeries} /> : undefined}
onUpdateVisualizationSetting={this.props.onUpdateVisualizationSetting}
/>
);
}
return (
<div className="p1 text-brand text-centered flex-full flex flex-column layout-centered">
<LoadingSpinner />
<h1 className="ml1 text-normal text-grey-2">Loading...</h1>
</div>
);
}
render() {
const { dashcard } = this.props;
const series = [dashcard.card].concat(dashcard.series || []).map(card => ({ card }));
const Viz = visualizations.get(series[0].card.display);
return (
<div className={"Card bordered rounded flex flex-column " + cx({ "Card--recent": dashcard.isAdded })}>
{this.renderCard(Viz)}
</div>
);
}
......
......@@ -2,7 +2,6 @@ import React, { Component, PropTypes } from "react";
import ReactDOM from "react-dom";
import ExplicitSize from "metabase/components/ExplicitSize.jsx";
import AbsoluteContainer from "metabase/components/AbsoluteContainer.jsx";
import * as charting from "metabase/visualizations/lib/CardRenderer";
......@@ -10,9 +9,9 @@ import { isSameSeries } from "metabase/visualizations/lib/utils";
import { getSettingsForVisualization } from "metabase/lib/visualization_settings";
import dc from "dc";
import cx from "classnames";
@ExplicitSize
@AbsoluteContainer
export default class CardRenderer extends Component {
static propTypes = {
chartType: PropTypes.string.isRequired,
......@@ -27,8 +26,7 @@ export default class CardRenderer extends Component {
}
componentDidMount() {
// avoid race condition with initial layout
setTimeout(() => this.renderChart());
this.renderChart();
}
componentDidUpdate() {
......@@ -55,7 +53,11 @@ export default class CardRenderer extends Component {
// reset the DOM:
let element = parent.firstChild;
parent.removeChild(element);
if (element) {
parent.removeChild(element);
}
// create a new container element
element = document.createElement("div");
parent.appendChild(element);
......@@ -80,9 +82,7 @@ export default class CardRenderer extends Component {
render() {
return (
<div className="Card-outer">
<div ref="chart"></div>
</div>
<div className={cx(this.props.className, "Card-outer")}></div>
);
}
}
......@@ -13,7 +13,6 @@ fullscreen dashboard mode
:local .LegendHeader {
margin-top: 0.5em;
margin-bottom: 0.5em;
height: 24px;
}
:local(.LegendItem).muted {
......
import React, { Component, PropTypes } from "react";
import ExplicitSize from "metabase/components/ExplicitSize.jsx";
import LegendHeader from "metabase/visualizations/components/LegendHeader.jsx";
import LoadingSpinner from "metabase/components/LoadingSpinner.jsx";
import Icon from "metabase/components/Icon.jsx";
import Tooltip from "metabase/components/Tooltip.jsx";
import visualizations from "metabase/visualizations";
import i from "icepick";
import _ from "underscore";
import cx from "classnames";
const ERROR_MESSAGE_GENERIC = "There was a problem displaying this chart.";
@ExplicitSize
export default class Visualization extends Component {
constructor(props, context) {
super(props, context)
......@@ -38,29 +46,6 @@ export default class Visualization extends Component {
onUpdateVisualizationSetting: (...args) => console.warn("onUpdateVisualizationSetting", args)
};
componentWillMount() {
this.componentWillReceiveProps(this.props);
}
componentWillReceiveProps(newProps) {
let { card, data } = newProps.series[0]
if (!data) {
this.setState({ error: "No data (TODO)" });
} else if (!card.display) {
this.setState({ error: "Chart type not set" });
} else {
let CardVisualization = visualizations.get(card.display);
try {
if (CardVisualization.checkRenderable) {
CardVisualization.checkRenderable(data.cols, data.rows);
}
this.setState({ error: null });
} catch (e) {
this.setState({ error: e.message || "Missing error message (TODO)" });
}
}
}
onHoverChange(hovered) {
const { renderInfo } = this.state;
if (hovered) {
......@@ -82,12 +67,30 @@ export default class Visualization extends Component {
}
render() {
let { series, actionButtons, className, isDashboard } = this.props;
let CardVisualization = visualizations.get(series[0].card.display);
const { series, actionButtons, className, isDashboard, width } = this.props;
const CardVisualization = visualizations.get(series[0].card.display);
const small = width < 330;
let error = this.props.error || this.state.error;
let loading = !(series.length > 0 && _.every(series, (s) => s.data));
if (!loading && !error) {
if (!CardVisualization) {
error = "Could not find visualization";
} else {
try {
if (CardVisualization.checkRenderable) {
CardVisualization.checkRenderable(series[0].data.cols, series[0].data.rows);
}
} catch (e) {
error = e.message || "Could not display this chart with this data.";
}
}
}
return (
<div className={cx(className, "flex flex-column")}>
{ isDashboard && (error || !CardVisualization.noHeader) ?
{ isDashboard && (loading || error || !CardVisualization.noHeader) ?
<div className="p1 flex-no-shrink">
<LegendHeader
series={series}
......@@ -97,13 +100,24 @@ export default class Visualization extends Component {
: null
}
{ error ?
<div className="QueryError flex flex-full align-center text-error">
<div className="QueryError-iconWrapper hide">
<svg className="QueryError-icon" viewBox="0 0 32 32" width="64" height="64" fill="currentcolor">
<path d="M4 8 L8 4 L16 12 L24 4 L28 8 L20 16 L28 24 L24 28 L16 20 L8 28 L4 24 L12 16 z "></path>
</svg>
</div>
<span className="QueryError-message">{error}</span>
<div className="flex-full px1 pb1 text-centered text-slate-light flex flex-column layout-centered">
<Tooltip tooltip={isDashboard ? ERROR_MESSAGE_GENERIC : error} isEnabled={small}>
<div className="mb2">
<Icon name="warning" width={50} height={50} />
</div>
</Tooltip>
{ !small &&
<span className="h4 text-bold">
{ isDashboard ? ERROR_MESSAGE_GENERIC : error }
</span>
}
</div>
: loading ?
<div className="flex-full p1 text-centered text-brand flex flex-column layout-centered">
<LoadingSpinner />
<span className="h4 text-bold ml1 text-slate-light">
Loading...
</span>
</div>
:
<CardVisualization
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment