Skip to content

Commit

Permalink
A nice CacheLabel React component (#2628)
Browse files Browse the repository at this point in the history
Introducing a nice component as a label that show when data was
loaded from cache, when the cache was taken (in humanize duration as in
`a few minutes ago`) in a tooltip, and it can act as a button that
can trigger a force-refresh.

While working on it, it became clear that it was going to be hard to
use this component in the Dashboard view since it's not pure React.
I'm planning on refactoring the dashboard view with proper React/Redux
and introducing the CachedLabel component at that point.

While digging around in the Dashboard view I realized that there was a
bunch on unused code around managing timers that was used in explorev1
and decided to rip it out.
  • Loading branch information
mistercrunch authored Apr 17, 2017
1 parent 23aeee5 commit 787daf6
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 34 deletions.
68 changes: 68 additions & 0 deletions superset/assets/javascripts/components/CachedLabel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React, { PropTypes } from 'react';
import { Label } from 'react-bootstrap';
import moment from 'moment';
import TooltipWrapper from './TooltipWrapper';

const propTypes = {
onClick: PropTypes.func,
cachedTimestamp: PropTypes.string,
className: PropTypes.string,
};

class CacheLabel extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
tooltipContent: '',
hovered: false,
};
}

updateTooltipContent() {
const cachedText = this.props.cachedTimestamp ? (
<span>
Loaded data cached <b>{moment(this.props.cachedTimestamp).fromNow()}</b>
</span>) :
'Loaded from cache';

const tooltipContent = (
<span>
{cachedText}.
Click to force-refresh
</span>
);
this.setState({ tooltipContent });
}

mouseOver() {
this.updateTooltipContent();
this.setState({ hovered: true });
}

mouseOut() {
this.setState({ hovered: false });
}

render() {
const labelStyle = this.state.hovered ? 'primary' : 'default';
return (
<TooltipWrapper
tooltip={this.state.tooltipContent}
label="cache-desc"
>
<Label
className={this.props.className}
bsStyle={labelStyle}
style={{ fontSize: '10px', marginRight: '5px', cursor: 'pointer' }}
onClick={this.props.onClick}
onMouseOver={this.mouseOver.bind(this)}
onMouseOut={this.mouseOut.bind(this)}
>
cached <i className="fa fa-refresh" />
</Label>
</TooltipWrapper>);
}
}
CacheLabel.propTypes = propTypes;

export default CacheLabel;
2 changes: 1 addition & 1 deletion superset/assets/javascripts/components/TooltipWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { slugify } from '../modules/utils';

const propTypes = {
label: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired,
tooltip: PropTypes.node.isRequired,
children: PropTypes.node.isRequired,
placement: PropTypes.string,
};
Expand Down
11 changes: 7 additions & 4 deletions superset/assets/javascripts/dashboard/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { render } from 'react-dom';
import d3 from 'd3';
import { Alert } from 'react-bootstrap';
import moment from 'moment';

