feat: add Current time-range options for time filter (#28637)
Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
parent
f2e020e398
commit
066f6b1f8a
|
|
@ -53,6 +53,7 @@ import {
|
|||
AdvancedFrame,
|
||||
DateLabel,
|
||||
} from './components';
|
||||
import { CurrentCalendarFrame } from './components/CurrentCalendarFrame';
|
||||
|
||||
const StyledRangeType = styled(Select)`
|
||||
width: 272px;
|
||||
|
|
@ -201,6 +202,7 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||
if (
|
||||
guessedFrame === 'Common' ||
|
||||
guessedFrame === 'Calendar' ||
|
||||
guessedFrame === 'Current' ||
|
||||
guessedFrame === 'No filter'
|
||||
) {
|
||||
setActualTimeRange(value);
|
||||
|
|
@ -296,6 +298,12 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||
{frame === 'Calendar' && (
|
||||
<CalendarFrame value={timeRangeValue} onChange={setTimeRangeValue} />
|
||||
)}
|
||||
{frame === 'Current' && (
|
||||
<CurrentCalendarFrame
|
||||
value={timeRangeValue}
|
||||
onChange={setTimeRangeValue}
|
||||
/>
|
||||
)}
|
||||
{frame === 'Advanced' && (
|
||||
<AdvancedFrame value={timeRangeValue} onChange={setTimeRangeValue} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React, { useEffect } from 'react';
|
||||
import { t } from '@superset-ui/core';
|
||||
import { Radio } from 'src/components/Radio';
|
||||
import {
|
||||
CURRENT_RANGE_OPTIONS,
|
||||
CURRENT_CALENDAR_RANGE_SET,
|
||||
} from 'src/explore/components/controls/DateFilterControl/utils';
|
||||
import { CurrentRangeType, CurrentWeek, FrameComponentProps } from '../types';
|
||||
|
||||
export function CurrentCalendarFrame({ onChange, value }: FrameComponentProps) {
|
||||
useEffect(() => {
|
||||
if (!CURRENT_CALENDAR_RANGE_SET.has(value as CurrentRangeType)) {
|
||||
onChange(CurrentWeek);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
if (!CURRENT_CALENDAR_RANGE_SET.has(value as CurrentRangeType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="section-title">
|
||||
{t('Configure Time Range: Current...')}
|
||||
</div>
|
||||
<Radio.Group
|
||||
value={value}
|
||||
onChange={(e: any) => {
|
||||
let newValue = e.target.value;
|
||||
// Sanitization: Trim whitespace
|
||||
newValue = newValue.trim();
|
||||
// Validation: Check if the value is non-empty
|
||||
if (newValue === '') {
|
||||
return;
|
||||
}
|
||||
onChange(newValue);
|
||||
}}
|
||||
>
|
||||
{CURRENT_RANGE_OPTIONS.map(({ value, label }) => (
|
||||
<Radio key={value} value={value} className="vertical-radio">
|
||||
{label}
|
||||
</Radio>
|
||||
))}
|
||||
</Radio.Group>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
export { CommonFrame } from './CommonFrame';
|
||||
export { CalendarFrame } from './CalendarFrame';
|
||||
export { CurrentCalendarFrame } from './CurrentCalendarFrame';
|
||||
export { CustomFrame } from './CustomFrame';
|
||||
export { AdvancedFrame } from './AdvancedFrame';
|
||||
export { DateLabel } from './DateLabel';
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect'; // For advanced DOM assertions
|
||||
import { CurrentCalendarFrame } from '../components/CurrentCalendarFrame';
|
||||
import { CurrentWeek } from '../types';
|
||||
|
||||
const mockOnChange = jest.fn();
|
||||
|
||||
test('calls onChange(CurrentWeek) when value is invalid', () => {
|
||||
render(<CurrentCalendarFrame onChange={mockOnChange} value="InvalidValue" />);
|
||||
expect(mockOnChange).toHaveBeenCalledWith(CurrentWeek);
|
||||
});
|
||||
|
||||
test('returns null if value is not a valid CurrentRangeType', () => {
|
||||
const { container } = render(
|
||||
<CurrentCalendarFrame onChange={mockOnChange} value="InvalidValue" />,
|
||||
);
|
||||
expect(container.childNodes.length).toBe(0);
|
||||
});
|
||||
|
|
@ -24,6 +24,7 @@ export type SelectOptionType = {
|
|||
export type FrameType =
|
||||
| 'Common'
|
||||
| 'Calendar'
|
||||
| 'Current'
|
||||
| 'Custom'
|
||||
| 'Advanced'
|
||||
| 'No filter';
|
||||
|
|
@ -85,6 +86,18 @@ export type CalendarRangeType =
|
|||
| typeof PreviousCalendarMonth
|
||||
| typeof PreviousCalendarYear;
|
||||
|
||||
export const CurrentDay = 'Current day';
|
||||
export const CurrentWeek = 'Current week';
|
||||
export const CurrentMonth = 'Current month';
|
||||
export const CurrentYear = 'Current year';
|
||||
export const CurrentQuarter = 'Current quarter';
|
||||
export type CurrentRangeType =
|
||||
| typeof CurrentDay
|
||||
| typeof CurrentWeek
|
||||
| typeof CurrentMonth
|
||||
| typeof CurrentQuarter
|
||||
| typeof CurrentYear;
|
||||
|
||||
export type FrameComponentProps = {
|
||||
onChange: (timeRange: string) => void;
|
||||
value: string;
|
||||
|
|
|
|||
|
|
@ -25,11 +25,18 @@ import {
|
|||
PreviousCalendarYear,
|
||||
CommonRangeType,
|
||||
CalendarRangeType,
|
||||
CurrentRangeType,
|
||||
CurrentWeek,
|
||||
CurrentMonth,
|
||||
CurrentYear,
|
||||
CurrentQuarter,
|
||||
CurrentDay,
|
||||
} from 'src/explore/components/controls/DateFilterControl/types';
|
||||
|
||||
export const FRAME_OPTIONS: SelectOptionType[] = [
|
||||
{ value: 'Common', label: t('Last') },
|
||||
{ value: 'Calendar', label: t('Previous') },
|
||||
{ value: 'Current', label: t('Current') },
|
||||
{ value: 'Custom', label: t('Custom') },
|
||||
{ value: 'Advanced', label: t('Advanced') },
|
||||
{ value: 'No filter', label: t('No filter') },
|
||||
|
|
@ -48,16 +55,24 @@ export const COMMON_RANGE_VALUES_SET = new Set(
|
|||
|
||||
export const CALENDAR_RANGE_OPTIONS: SelectOptionType[] = [
|
||||
{ value: PreviousCalendarWeek, label: t('previous calendar week') },
|
||||
{
|
||||
value: PreviousCalendarMonth,
|
||||
label: t('previous calendar month'),
|
||||
},
|
||||
{ value: PreviousCalendarMonth, label: t('previous calendar month') },
|
||||
{ value: PreviousCalendarYear, label: t('previous calendar year') },
|
||||
];
|
||||
export const CALENDAR_RANGE_VALUES_SET = new Set(
|
||||
CALENDAR_RANGE_OPTIONS.map(({ value }) => value),
|
||||
);
|
||||
|
||||
export const CURRENT_RANGE_OPTIONS: SelectOptionType[] = [
|
||||
{ value: CurrentDay, label: t('Current day') },
|
||||
{ value: CurrentWeek, label: t('Current week') },
|
||||
{ value: CurrentMonth, label: t('Current month') },
|
||||
{ value: CurrentQuarter, label: t('Current quarter') },
|
||||
{ value: CurrentYear, label: t('Current year') },
|
||||
];
|
||||
export const CURRENT_RANGE_VALUES_SET = new Set(
|
||||
CURRENT_RANGE_OPTIONS.map(({ value }) => value),
|
||||
);
|
||||
|
||||
const GRAIN_OPTIONS = [
|
||||
{ value: 'second', label: (rel: string) => t('Seconds %s', rel) },
|
||||
{ value: 'minute', label: (rel: string) => t('Minutes %s', rel) },
|
||||
|
|
@ -107,6 +122,14 @@ export const CALENDAR_RANGE_SET: Set<CalendarRangeType> = new Set([
|
|||
PreviousCalendarYear,
|
||||
]);
|
||||
|
||||
export const CURRENT_CALENDAR_RANGE_SET: Set<CurrentRangeType> = new Set([
|
||||
CurrentDay,
|
||||
CurrentWeek,
|
||||
CurrentMonth,
|
||||
CurrentQuarter,
|
||||
CurrentYear,
|
||||
]);
|
||||
|
||||
export const MOMENT_FORMAT = 'YYYY-MM-DD[T]HH:mm:ss';
|
||||
export const SEVEN_DAYS_AGO = moment()
|
||||
.utc()
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import { useSelector } from 'react-redux';
|
|||
import {
|
||||
COMMON_RANGE_VALUES_SET,
|
||||
CALENDAR_RANGE_VALUES_SET,
|
||||
CURRENT_RANGE_VALUES_SET,
|
||||
customTimeRangeDecode,
|
||||
} from '.';
|
||||
import { FrameType } from '../types';
|
||||
|
|
@ -32,6 +33,9 @@ export const guessFrame = (timeRange: string): FrameType => {
|
|||
if (CALENDAR_RANGE_VALUES_SET.has(timeRange)) {
|
||||
return 'Calendar';
|
||||
}
|
||||
if (CURRENT_RANGE_VALUES_SET.has(timeRange)) {
|
||||
return 'Current';
|
||||
}
|
||||
if (timeRange === NO_TIME_RANGE) {
|
||||
return 'No filter';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,36 @@ def get_since_until( # pylint: disable=too-many-arguments,too-many-locals,too-m
|
|||
and separator not in time_range
|
||||
):
|
||||
time_range = "DATETRUNC(DATEADD(DATETIME('today'), -1, YEAR), YEAR) : DATETRUNC(DATETIME('today'), YEAR)" # pylint: disable=line-too-long,useless-suppression
|
||||
if (
|
||||
time_range
|
||||
and time_range.startswith("Current day")
|
||||
and separator not in time_range
|
||||
):
|
||||
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, DAY), DAY) : DATETRUNC(DATEADD(DATETIME('today'), 1, DAY), DAY)" # pylint: disable=line-too-long,useless-suppression
|
||||
if (
|
||||
time_range
|
||||
and time_range.startswith("Current week")
|
||||
and separator not in time_range
|
||||
):
|
||||
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, WEEK), WEEK) : DATETRUNC(DATEADD(DATETIME('today'), 1, WEEK), WEEK)" # pylint: disable=line-too-long,useless-suppression
|
||||
if (
|
||||
time_range
|
||||
and time_range.startswith("Current month")
|
||||
and separator not in time_range
|
||||
):
|
||||
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, MONTH), MONTH) : DATETRUNC(DATEADD(DATETIME('today'), 1, MONTH), MONTH)" # pylint: disable=line-too-long,useless-suppression
|
||||
if (
|
||||
time_range
|
||||
and time_range.startswith("Current quarter")
|
||||
and separator not in time_range
|
||||
):
|
||||
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, QUARTER), QUARTER) : DATETRUNC(DATEADD(DATETIME('today'), 1, QUARTER), QUARTER)" # pylint: disable=line-too-long,useless-suppression
|
||||
if (
|
||||
time_range
|
||||
and time_range.startswith("Current year")
|
||||
and separator not in time_range
|
||||
):
|
||||
time_range = "DATETRUNC(DATEADD(DATETIME('today'), 0, YEAR), YEAR) : DATETRUNC(DATEADD(DATETIME('today'), 1, YEAR), YEAR)" # pylint: disable=line-too-long,useless-suppression
|
||||
|
||||
if time_range and separator in time_range:
|
||||
time_range_lookup = [
|
||||
|
|
|
|||
|
|
@ -160,6 +160,26 @@ def test_get_since_until() -> None:
|
|||
expected = datetime(2015, 1, 1, 0, 0, 0), datetime(2016, 1, 1, 0, 0, 0)
|
||||
assert result == expected
|
||||
|
||||
result = get_since_until("Current day")
|
||||
expected = datetime(2016, 11, 7, 0, 0, 0), datetime(2016, 11, 8, 0, 0, 0)
|
||||
assert result == expected
|
||||
|
||||
result = get_since_until("Current week")
|
||||
expected = datetime(2016, 11, 7, 0, 0, 0), datetime(2016, 11, 14, 0, 0, 0)
|
||||
assert result == expected
|
||||
|
||||
result = get_since_until("Current month")
|
||||
expected = datetime(2016, 11, 1, 0, 0, 0), datetime(2016, 12, 1, 0, 0, 0)
|
||||
assert result == expected
|
||||
|
||||
result = get_since_until("Current quarter")
|
||||
expected = datetime(2016, 10, 1, 0, 0, 0), datetime(2017, 1, 1, 0, 0, 0)
|
||||
assert result == expected
|
||||
|
||||
result = get_since_until("Current year")
|
||||
expected = expected = datetime(2016, 1, 1, 0, 0, 0), datetime(2017, 1, 1, 0, 0, 0)
|
||||
assert result == expected
|
||||
|
||||
# Tests for our new instant_time_comparison logic and Feature Flag off
|
||||
result = get_since_until(
|
||||
time_range="2000-01-01T00:00:00 : 2018-01-01T00:00:00",
|
||||
|
|
|
|||
Loading…
Reference in New Issue