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

Add 'current period' support to date parameters

parent ed2b1baf
No related branches found
No related tags found
No related merge requests found
......@@ -20,7 +20,6 @@ export default class CheckBox extends Component {
};
onClick() {
console.log('called?')
if (this.props.onChange) {
// TODO: use a proper event object?
this.props.onChange({ target: { checked: !this.props.checked }})
......
......@@ -60,11 +60,11 @@ type Deserializer = { testRegex: RegExp, deserialize: DeserializeFn}
type DeserializeFn = (match: any[], fieldRef: LocalFieldReference | ForeignFieldReference) => FieldFilter;
const timeParameterValueDeserializers: Deserializer[] = [
{testRegex: /^past([0-9]+)([a-z]+)s$/, deserialize: (matches, fieldRef) =>
["time-interval", fieldRef, -parseInt(matches[0]), matches[1]]
{testRegex: /^past([0-9]+)([a-z]+)s(~)?$/, deserialize: (matches, fieldRef) =>
["time-interval", fieldRef, -parseInt(matches[0]), matches[1], matches[2] ? { "include-current": true } : {}]
},
{testRegex: /^next([0-9]+)([a-z]+)s$/, deserialize: (matches, fieldRef) =>
["time-interval", fieldRef, parseInt(matches[0]), matches[1]]
{testRegex: /^next([0-9]+)([a-z]+)s(~)?$/, deserialize: (matches, fieldRef) =>
["time-interval", fieldRef, parseInt(matches[0]), matches[1], matches[2] ? { "include-current": true } : {}]
},
{testRegex: /^this([a-z]+)$/, deserialize: (matches, fieldRef) =>
["time-interval", fieldRef, "current", matches[0]]
......
......@@ -4,6 +4,7 @@ import React, {Component} from "react";
import cx from "classnames";
import DatePicker, {DATE_OPERATORS} from "metabase/query_builder/components/filters/pickers/DatePicker.jsx";
import FilterOptions from "metabase/query_builder/components/filters/FilterOptions.jsx";
import {generateTimeFilterValuesDescriptions} from "metabase/lib/query_time";
import { dateParameterValueToMBQL } from "metabase/meta/Parameter";
......@@ -18,13 +19,13 @@ const noopRef: LocalFieldReference = null;
function getFilterValueSerializer(func: ((val1: string, val2: string) => UrlEncoded)) {
// $FlowFixMe
return filter => func(filter[2], filter[3])
return filter => func(filter[2], filter[3], filter[4] || {})
}
const serializersByOperatorName: { [id: OperatorName]: (FieldFilter) => UrlEncoded } = {
// $FlowFixMe
"Previous": getFilterValueSerializer((value, unit) => `past${-value}${unit}s`),
"Next": getFilterValueSerializer((value, unit) => `next${value}${unit}s`),
"Previous": getFilterValueSerializer((value, unit, options) => `past${-value}${unit}s${options['include-current'] ? "~" : ""}`),
"Next": getFilterValueSerializer((value, unit, options) => `next${value}${unit}s${options['include-current'] ? "~" : ""}`),
"Current": getFilterValueSerializer((_, unit) => `this${unit}`),
"Before": getFilterValueSerializer((value) => `~${value}`),
"After": getFilterValueSerializer((value) => `${value}~`),
......@@ -99,6 +100,7 @@ export default class DateAllOptionsWidget extends Component {
}
render() {
const { filter } = this.state;
return (<div style={{minWidth: "300px"}}>
<DatePicker
filter={this.state.filter}
......@@ -106,9 +108,10 @@ export default class DateAllOptionsWidget extends Component {
hideEmptinessOperators
hideTimeSelectors
/>
<div className="FilterPopover-footer p1">
<div className="FilterPopover-footer border-top flex align-center p2">
<FilterOptions filter={filter} onFilterChange={this.setFilter} />
<button
className={cx("Button Button--purple full", {"disabled": !this.isValid()})}
className={cx("Button Button--purple ml-auto", {"disabled": !this.isValid()})}
onClick={this.commitAndClose}
>
Update filter
......
import React, { Component } from "react";
import { t, jt } from 'c-3po';
import CheckBox from "metabase/components/CheckBox";
import { getOperator } from "./pickers/DatePicker.jsx";
const CURRENT_INTERVAL_NAME = {
"day": t`today`,
"week": t`this week`,
"month": t`this month`,
"year": t`this year`,
"minute": t`this minute`,
"hour": t`this hour`,
};
function getCurrentIntervalName(filter: FieldFilter): ?string {
if (filter[0] === "time-interval") {
// $FlowFixMe:
return CURRENT_INTERVAL_NAME[filter[3]];
}
return null;
}
function getFilterOptions(filter: FieldFilter): FilterOptions {
if (filter[0] === "time-interval") {
// $FlowFixMe:
const options: FilterOptions = filter[4] || {};
return options;
}
return {};
}
function setFilterOptions<T: FieldFilter>(filter: T, options: FilterOptions): T {
if (filter[0] === "time-interval") {
// $FlowFixMe
return [...filter.slice(0,4), options];
} else {
return filter;
}
}
export default class FilterOptions extends Component {
hasCurrentPeriod = () => {
const { filter } = this.props;
return getFilterOptions(filter)["include-current"] || false;
}
toggleCurrentPeriod = () => {
const { filter } = this.props;
const operator = getOperator(filter);
if (operator && operator.options && operator.options["include-current"]) {
const options = getFilterOptions(filter);
this.props.onFilterChange(setFilterOptions(filter, {
...options,
"include-current": !options["include-current"]
}));
}
}
render() {
const { filter } = this.props;
const operator = getOperator(filter);
if (operator && operator.options && operator.options["include-current"]) {
return (
<div className="flex align-center" onClick={() => this.toggleCurrentPeriod()}>
<CheckBox checked={this.hasCurrentPeriod()} />
<label className="ml1">
{jt`Include ${<b>{getCurrentIntervalName(filter)}</b>}`}
</label>
</div>
)
}
return null;
}
}
/* @flow */
import React, { Component } from "react";
import { t } from 'c-3po';
import FieldList from "../FieldList.jsx";
import OperatorSelector from "./OperatorSelector.jsx";
import { t, jt } from 'c-3po';
import FilterOptions from "./FilterOptions";
import DatePicker, { getOperator } from "./pickers/DatePicker.jsx";
import NumberPicker from "./pickers/NumberPicker.jsx";
import SelectPicker from "./pickers/SelectPicker.jsx";
import TextPicker from "./pickers/TextPicker.jsx";
import Checkbox from "metabase/components/CheckBox";
import Icon from "metabase/components/Icon.jsx";
......@@ -20,7 +20,7 @@ import { formatField, singularize } from "metabase/lib/formatting";
import cx from "classnames";
import StructuredQuery from "metabase-lib/lib/queries/StructuredQuery";
import type { Filter, FieldFilter, ConcreteField, FilterOptions } from "metabase/meta/types/Query";
import type { Filter, FieldFilter, ConcreteField } from "metabase/meta/types/Query";
import type { FieldMetadata, Operator } from "metabase/meta/types/Metadata";
type Props = {
......@@ -35,41 +35,6 @@ type State = {
filter: FieldFilter
}
const CURRENT_INTERVAL_NAME = {
"day": t`today`,
"week": t`this week`,
"month": t`this month`,
"year": t`this year`,
"minute": t`this minute`,
"hour": t`this hour`,
};
function getCurrentIntervalName(filter: FieldFilter): ?string {
if (filter[0] === "time-interval") {
// $FlowFixMe:
return CURRENT_INTERVAL_NAME[filter[3]];
}
return null;
}
function getFilterOptions(filter: FieldFilter): FilterOptions {
if (filter[0] === "time-interval") {
// $FlowFixMe:
const options: FilterOptions = filter[4] || {};
return options;
}
return {};
}
function setFilterOptions<T: FieldFilter>(filter: T, options: FilterOptions): T {
if (filter[0] === "time-interval") {
// $FlowFixMe
return [...filter.slice(0,4), options];
} else {
return filter;
}
}
export default class FilterPopover extends Component {
props: Props;
state: State;
......@@ -144,25 +109,6 @@ export default class FilterPopover extends Component {
this.setState({ filter: newFilter });
}
hasCurrentPeriod = () => {
return getFilterOptions(this.state.filter)["include-current"] || false;
}
toggleCurrentPeriod = () => {
const { filter } = this.state;
const operator = getOperator(filter);
if (operator && operator.options && operator.options["include-current"]) {
const options = getFilterOptions(filter);
this.setState({
filter: setFilterOptions(filter, {
...options,
"include-current": !options["include-current"]
})
});
}
}
setValues = (values: any[]) => {
let { filter } = this.state;
// $FlowFixMe
......@@ -316,7 +262,6 @@ export default class FilterPopover extends Component {
} else {
let { table, field } = Query.getFieldTarget(fieldRef, query.table());
const dimension = query.parseFieldReference(fieldRef);
const operator = getOperator(filter);
return (
<div style={{
minWidth: 300,
......@@ -348,14 +293,7 @@ export default class FilterPopover extends Component {
</div>
}
<div className="FilterPopover-footer border-top flex align-center p2">
{ operator && operator.options && operator.options["include-current"] && (
<div className="flex align-center" onClick={() => this.toggleCurrentPeriod()}>
<Checkbox checked={this.hasCurrentPeriod()} />
<label className="ml1">
{jt`Include ${<b>{getCurrentIntervalName(filter)}</b>}`}
</label>
</div>
)}
<FilterOptions filter={filter} onFilterChange={this.setFilter} />
<button
data-ui-tag="add-filter"
className={cx("Button Button--purple ml-auto", { "disabled": !this.isValid() })}
......
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