import GridLayout from './components/GridLayout';
import Header from './components/Header';
Expand Down Expand Up @@ -143,13 +144,15 @@ export function dashboardContainer(dashboard, datasources) {
done(slice) {
const refresh = slice.getWidgetHeader().find('.refresh');
const data = slice.data;
const cachedWhen = moment(data.cached_dttm).fromNow();
if (data !== undefined && data.is_cached) {
refresh
.addClass('danger')
.attr('title',
'Served from data cached at ' + data.cached_dttm +
'. Click to force refresh')
.tooltip('fixTitle');
.attr(
'title',
`Served from data cached ${cachedWhen}. ` +
'Click to force refresh')
.tooltip('fixTitle');
} else {
refresh
.removeClass('danger')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import $ from 'jquery';
import Mustache from 'mustache';
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { Alert, Collapse, Label, Panel } from 'react-bootstrap';
import { Alert, Collapse, Panel } from 'react-bootstrap';
import visMap from '../../../visualizations/main';
import { d3format } from '../../modules/utils';
import ExploreActionButtons from './ExploreActionButtons';
Expand All @@ -11,6 +11,7 @@ import TooltipWrapper from '../../components/TooltipWrapper';
import Timer from '../../components/Timer';
import { getExploreUrl } from '../exploreUtils';
import { getFormDataFromControls } from '../stores/store';
import CachedLabel from '../../components/CachedLabel';

const CHART_STATUS_MAP = {
failed: 'danger',
Expand Down Expand Up @@ -265,17 +266,10 @@ class ChartContainer extends React.PureComponent {
{this.props.chartStatus === 'success' &&
this.props.queryResponse &&
this.props.queryResponse.is_cached &&
<TooltipWrapper
tooltip="Loaded from cache. Click to force refresh"
label="cache-desc"
>
<Label
style={{ fontSize: '10px', marginRight: '5px', cursor: 'pointer' }}
onClick={this.runQuery.bind(this)}
>
cached
</Label>
</TooltipWrapper>
<CachedLabel
onClick={this.runQuery.bind(this)}
cachedTimestamp={queryResponse.cached_dttm}
/>
}
<Timer
startTime={this.props.chartUpdateStartTime}
Expand Down
17 changes: 0 additions & 17 deletions superset/assets/javascripts/modules/superset.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,12 @@ const px = function () {
.tooltip();
}
const Slice = function (data, datasource, controller) {
let timer;
const token = $('#token_' + data.slice_id);
const containerId = 'con_' + data.slice_id;
const selector = '#' + containerId;
const container = $(selector);
const sliceId = data.slice_id;
const formData = applyDefaultFormData(data.form_data);
let dttm = 0;
const stopwatch = function () {
dttm += 10;
const num = dttm / 1000;
$('#timer').text(num.toFixed(2) + ' sec');
};
slice = {
data,
formData,
Expand Down Expand Up @@ -114,22 +107,17 @@ const px = function () {
},
/* eslint no-shadow: 0 */
always(data) {
clearInterval(timer);
$('#timer').removeClass('btn-warning');
if (data && data.query) {
slice.viewSqlQuery = data.query;
}
},
done(payload) {
Object.assign(data, payload);

clearInterval(timer);
token.find('img.loading').hide();
container.fadeTo(0.5, 1);
container.show();

$('#timer').addClass('label-success');
$('#timer').removeClass('label-warning label-danger');
$('.query-and-save button').removeAttr('disabled');
this.always(data);
controller.done(this);
Expand Down Expand Up @@ -173,7 +161,6 @@ const px = function () {
container.html(errHtml);
container.show();
$('span.query').removeClass('disabled');
$('#timer').addClass('btn-danger');
$('.query-and-save button').removeAttr('disabled');
this.always(o);
controller.error(this);
Expand Down Expand Up @@ -213,10 +200,6 @@ const px = function () {
token.find('img.loading').show();
container.fadeTo(0.5, 0.25);
container.css('height', this.height());
dttm = 0;
timer = setInterval(stopwatch, 10);
$('#timer').removeClass('label-danger label-success');
$('#timer').addClass('label-warning');
$.getJSON(this.jsonEndpoint(), (queryResponse) => {
try {
vizMap[formData.viz_type](this, queryResponse);
Expand Down
26 changes: 26 additions & 0 deletions superset/assets/spec/javascripts/components/CachedLabel_spec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { expect } from 'chai';
import { describe, it } from 'mocha';
import { shallow } from 'enzyme';
import { Label } from 'react-bootstrap';

import CachedLabel from '../../../javascripts/components/CachedLabel';

describe('CachedLabel', () => {
const defaultProps = {
onClick: () => {},
cachedTimestamp: '2017-01-01',
};

it('is valid', () => {
expect(
React.isValidElement(<CachedLabel {...defaultProps} />),
).to.equal(true);
});
it('renders', () => {
const wrapper = shallow(
<CachedLabel {...defaultProps} />,
);
expect(wrapper.find(Label)).to.have.length(1);
});
});

0 comments on commit 787daf6

Please sign in to comment.