blob: 1bcaa52fa579a73c6e360725df022a9648559f7d [file] [log] [blame]
// JS
import d3 from 'd3';
import React from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import Select from 'react-select';
import { Button } from 'react-bootstrap';
import { TIME_CHOICES } from './constants';
import './filter_box.css';
import { t } from '../javascripts/locales';
const propTypes = {
origSelectedValues: PropTypes.object,
instantFiltering: PropTypes.bool,
filtersChoices: PropTypes.object,
onChange: PropTypes.func,
showDateFilter: PropTypes.bool,
datasource: PropTypes.object.isRequired,
};
const defaultProps = {
origSelectedValues: {},
onChange: () => {},
showDateFilter: false,
instantFiltering: true,
};
class FilterBox extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedValues: props.origSelectedValues,
hasChanged: false,
};
}
clickApply() {
this.props.onChange(Object.keys(this.state.selectedValues)[0], [], true, true);
this.setState({ hasChanged: false });
}
changeFilter(filter, options) {
let vals = null;
if (options) {
if (Array.isArray(options)) {
vals = options.map(opt => opt.value);
} else {
vals = options.value;
}
}
const selectedValues = Object.assign({}, this.state.selectedValues);
selectedValues[filter] = vals;
this.setState({ selectedValues, hasChanged: true });
this.props.onChange(filter, vals, false, this.props.instantFiltering);
}
render() {
let dateFilter;
if (this.props.showDateFilter) {
dateFilter = ['__from', '__to'].map((field) => {
const val = this.state.selectedValues[field];
const choices = TIME_CHOICES.slice();
if (!choices.includes(val)) {
choices.push(val);
}
const options = choices.map(s => ({ value: s, label: s }));
return (
<div className="m-b-5" key={field}>
{field.replace('__', '')}
<Select.Creatable
placeholder="Select"
options={options}
value={this.state.selectedValues[field]}
onChange={this.changeFilter.bind(this, field)}
/>
</div>
);
});
}
// Add created options to filtersChoices, even though it doesn't exist,
// or these options will exist in query sql but invisible to end user.
if (this.state.selectedValues.hasOwnProperty()) {
for (const filterKey of this.state.selectedValues) {
const existValues = this.props.filtersChoices[filterKey].map(f => f.id);
for (const v of this.state.selectedValues[filterKey]) {
if (existValues.indexOf(v) === -1) {
const addChoice = {
filter: filterKey,
id: v,
text: v,
metric: 0,
};
this.props.filtersChoices[filterKey].push(addChoice);
}
}
}
}
const filters = Object.keys(this.props.filtersChoices).map((filter) => {
const data = this.props.filtersChoices[filter];
const maxes = {};
maxes[filter] = d3.max(data, function (d) {
return d.metric;
});
return (
<div key={filter} className="m-b-5">
{this.props.datasource.verbose_map[filter] || filter}
<Select.Creatable
placeholder={t('Select [%s]', filter)}
key={filter}
multi
value={this.state.selectedValues[filter]}
options={data.map((opt) => {
const perc = Math.round((opt.metric / maxes[opt.filter]) * 100);
const backgroundImage = (
'linear-gradient(to right, lightgrey, ' +
`lightgrey ${perc}%, rgba(0,0,0,0) ${perc}%`
);
const style = {
backgroundImage,
padding: '2px 5px',
};
return { value: opt.id, label: opt.id, style };
})}
onChange={this.changeFilter.bind(this, filter)}
/>
</div>
);
});
return (
<div>
{dateFilter}
{filters}
{!this.props.instantFiltering &&
<Button
bsSize="small"
bsStyle="primary"
onClick={this.clickApply.bind(this)}
disabled={!this.state.hasChanged}
>
Apply
</Button>
}
</div>
);
}
}
FilterBox.propTypes = propTypes;
FilterBox.defaultProps = defaultProps;
function filterBox(slice, payload) {
const d3token = d3.select(slice.selector);
d3token.selectAll('*').remove();
// filter box should ignore the dashboard's filters
// const url = slice.jsonEndpoint({ extraFilters: false });
const fd = slice.formData;
const filtersChoices = {};
// Making sure the ordering of the fields matches the setting in the
// dropdown as it may have been shuffled while serialized to json
fd.groupby.forEach((f) => {
filtersChoices[f] = payload.data[f];
});
ReactDOM.render(
<FilterBox
filtersChoices={filtersChoices}
onChange={slice.addFilter}
showDateFilter={fd.date_filter}
datasource={slice.datasource}
origSelectedValues={slice.getFilters() || {}}
instantFiltering={fd.instant_filtering}
/>,
document.getElementById(slice.containerId),
);
}
module.exports = filterBox;