From a485ba02bc5fa372ba0355f246c8df14bb2d2bea Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Mon, 7 Jun 2021 22:06:16 -0700 Subject: [PATCH 1/8] tidy up --- .../date-range-selector.scss} | 10 +- .../date-range-selector.tsx | 67 ++++++ .../interval-input/interval-input.tsx | 40 +--- .../segment-timeline}/bar-group.tsx | 8 +- .../segment-timeline/bar-unit.spec.tsx} | 15 +- .../segment-timeline}/bar-unit.tsx | 4 +- .../segment-timeline}/chart-axis.tsx | 2 +- .../segment-timeline/segment-timeline.scss | 15 +- .../segment-timeline.spec.tsx | 79 +------ .../segment-timeline/segment-timeline.tsx | 205 +++++++++--------- .../segment-timeline/stacked-bar-chart.scss | 50 +++++ .../segment-timeline}/stacked-bar-chart.tsx | 150 +++++++------ .../date.spec.ts} | 20 +- web-console/src/utils/date.ts | 65 ++++++ .../datasource-view/datasource-view.scss | 2 +- .../views/datasource-view/datasource-view.tsx | 47 ++-- .../__snapshots__/visualization.spec.tsx.snap | 22 -- 17 files changed, 423 insertions(+), 378 deletions(-) rename web-console/src/{visualization/bar-unit.scss => components/date-range-selector/date-range-selector.scss} (88%) create mode 100644 web-console/src/components/date-range-selector/date-range-selector.tsx rename web-console/src/{visualization => components/segment-timeline}/bar-group.tsx (91%) rename web-console/src/{visualization/visualization.spec.tsx => components/segment-timeline/bar-unit.spec.tsx} (76%) rename web-console/src/{visualization => components/segment-timeline}/bar-unit.tsx (95%) rename web-console/src/{visualization => components/segment-timeline}/chart-axis.tsx (98%) create mode 100644 web-console/src/components/segment-timeline/stacked-bar-chart.scss rename web-console/src/{visualization => components/segment-timeline}/stacked-bar-chart.tsx (54%) rename web-console/src/{visualization/stacked-bar-chart.scss => utils/date.spec.ts} (78%) create mode 100644 web-console/src/utils/date.ts delete mode 100644 web-console/src/visualization/__snapshots__/visualization.spec.tsx.snap diff --git a/web-console/src/visualization/bar-unit.scss b/web-console/src/components/date-range-selector/date-range-selector.scss similarity index 88% rename from web-console/src/visualization/bar-unit.scss rename to web-console/src/components/date-range-selector/date-range-selector.scss index 5767d681883c..a30d62287619 100644 --- a/web-console/src/visualization/bar-unit.scss +++ b/web-console/src/components/date-range-selector/date-range-selector.scss @@ -16,6 +16,12 @@ * limitations under the License. */ -.bar-chart-unit { - transform: translateX(65px); +.date-range-selector { + .bp3-popover-target { + display: block; + } + + * { + cursor: pointer; + } } diff --git a/web-console/src/components/date-range-selector/date-range-selector.tsx b/web-console/src/components/date-range-selector/date-range-selector.tsx new file mode 100644 index 000000000000..c06040383b66 --- /dev/null +++ b/web-console/src/components/date-range-selector/date-range-selector.tsx @@ -0,0 +1,67 @@ +/* + * 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 { Button, InputGroup, Popover, Position } from '@blueprintjs/core'; +import { DateRange, DateRangePicker } from '@blueprintjs/datetime'; +import { IconNames } from '@blueprintjs/icons'; +import React, { useState } from 'react'; + +import { dateToIsoDateString, localToUtcDate, utcToLocalDate } from '../../utils/date'; + +import './date-range-selector.scss'; + +interface DateRangeSelectorProps { + startDate: Date; + endDate: Date; + onChange: (startDate: Date, endDate: Date) => void; +} + +export const DateRangeSelector = React.memo(function DateRangeSelector( + props: DateRangeSelectorProps, +) { + const { startDate, endDate, onChange } = props; + const [intermediateDateRange, setIntermediateDateRange] = useState(); + + return ( + { + const [startDate, endDate] = selectedRange; + if (!startDate || !endDate) { + setIntermediateDateRange(selectedRange); + return; + } + onChange(localToUtcDate(startDate), localToUtcDate(endDate)); + }} + /> + } + position={Position.BOTTOM_RIGHT} + > + } + /> + + ); +}); diff --git a/web-console/src/components/interval-input/interval-input.tsx b/web-console/src/components/interval-input/interval-input.tsx index 64866cf82190..7944f1c4acf0 100644 --- a/web-console/src/components/interval-input/interval-input.tsx +++ b/web-console/src/components/interval-input/interval-input.tsx @@ -17,40 +17,13 @@ */ import { Button, InputGroup, Intent, Popover, Position } from '@blueprintjs/core'; -import { DateRange, DateRangePicker } from '@blueprintjs/datetime'; +import { DateRange, DateRangePicker, TimePrecision } from '@blueprintjs/datetime'; import { IconNames } from '@blueprintjs/icons'; import React from 'react'; -import './interval-input.scss'; - -const CURRENT_YEAR = new Date().getUTCFullYear(); - -function removeLocalTimezone(localDate: Date): Date { - // Function removes the local timezone of the date and displays it in UTC - return new Date(localDate.getTime() - localDate.getTimezoneOffset() * 60000); -} +import { intervalToLocalDateRange, localDateRangeToInterval } from '../../utils/date'; -function parseInterval(interval: string): DateRange { - const dates = interval.split('/'); - if (dates.length !== 2) { - return [null, null]; - } - const startDate = Date.parse(dates[0]) ? new Date(dates[0]) : null; - const endDate = Date.parse(dates[1]) ? new Date(dates[1]) : null; - // Must check if the start and end dates are within range - return [ - startDate && startDate.getFullYear() < CURRENT_YEAR - 20 ? null : startDate, - endDate && endDate.getFullYear() > CURRENT_YEAR ? null : endDate, - ]; -} -function stringifyDateRange(localRange: DateRange): string { - // This function takes in the dates selected from datepicker in local time, and displays them in UTC - // Shall Blueprint make any changes to the way dates are selected, this function will have to be reworked - const [localStartDate, localEndDate] = localRange; - return `${ - localStartDate ? removeLocalTimezone(localStartDate).toISOString().substring(0, 19) : '' - }/${localEndDate ? removeLocalTimezone(localEndDate).toISOString().substring(0, 19) : ''}`; -} +import './interval-input.scss'; export interface IntervalInputProps { interval: string; @@ -72,11 +45,12 @@ export const IntervalInput = React.memo(function IntervalInput(props: IntervalIn popoverClassName="calendar" content={ { - onValueChange(stringifyDateRange(selectedRange)); + onValueChange(localDateRangeToInterval(selectedRange)); }} /> } diff --git a/web-console/src/visualization/bar-group.tsx b/web-console/src/components/segment-timeline/bar-group.tsx similarity index 91% rename from web-console/src/visualization/bar-group.tsx rename to web-console/src/components/segment-timeline/bar-group.tsx index 1975c49e1f36..4bd75e6d117a 100644 --- a/web-console/src/visualization/bar-group.tsx +++ b/web-console/src/components/segment-timeline/bar-group.tsx @@ -19,10 +19,8 @@ import { AxisScale } from 'd3-axis'; import React from 'react'; -import { BarUnitData } from '../components/segment-timeline/segment-timeline'; - import { BarUnit } from './bar-unit'; -import { HoveredBarInfo } from './stacked-bar-chart'; +import { BarUnitData, HoveredBarInfo } from './stacked-bar-chart'; interface BarGroupProps { dataToRender: BarUnitData[]; @@ -54,9 +52,9 @@ export class BarGroup extends React.Component { return dataToRender.map((entry: BarUnitData, i: number) => { const y0 = yScale(entry.y0 || 0) || 0; - const x = xScale(new Date(entry.x)); + const x = xScale(new Date(entry.x + 'Z')); const y = yScale((entry.y0 || 0) + entry.y) || 0; - const height = y0 - y; + const height = Math.max(y0 - y, 0); const barInfo: HoveredBarInfo = { xCoordinate: x, yCoordinate: y, diff --git a/web-console/src/visualization/visualization.spec.tsx b/web-console/src/components/segment-timeline/bar-unit.spec.tsx similarity index 76% rename from web-console/src/visualization/visualization.spec.tsx rename to web-console/src/components/segment-timeline/bar-unit.spec.tsx index a879a73922c8..e8e62b5616b2 100644 --- a/web-console/src/visualization/visualization.spec.tsx +++ b/web-console/src/components/segment-timeline/bar-unit.spec.tsx @@ -20,10 +20,9 @@ import { render } from '@testing-library/react'; import React from 'react'; import { BarUnit } from './bar-unit'; -import { ChartAxis } from './chart-axis'; -describe('Visualization', () => { - it('BarUnit', () => { +describe('BarUnit', () => { + it('matches snapshot', () => { const barGroup = ( @@ -32,14 +31,4 @@ describe('Visualization', () => { const { container } = render(barGroup); expect(container.firstChild).toMatchSnapshot(); }); - - it('action barGroup', () => { - const barGroup = ( - - null} /> - - ); - const { container } = render(barGroup); - expect(container.firstChild).toMatchSnapshot(); - }); }); diff --git a/web-console/src/visualization/bar-unit.tsx b/web-console/src/components/segment-timeline/bar-unit.tsx similarity index 95% rename from web-console/src/visualization/bar-unit.tsx rename to web-console/src/components/segment-timeline/bar-unit.tsx index 4cb1fd8c6b19..8d783f67555a 100644 --- a/web-console/src/visualization/bar-unit.tsx +++ b/web-console/src/components/segment-timeline/bar-unit.tsx @@ -18,8 +18,6 @@ import React from 'react'; -import './bar-unit.scss'; - interface BarChartUnitProps { x: number | undefined; y: number; @@ -35,7 +33,7 @@ export function BarUnit(props: BarChartUnitProps) { const { x, y, width, height, style, onClick, onHover, offHover } = props; return ( { it('.getSqlQuery', () => { - expect(SegmentTimeline.getSqlQuery(3)).toEqual(sane` + expect( + SegmentTimeline.getSqlQuery( + new Date('2020-01-01T00:00:00'), + new Date('2021-02-01T02:03:04'), // Note: these are in local time, not ISO + ), + ).toEqual(sane` SELECT "start", "end", "datasource", COUNT(*) AS "count", SUM("size") AS "size" FROM sys.segments WHERE - "start" > TIME_FORMAT(TIMESTAMPADD(MONTH, -3, CURRENT_TIMESTAMP), 'yyyy-MM-dd''T''hh:mm:ss.SSS') AND + '2020-01-01' <= "start" AND + "end" < '2021-02-01' AND is_published = 1 AND is_overshadowed = 0 GROUP BY 1, 2, 3 @@ -43,72 +48,8 @@ describe('Segment Timeline', () => { }); it('matches snapshot', () => { - const segmentTimeline = ( - - ); + const segmentTimeline = ; const { container } = render(segmentTimeline); expect(container.firstChild).toMatchSnapshot(); }); - - it('queries 3 months of data by default', () => { - const dataQueryManager = new MockDataQueryManager(); - const segmentTimeline = ( - - ); - render(segmentTimeline); - - // Ideally, the test should verify the rendered bar graph to see if the bars - // cover the selected period. Since the unit test does not have a druid - // instance to query from, just verify the query has the correct time span. - expect(dataQueryManager.queryTimeSpan).toBe(3); - }); - - it('queries matching time span when new period is selected from dropdown', () => { - const dataQueryManager = new MockDataQueryManager(); - const segmentTimeline = ( - - ); - const wrapper = mount(segmentTimeline); - const selects = wrapper.find('select'); - expect(selects.length).toBe(2); // Datasource & Period - const periodSelect = selects.at(1); - const newTimeSpanMonths = 6; - periodSelect.simulate('change', { target: { value: newTimeSpanMonths } }); - - // Ideally, the test should verify the rendered bar graph to see if the bars - // cover the selected period. Since the unit test does not have a druid - // instance to query from, just verify the query has the correct time span. - expect(dataQueryManager.queryTimeSpan).toBe(newTimeSpanMonths); - }); }); - -/** - * Mock the data query manager, since the unit test does not have a druid instance - */ -class MockDataQueryManager extends QueryManager< - { capabilities: Capabilities; timeSpan: number }, - any -> { - queryTimeSpan?: number; - - constructor() { - super({ - // eslint-disable-next-line @typescript-eslint/require-await - processQuery: async ({ timeSpan }) => { - this.queryTimeSpan = timeSpan; - }, - debounceIdle: 0, - debounceLoading: 0, - }); - } -} diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx index ba48839f1bb3..0f8129dc8498 100644 --- a/web-console/src/components/segment-timeline/segment-timeline.tsx +++ b/web-console/src/components/segment-timeline/segment-timeline.tsx @@ -16,30 +16,43 @@ * limitations under the License. */ -import { FormGroup, HTMLSelect, Radio, RadioGroup } from '@blueprintjs/core'; +import { + FormGroup, + HTMLSelect, + IResizeEntry, + Radio, + RadioGroup, + ResizeSensor, +} from '@blueprintjs/core'; import { AxisScale } from 'd3-axis'; -import { scaleLinear, scaleTime } from 'd3-scale'; +import { scaleLinear, scaleUtc } from 'd3-scale'; import React from 'react'; import { Api } from '../../singletons'; import { Capabilities, formatBytes, queryDruidSql, QueryManager, uniq } from '../../utils'; -import { StackedBarChart } from '../../visualization/stacked-bar-chart'; +import { ceilToUtcDay } from '../../utils/date'; +import { DateRangeSelector } from '../date-range-selector/date-range-selector'; import { Loader } from '../loader/loader'; +import { BarUnitData, StackedBarChart } from './stacked-bar-chart'; + import './segment-timeline.scss'; interface SegmentTimelineProps { capabilities: Capabilities; - chartHeight: number; - chartWidth: number; // For testing: - dataQueryManager?: QueryManager<{ capabilities: Capabilities; timeSpan: number }, any>; + dataQueryManager?: QueryManager< + { capabilities: Capabilities; startDate: Date; endDate: Date }, + any + >; } type ActiveDataType = 'sizeData' | 'countData'; interface SegmentTimelineState { + chartHeight: number; + chartWidth: number; data?: Record; datasources: string[]; stackedData?: Record; @@ -47,13 +60,12 @@ interface SegmentTimelineState { activeDatasource: string | null; activeDataType: ActiveDataType; dataToRender: BarUnitData[]; - timeSpan: number; // by months loading: boolean; error?: Error; xScale: AxisScale | null; yScale: AxisScale | null; - dStart: Date; - dEnd: Date; + startDate: Date; + endDate: Date; } interface BarChartScales { @@ -61,22 +73,6 @@ interface BarChartScales { yScale: AxisScale; } -export interface BarUnitData { - x: number; - y: number; - y0?: number; - width: number; - datasource: string; - color: string; -} - -export interface BarChartMargin { - top: number; - right: number; - bottom: number; - left: number; -} - interface IntervalRow { start: string; end: string; @@ -114,14 +110,15 @@ export class SegmentTimeline extends React.PureComponent< return SegmentTimeline.COLORS[index % SegmentTimeline.COLORS.length]; } - static getSqlQuery(timeSpan: number): string { + static getSqlQuery(startDate: Date, endDate: Date): string { return `SELECT "start", "end", "datasource", COUNT(*) AS "count", SUM("size") AS "size" FROM sys.segments WHERE - "start" > TIME_FORMAT(TIMESTAMPADD(MONTH, -${timeSpan}, CURRENT_TIMESTAMP), 'yyyy-MM-dd''T''hh:mm:ss.SSS') AND + '${startDate.toISOString()}' <= "start" AND + "end" <= '${endDate.toISOString()}' AND is_published = 1 AND is_overshadowed = 0 GROUP BY 1, 2, 3 @@ -233,18 +230,21 @@ ORDER BY "start" DESC`; } private readonly dataQueryManager: QueryManager< - { capabilities: Capabilities; timeSpan: number }, + { capabilities: Capabilities; startDate: Date; endDate: Date }, any >; - private readonly chartMargin = { top: 20, right: 10, bottom: 20, left: 10 }; + private readonly chartMargin = { top: 40, right: 15, bottom: 20, left: 60 }; constructor(props: SegmentTimelineProps) { super(props); - const dStart = new Date(); - const dEnd = new Date(); - dStart.setMonth(dStart.getMonth() - DEFAULT_TIME_SPAN_MONTHS); + const startDate = ceilToUtcDay(new Date()); + const endDate = new Date(startDate.valueOf()); + startDate.setUTCMonth(startDate.getUTCMonth() - DEFAULT_TIME_SPAN_MONTHS); + this.state = { + chartWidth: 1, // Dummy init values to be replaced + chartHeight: 1, // after first render data: {}, datasources: [], stackedData: {}, @@ -252,27 +252,26 @@ ORDER BY "start" DESC`; dataToRender: [], activeDatasource: null, activeDataType: 'sizeData', - timeSpan: DEFAULT_TIME_SPAN_MONTHS, loading: true, xScale: null, yScale: null, - dEnd: dEnd, - dStart: dStart, + startDate, + endDate, }; this.dataQueryManager = props.dataQueryManager || new QueryManager({ - processQuery: async ({ capabilities, timeSpan }) => { + processQuery: async ({ capabilities, startDate, endDate }) => { let intervals: IntervalRow[]; let datasources: string[]; if (capabilities.hasSql()) { - intervals = await queryDruidSql({ query: SegmentTimeline.getSqlQuery(timeSpan) }); + intervals = await queryDruidSql({ + query: SegmentTimeline.getSqlQuery(startDate, endDate), + }); datasources = uniq(intervals.map(r => r.datasource)); } else if (capabilities.hasCoordinatorAccess()) { - const before = new Date(); - before.setMonth(before.getMonth() - timeSpan); - const beforeIso = before.toISOString(); + const startIso = startDate.toISOString(); datasources = (await Api.instance.get(`/druid/coordinator/v1/datasources`)).data; intervals = ( @@ -298,7 +297,7 @@ ORDER BY "start" DESC`; size, }; }) - .filter(a => beforeIso < a.start); + .filter(a => startIso < a.start); }), ) ) @@ -331,23 +330,23 @@ ORDER BY "start" DESC`; componentDidMount(): void { const { capabilities } = this.props; - const { timeSpan } = this.state; + const { startDate, endDate } = this.state; - this.dataQueryManager.runQuery({ capabilities, timeSpan }); + this.dataQueryManager.runQuery({ capabilities, startDate, endDate }); } componentWillUnmount(): void { this.dataQueryManager.terminate(); } - componentDidUpdate(prevProps: SegmentTimelineProps, prevState: SegmentTimelineState): void { + componentDidUpdate(_prevProps: SegmentTimelineProps, prevState: SegmentTimelineState): void { const { activeDatasource, activeDataType, singleDatasourceData, stackedData } = this.state; if ( prevState.data !== this.state.data || prevState.activeDataType !== this.state.activeDataType || prevState.activeDatasource !== this.state.activeDatasource || - prevProps.chartWidth !== this.props.chartWidth || - prevProps.chartHeight !== this.props.chartHeight + prevState.chartWidth !== this.state.chartWidth || + prevState.chartHeight !== this.state.chartHeight ) { const scales: BarChartScales | undefined = this.calculateScales(); const dataToRender: BarUnitData[] | undefined = activeDatasource @@ -369,18 +368,19 @@ ORDER BY "start" DESC`; } private calculateScales(): BarChartScales | undefined { - const { chartWidth, chartHeight } = this.props; const { + chartWidth, + chartHeight, data, activeDataType, activeDatasource, singleDatasourceData, - dStart, - dEnd, + startDate, + endDate, } = this.state; if (!data || !Object.keys(data).length) return; const activeData = data[activeDataType]; - const xDomain: Date[] = [dStart, dEnd]; + let yDomain: number[] = [ 0, activeData.length === 0 @@ -400,8 +400,8 @@ ORDER BY "start" DESC`; ]; } - const xScale: AxisScale = scaleTime() - .domain(xDomain) + const xScale: AxisScale = scaleUtc() + .domain([startDate, endDate]) .range([0, chartWidth - this.chartMargin.left - this.chartMargin.right]); const yScale: AxisScale = scaleLinear() @@ -414,22 +414,7 @@ ORDER BY "start" DESC`; }; } - onTimeSpanChange = (e: any) => { - const dStart = new Date(); - const dEnd = new Date(); - const capabilities = this.props.capabilities; - const timeSpan = parseInt(e, 10) || DEFAULT_TIME_SPAN_MONTHS; - dStart.setMonth(dStart.getMonth() - timeSpan); - this.setState({ - timeSpan: e, - loading: true, - dStart, - dEnd, - }); - this.dataQueryManager.runQuery({ capabilities, timeSpan }); - }; - - formatTick = (n: number) => { + private readonly formatTick = (n: number) => { const { activeDataType } = this.state; if (activeDataType === 'countData') { return n.toString(); @@ -438,9 +423,18 @@ ORDER BY "start" DESC`; } }; + private readonly handleResize = (entries: IResizeEntry[]) => { + const chartRect = entries[0].contentRect; + this.setState({ + chartWidth: chartRect.width, + chartHeight: chartRect.height, + }); + }; + renderStackedBarChart() { - const { chartWidth, chartHeight } = this.props; const { + chartWidth, + chartHeight, loading, dataToRender, activeDataType, @@ -449,9 +443,10 @@ ORDER BY "start" DESC`; yScale, data, activeDatasource, - dStart, - dEnd, + startDate, + endDate, } = this.state; + if (loading) { return (
@@ -498,30 +493,36 @@ ORDER BY "start" DESC`; } const millisecondsPerDay = 24 * 60 * 60 * 1000; - const barCounts = (dEnd.getTime() - dStart.getTime()) / millisecondsPerDay; - const barWidth = (chartWidth - this.chartMargin.left - this.chartMargin.right) / barCounts; + const barCounts = (endDate.getTime() - startDate.getTime()) / millisecondsPerDay; + const barWidth = Math.max( + 0, + (chartWidth - this.chartMargin.left - this.chartMargin.right) / barCounts, + ); return ( - - this.setState(prevState => ({ - activeDatasource: prevState.activeDatasource ? null : datasource, - })) - } - activeDataType={activeDataType} - formatTick={(n: number) => this.formatTick(n)} - xScale={xScale} - yScale={yScale} - barWidth={barWidth} - /> + + + this.setState(prevState => ({ + activeDatasource: prevState.activeDatasource ? null : datasource, + })) + } + activeDataType={activeDataType} + formatTick={(n: number) => this.formatTick(n)} + xScale={xScale} + yScale={yScale} + barWidth={barWidth} + /> + ); } render(): JSX.Element { - const { datasources, activeDataType, activeDatasource, timeSpan } = this.state; + const { capabilities } = this.props; + const { datasources, activeDataType, activeDatasource, startDate, endDate } = this.state; return (
@@ -537,7 +538,7 @@ ORDER BY "start" DESC`; - + this.setState({ @@ -558,18 +559,16 @@ ORDER BY "start" DESC`; - - this.onTimeSpanChange(e.target.value)} - value={timeSpan} - fill - > - - - - - - + + { + this.setState({ startDate, endDate }, () => { + this.dataQueryManager.runQuery({ capabilities, startDate, endDate }); + }); + }} + />
diff --git a/web-console/src/components/segment-timeline/stacked-bar-chart.scss b/web-console/src/components/segment-timeline/stacked-bar-chart.scss new file mode 100644 index 000000000000..26e5f5186b5f --- /dev/null +++ b/web-console/src/components/segment-timeline/stacked-bar-chart.scss @@ -0,0 +1,50 @@ +/* + * 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. + */ + +.stacked-bar-chart { + position: relative; + overflow: hidden; + + .bar-chart-tooltip { + position: absolute; + left: 100px; + right: 0; + + div { + display: inline-block; + width: 230px; + } + } + + svg { + position: absolute; + + .hovered-bar { + fill: transparent; + stroke: #ffffff; + stroke-width: 1.5px; + } + + .gridline-x { + line { + stroke-dasharray: 5, 5; + opacity: 0.5; + } + } + } +} diff --git a/web-console/src/visualization/stacked-bar-chart.tsx b/web-console/src/components/segment-timeline/stacked-bar-chart.tsx similarity index 54% rename from web-console/src/visualization/stacked-bar-chart.tsx rename to web-console/src/components/segment-timeline/stacked-bar-chart.tsx index c51146322317..7c772ef993fb 100644 --- a/web-console/src/visualization/stacked-bar-chart.tsx +++ b/web-console/src/components/segment-timeline/stacked-bar-chart.tsx @@ -19,13 +19,37 @@ import { axisBottom, axisLeft, AxisScale } from 'd3-axis'; import React, { useState } from 'react'; -import { BarChartMargin, BarUnitData } from '../components/segment-timeline/segment-timeline'; - import { BarGroup } from './bar-group'; import { ChartAxis } from './chart-axis'; import './stacked-bar-chart.scss'; +export interface BarUnitData { + x: number; + y: number; + y0?: number; + width: number; + datasource: string; + color: string; +} + +export interface BarChartMargin { + top: number; + right: number; + bottom: number; + left: number; +} + +export interface HoveredBarInfo { + xCoordinate?: number; + yCoordinate?: number; + height?: number; + width?: number; + datasource?: string; + xValue?: number; + yValue?: number; +} + interface StackedBarChartProps { svgWidth: number; svgHeight: number; @@ -39,21 +63,12 @@ interface StackedBarChartProps { barWidth: number; } -export interface HoveredBarInfo { - xCoordinate?: number; - yCoordinate?: number; - height?: number; - width?: number; - datasource?: string; - xValue?: number; - yValue?: number; -} - export const StackedBarChart = React.memo(function StackedBarChart(props: StackedBarChartProps) { const { activeDataType, svgWidth, svgHeight, + margin, formatTick, xScale, yScale, @@ -63,84 +78,87 @@ export const StackedBarChart = React.memo(function StackedBarChart(props: Stacke } = props; const [hoverOn, setHoverOn] = useState(); - const width = props.svgWidth - props.margin.left - props.margin.right; - const height = props.svgHeight - props.margin.bottom - props.margin.top; + const width = svgWidth - margin.left - margin.right; + const height = svgHeight - margin.top - margin.bottom; function renderBarChart() { return ( -
- + setHoverOn(undefined)} > '') .tickSizeOuter(0)} /> + setHoverOn(e)} + hoverOn={hoverOn} + barWidth={barWidth} + /> formatTick(e))} /> - setHoverOn(undefined)}> - setHoverOn(e)} - hoverOn={hoverOn} - barWidth={barWidth} - /> - {hoverOn && ( - { - setHoverOn(undefined); - changeActiveDatasource(hoverOn.datasource ?? null); - }} - > - - - )} - - -
+ {hoverOn && ( + { + setHoverOn(undefined); + changeActiveDatasource(hoverOn.datasource ?? null); + }} + > + + + )} + + ); } return ( -
-
-
Datasource: {hoverOn ? hoverOn.datasource : ''}
-
Time: {hoverOn ? hoverOn.xValue : ''}
-
- {`${activeDataType === 'countData' ? 'Count:' : 'Size:'} ${ - hoverOn ? formatTick(hoverOn.yValue!) : '' - }`} -
-
+
+ {hoverOn && ( + <> +
+
Datasource: {hoverOn.datasource}
+
Time: {hoverOn.xValue}
+
+ {`${activeDataType === 'countData' ? 'Count:' : 'Size:'} ${formatTick( + hoverOn.yValue!, + )}`} +
+
+ + )} {renderBarChart()}
); diff --git a/web-console/src/visualization/stacked-bar-chart.scss b/web-console/src/utils/date.spec.ts similarity index 78% rename from web-console/src/visualization/stacked-bar-chart.scss rename to web-console/src/utils/date.spec.ts index fed00ae31dd5..a9a6e6f1bc00 100644 --- a/web-console/src/visualization/stacked-bar-chart.scss +++ b/web-console/src/utils/date.spec.ts @@ -16,18 +16,10 @@ * limitations under the License. */ -.bar-chart { - .hovered-bar { - fill: transparent; - stroke: #ffffff; - stroke-width: 1.5px; - transform: translateX(65px); - } +import { dateToIsoDateString } from './date'; - .gridline-x { - line { - stroke-dasharray: 5, 5; - opacity: 0.5; - } - } -} +describe('date', () => { + describe('dateToIsoDateString', () => { + expect(dateToIsoDateString(new Date('2021-02-03T12:00:00Z'))).toEqual('2021-02-03'); + }); +}); diff --git a/web-console/src/utils/date.ts b/web-console/src/utils/date.ts new file mode 100644 index 000000000000..ab2ee07e9a44 --- /dev/null +++ b/web-console/src/utils/date.ts @@ -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 { DateRange } from '@blueprintjs/datetime'; + +const CURRENT_YEAR = new Date().getUTCFullYear(); + +export function dateToIsoDateString(date: Date): string { + return date.toISOString().substr(0, 10); +} + +export function utcToLocalDate(utcDate: Date): Date { + // Function removes the local timezone of the date and displays it in UTC + return new Date(utcDate.getTime() + utcDate.getTimezoneOffset() * 60000); +} + +export function localToUtcDate(localDate: Date): Date { + // Function removes the local timezone of the date and displays it in UTC + return new Date(localDate.getTime() - localDate.getTimezoneOffset() * 60000); +} + +export function localDateRangeToInterval(localRange: DateRange): string { + // This function takes in the dates selected from datepicker in local time, and displays them in UTC + // Shall Blueprint make any changes to the way dates are selected, this function will have to be reworked + const [localStartDate, localEndDate] = localRange; + return `${localStartDate ? localToUtcDate(localStartDate).toISOString().substring(0, 19) : ''}/${ + localEndDate ? localToUtcDate(localEndDate).toISOString().substring(0, 19) : '' + }`; +} + +export function intervalToLocalDateRange(interval: string): DateRange { + const dates = interval.split('/'); + if (dates.length !== 2) return [null, null]; + + const startDate = Date.parse(dates[0]) ? new Date(dates[0]) : null; + const endDate = Date.parse(dates[1]) ? new Date(dates[1]) : null; + + // Must check if the start and end dates are within range + return [ + startDate && startDate.getFullYear() < CURRENT_YEAR - 20 ? null : startDate, + endDate && endDate.getFullYear() > CURRENT_YEAR ? null : endDate, + ]; +} + +export function ceilToUtcDay(date: Date): Date { + date = new Date(date.valueOf()); + date.setUTCHours(0, 0, 0, 0); + date.setUTCDate(date.getUTCDate() + 1); + return date; +} diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/views/datasource-view/datasource-view.scss index 41d53c973246..287d0044fb13 100644 --- a/web-console/src/views/datasource-view/datasource-view.scss +++ b/web-console/src/views/datasource-view/datasource-view.scss @@ -34,7 +34,7 @@ } &.show-chart { - .chart-container { + .segment-timeline { height: calc(50% - 55px); margin-top: 10px; } diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx index 1d4d27df45db..99bfbd4178e8 100644 --- a/web-console/src/views/datasource-view/datasource-view.tsx +++ b/web-console/src/views/datasource-view/datasource-view.tsx @@ -252,9 +252,7 @@ export interface DatasourcesViewState { useUnuseInterval: string; showForceCompact: boolean; hiddenColumns: LocalStorageBackedArray; - showChart: boolean; - chartWidth: number; - chartHeight: number; + showSegmentTimeline: boolean; datasourceTableActionDialogId?: string; actions: BasicAction[]; @@ -356,9 +354,7 @@ ORDER BY 1`; hiddenColumns: new LocalStorageBackedArray( LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION, ), - showChart: false, - chartWidth: window.innerWidth * 0.85, - chartHeight: window.innerHeight * 0.4, + showSegmentTimeline: true, actions: [], }; @@ -482,13 +478,6 @@ ORDER BY 1`; }); } - private readonly handleResize = () => { - this.setState({ - chartWidth: window.innerWidth * 0.85, - chartHeight: window.innerHeight * 0.4, - }); - }; - private readonly refresh = (auto: any): void => { this.datasourceQueryManager.rerunLastQuery(auto); this.tiersQueryManager.rerunLastQuery(auto); @@ -504,7 +493,6 @@ ORDER BY 1`; const { capabilities } = this.props; this.fetchDatasourceData(); this.tiersQueryManager.runQuery(capabilities); - window.addEventListener('resize', this.handleResize); } componentWillUnmount(): void { @@ -1399,16 +1387,17 @@ ORDER BY 1`; const { showUnused, hiddenColumns, - showChart, - chartHeight, - chartWidth, + showSegmentTimeline, datasourceTableActionDialogId, actions, } = this.state; return (
{this.renderBulkDatasourceActions()} - this.setState({ showChart: !showChart })} - disabled={!capabilities.hasSqlOrCoordinatorAccess()} - /> this.toggleUnused(showUnused)} disabled={!capabilities.hasCoordinatorAccess()} /> + this.setState({ showSegmentTimeline: !showSegmentTimeline })} + disabled={!capabilities.hasSqlOrCoordinatorAccess()} + /> @@ -1444,15 +1433,7 @@ ORDER BY 1`; tableColumnsHidden={hiddenColumns.storedArray} /> - {showChart && ( -
- -
- )} + {showSegmentTimeline && } {this.renderDatasourceTable()} {datasourceTableActionDialogId && ( - - -`; - -exports[`Visualization action barGroup 1`] = ` - - - -`; From 3619be7bd47abca2d7041922e84ba6005dda7e6c Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Mon, 7 Jun 2021 22:48:10 -0700 Subject: [PATCH 2/8] add to segments view --- .../datasource-view/datasource-view.scss | 9 ++------- .../views/datasource-view/datasource-view.tsx | 7 +++---- .../views/segments-view/segments-view.scss | 11 ++++++++++ .../src/views/segments-view/segments-view.tsx | 20 +++++++++++++++++-- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/web-console/src/views/datasource-view/datasource-view.scss b/web-console/src/views/datasource-view/datasource-view.scss index 287d0044fb13..b141d64b05e5 100644 --- a/web-console/src/views/datasource-view/datasource-view.scss +++ b/web-console/src/views/datasource-view/datasource-view.scss @@ -29,11 +29,12 @@ .ReactTable { position: absolute; + top: $view-control-bar-height + $standard-padding; bottom: 0; width: 100%; } - &.show-chart { + &.show-segment-timeline { .segment-timeline { height: calc(50% - 55px); margin-top: 10px; @@ -43,10 +44,4 @@ top: 50%; } } - - &.no-chart { - .ReactTable { - top: $view-control-bar-height + $standard-padding; - } - } } diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx index 99bfbd4178e8..eef9ff2e9f52 100644 --- a/web-console/src/views/datasource-view/datasource-view.tsx +++ b/web-console/src/views/datasource-view/datasource-view.tsx @@ -1394,10 +1394,9 @@ ORDER BY 1`; return (
; groupByInterval: boolean; + showSegmentTimeline: boolean; } export class SegmentsView extends React.PureComponent { @@ -251,6 +254,7 @@ END AS "partitioning"`, LocalStorageKeys.SEGMENT_TABLE_COLUMN_SELECTION, ), groupByInterval: false, + showSegmentTimeline: false, }; this.segmentsQueryManager = new QueryManager({ @@ -745,13 +749,18 @@ END AS "partitioning"`, datasourceTableActionDialogId, actions, hiddenColumns, + showSegmentTimeline, } = this.state; const { capabilities } = this.props; const { groupByInterval } = this.state; return ( <> -
+
this.segmentsQueryManager.rerunLastQuery(auto)} @@ -779,6 +788,12 @@ END AS "partitioning"`, {this.renderBulkSegmentsActions()} + this.setState({ showSegmentTimeline: !showSegmentTimeline })} + disabled={!capabilities.hasSqlOrCoordinatorAccess()} + /> @@ -793,6 +808,7 @@ END AS "partitioning"`, tableColumnsHidden={hiddenColumns.storedArray} /> + {showSegmentTimeline && } {this.renderSegmentsTable()}
{this.renderTerminateSegmentAction()} From 59cced9c12cd3f65299ff8e1f0ec9b67750cf982 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Tue, 8 Jun 2021 14:46:16 -0700 Subject: [PATCH 3/8] add unit tests for date --- web-console/src/utils/date.spec.ts | 50 ++++++++++++++++++++++++++++-- web-console/src/utils/date.ts | 18 +++++------ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/web-console/src/utils/date.spec.ts b/web-console/src/utils/date.spec.ts index a9a6e6f1bc00..843c144244ef 100644 --- a/web-console/src/utils/date.spec.ts +++ b/web-console/src/utils/date.spec.ts @@ -16,10 +16,56 @@ * limitations under the License. */ -import { dateToIsoDateString } from './date'; +import { + ceilToUtcDay, + dateToIsoDateString, + intervalToLocalDateRange, + localDateRangeToInterval, + localToUtcDate, + utcToLocalDate, +} from './date'; describe('date', () => { describe('dateToIsoDateString', () => { - expect(dateToIsoDateString(new Date('2021-02-03T12:00:00Z'))).toEqual('2021-02-03'); + it('works', () => { + expect(dateToIsoDateString(new Date('2021-02-03T12:00:00Z'))).toEqual('2021-02-03'); + }); + }); + + describe('utcToLocalDate / localToUtcDate', () => { + it('works', () => { + const date = new Date('2021-02-03T12:00:00Z'); + + expect(localToUtcDate(utcToLocalDate(date))).toEqual(date); + expect(utcToLocalDate(localToUtcDate(date))).toEqual(date); + }); + }); + + describe('intervalToLocalDateRange / localDateRangeToInterval', () => { + it('works with full interval', () => { + const interval = '2021-02-03T12:00:00/2021-03-03T12:00:00'; + + expect(localDateRangeToInterval(intervalToLocalDateRange(interval))).toEqual(interval); + }); + + it('works with start only', () => { + const interval = '2021-02-03T12:00:00/'; + + expect(localDateRangeToInterval(intervalToLocalDateRange(interval))).toEqual(interval); + }); + + it('works with end only', () => { + const interval = '/2021-02-03T12:00:00'; + + expect(localDateRangeToInterval(intervalToLocalDateRange(interval))).toEqual(interval); + }); + }); + + describe('ceilToUtcDay', () => { + it('works', () => { + expect(ceilToUtcDay(new Date('2021-02-03T12:03:02.001Z'))).toEqual( + new Date('2021-02-04T00:00:00Z'), + ); + }); }); }); diff --git a/web-console/src/utils/date.ts b/web-console/src/utils/date.ts index ab2ee07e9a44..be548f7d6560 100644 --- a/web-console/src/utils/date.ts +++ b/web-console/src/utils/date.ts @@ -34,15 +34,6 @@ export function localToUtcDate(localDate: Date): Date { return new Date(localDate.getTime() - localDate.getTimezoneOffset() * 60000); } -export function localDateRangeToInterval(localRange: DateRange): string { - // This function takes in the dates selected from datepicker in local time, and displays them in UTC - // Shall Blueprint make any changes to the way dates are selected, this function will have to be reworked - const [localStartDate, localEndDate] = localRange; - return `${localStartDate ? localToUtcDate(localStartDate).toISOString().substring(0, 19) : ''}/${ - localEndDate ? localToUtcDate(localEndDate).toISOString().substring(0, 19) : '' - }`; -} - export function intervalToLocalDateRange(interval: string): DateRange { const dates = interval.split('/'); if (dates.length !== 2) return [null, null]; @@ -57,6 +48,15 @@ export function intervalToLocalDateRange(interval: string): DateRange { ]; } +export function localDateRangeToInterval(localRange: DateRange): string { + // This function takes in the dates selected from datepicker in local time, and displays them in UTC + // Shall Blueprint make any changes to the way dates are selected, this function will have to be reworked + const [localStartDate, localEndDate] = localRange; + return `${localStartDate ? localToUtcDate(localStartDate).toISOString().substring(0, 19) : ''}/${ + localEndDate ? localToUtcDate(localEndDate).toISOString().substring(0, 19) : '' + }`; +} + export function ceilToUtcDay(date: Date): Date { date = new Date(date.valueOf()); date.setUTCHours(0, 0, 0, 0); From 30a4b500a1966ed2ff40c68b1d6435d468b03d22 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Tue, 8 Jun 2021 14:48:09 -0700 Subject: [PATCH 4/8] better util export --- .../date-range-selector/date-range-selector.tsx | 2 +- .../src/components/interval-input/interval-input.tsx | 2 +- .../components/segment-timeline/segment-timeline.tsx | 10 ++++++++-- web-console/src/utils/index.tsx | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/web-console/src/components/date-range-selector/date-range-selector.tsx b/web-console/src/components/date-range-selector/date-range-selector.tsx index c06040383b66..df0a5dc81770 100644 --- a/web-console/src/components/date-range-selector/date-range-selector.tsx +++ b/web-console/src/components/date-range-selector/date-range-selector.tsx @@ -21,7 +21,7 @@ import { DateRange, DateRangePicker } from '@blueprintjs/datetime'; import { IconNames } from '@blueprintjs/icons'; import React, { useState } from 'react'; -import { dateToIsoDateString, localToUtcDate, utcToLocalDate } from '../../utils/date'; +import { dateToIsoDateString, localToUtcDate, utcToLocalDate } from '../../utils'; import './date-range-selector.scss'; diff --git a/web-console/src/components/interval-input/interval-input.tsx b/web-console/src/components/interval-input/interval-input.tsx index 7944f1c4acf0..138c60d57a9e 100644 --- a/web-console/src/components/interval-input/interval-input.tsx +++ b/web-console/src/components/interval-input/interval-input.tsx @@ -21,7 +21,7 @@ import { DateRange, DateRangePicker, TimePrecision } from '@blueprintjs/datetime import { IconNames } from '@blueprintjs/icons'; import React from 'react'; -import { intervalToLocalDateRange, localDateRangeToInterval } from '../../utils/date'; +import { intervalToLocalDateRange, localDateRangeToInterval } from '../../utils'; import './interval-input.scss'; diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx index 0f8129dc8498..cc6aa4912cd3 100644 --- a/web-console/src/components/segment-timeline/segment-timeline.tsx +++ b/web-console/src/components/segment-timeline/segment-timeline.tsx @@ -29,8 +29,14 @@ import { scaleLinear, scaleUtc } from 'd3-scale'; import React from 'react'; import { Api } from '../../singletons'; -import { Capabilities, formatBytes, queryDruidSql, QueryManager, uniq } from '../../utils'; -import { ceilToUtcDay } from '../../utils/date'; +import { + Capabilities, + ceilToUtcDay, + formatBytes, + queryDruidSql, + QueryManager, + uniq, +} from '../../utils'; import { DateRangeSelector } from '../date-range-selector/date-range-selector'; import { Loader } from '../loader/loader'; diff --git a/web-console/src/utils/index.tsx b/web-console/src/utils/index.tsx index a47fc2c59e5a..0b40e734d9f6 100644 --- a/web-console/src/utils/index.tsx +++ b/web-console/src/utils/index.tsx @@ -18,6 +18,7 @@ export * from './capabilities'; export * from './column-metadata'; +export * from './date'; export * from './druid-lookup'; export * from './druid-query'; export * from './general'; From 4c501441f4614f5f13df73f3e55498e0ce2b5a52 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Tue, 8 Jun 2021 14:51:31 -0700 Subject: [PATCH 5/8] fix ds view --- web-console/src/views/datasource-view/datasource-view.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx index eef9ff2e9f52..2901733e1648 100644 --- a/web-console/src/views/datasource-view/datasource-view.tsx +++ b/web-console/src/views/datasource-view/datasource-view.tsx @@ -354,7 +354,7 @@ ORDER BY 1`; hiddenColumns: new LocalStorageBackedArray( LocalStorageKeys.DATASOURCE_TABLE_COLUMN_SELECTION, ), - showSegmentTimeline: true, + showSegmentTimeline: false, actions: [], }; From fb90e9b641c17340579a4277ef087739912e81ea Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Thu, 10 Jun 2021 16:51:13 -0700 Subject: [PATCH 6/8] fix tests --- .../__snapshots__/bar-unit.spec.tsx.snap | 13 +++ .../segment-timeline.spec.tsx.snap | 89 +++++++++---------- .../segment-timeline.spec.tsx | 8 +- .../segment-timeline/segment-timeline.tsx | 2 +- .../datasource-view.spec.tsx.snap | 6 +- .../__snapshots__/segments-view.spec.tsx.snap | 6 ++ 6 files changed, 70 insertions(+), 54 deletions(-) create mode 100644 web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap diff --git a/web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap b/web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap new file mode 100644 index 000000000000..7d98145d4b5e --- /dev/null +++ b/web-console/src/components/segment-timeline/__snapshots__/bar-unit.spec.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`BarUnit matches snapshot 1`] = ` + + + +`; diff --git a/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap b/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap index c7a96a16d9ff..40e6e87e50a9 100644 --- a/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap +++ b/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap @@ -85,7 +85,7 @@ exports[`Segment Timeline matches snapshot 1`] = `
+
diff --git a/web-console/src/components/segment-timeline/segment-timeline.spec.tsx b/web-console/src/components/segment-timeline/segment-timeline.spec.tsx index 6d58cd91d9ca..75f7c49d5f47 100644 --- a/web-console/src/components/segment-timeline/segment-timeline.spec.tsx +++ b/web-console/src/components/segment-timeline/segment-timeline.spec.tsx @@ -28,8 +28,8 @@ describe('Segment Timeline', () => { it('.getSqlQuery', () => { expect( SegmentTimeline.getSqlQuery( - new Date('2020-01-01T00:00:00'), - new Date('2021-02-01T02:03:04'), // Note: these are in local time, not ISO + new Date('2020-01-01T00:00:00Z'), + new Date('2021-02-01T00:00:00Z'), ), ).toEqual(sane` SELECT @@ -38,8 +38,8 @@ describe('Segment Timeline', () => { SUM("size") AS "size" FROM sys.segments WHERE - '2020-01-01' <= "start" AND - "end" < '2021-02-01' AND + '2020-01-01T00:00:00.000Z' <= "start" AND + "end" <= '2021-02-01T00:00:00.000Z' AND is_published = 1 AND is_overshadowed = 0 GROUP BY 1, 2, 3 diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx index cc6aa4912cd3..8b771d585a8e 100644 --- a/web-console/src/components/segment-timeline/segment-timeline.tsx +++ b/web-console/src/components/segment-timeline/segment-timeline.tsx @@ -124,7 +124,7 @@ export class SegmentTimeline extends React.PureComponent< FROM sys.segments WHERE '${startDate.toISOString()}' <= "start" AND - "end" <= '${endDate.toISOString()}' AND + "end" <= '${endDate.toISOString()}' AND is_published = 1 AND is_overshadowed = 0 GROUP BY 1, 2, 3 diff --git a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap index 866ea3dd750d..2a1727c6ad94 100755 --- a/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap +++ b/web-console/src/views/datasource-view/__snapshots__/datasource-view.spec.tsx.snap @@ -2,7 +2,7 @@ exports[`data source view matches snapshot 1`] = `
+ Date: Thu, 17 Jun 2021 10:14:18 -0700 Subject: [PATCH 7/8] fix test in time --- .../__snapshots__/segment-timeline.spec.tsx.snap | 4 ++-- .../src/components/segment-timeline/segment-timeline.spec.tsx | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap b/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap index 40e6e87e50a9..5f76670f14e0 100644 --- a/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap +++ b/web-console/src/components/segment-timeline/__snapshots__/segment-timeline.spec.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Segment Timeline matches snapshot 1`] = ` +exports[`SegmentTimeline matches snapshot 1`] = `
@@ -155,7 +155,7 @@ exports[`Segment Timeline matches snapshot 1`] = ` readonly="" style="padding-right: 0px;" type="text" - value="2021-03-11 ➔ 2021-06-11" + value="2021-03-09 ➔ 2021-06-09" /> { +jest.useFakeTimers('modern').setSystemTime(Date.parse('2021-06-08T12:34:56Z')); + +describe('SegmentTimeline', () => { it('.getSqlQuery', () => { expect( SegmentTimeline.getSqlQuery( From 33d3df74e6981dc316c1b37b2dc14e2031221833 Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Thu, 17 Jun 2021 10:17:46 -0700 Subject: [PATCH 8/8] unset untermediate state --- .../components/date-range-selector/date-range-selector.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web-console/src/components/date-range-selector/date-range-selector.tsx b/web-console/src/components/date-range-selector/date-range-selector.tsx index df0a5dc81770..b58daecb3075 100644 --- a/web-console/src/components/date-range-selector/date-range-selector.tsx +++ b/web-console/src/components/date-range-selector/date-range-selector.tsx @@ -49,9 +49,10 @@ export const DateRangeSelector = React.memo(function DateRangeSelector( const [startDate, endDate] = selectedRange; if (!startDate || !endDate) { setIntermediateDateRange(selectedRange); - return; + } else { + setIntermediateDateRange(undefined); + onChange(localToUtcDate(startDate), localToUtcDate(endDate)); } - onChange(localToUtcDate(startDate), localToUtcDate(endDate)); }} /> }