diff --git a/CHANGELOG.md b/CHANGELOG.md index 654201a06..bff2892e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ 1. [4846](https://github.com/influxdata/chronograf/pull/4846): Fix missing data and type in refreshing graph 1. [4861](https://github.com/influxdata/chronograf/pull/4861): Fix logs stuck in loading state 1. [4847](https://github.com/influxdata/chronograf/pull/4847): Improve display of Flux Wizard on small screens +1. [4863](https://github.com/influxdata/chronograf/pull/4863): Update logs histogram data on click and new search ### UI Improvements 1. [#4809](https://github.com/influxdata/chronograf/pull/4809): Add loading spinners while fetching protoboards diff --git a/ui/src/localStorage.ts b/ui/src/localStorage.ts index e02fe2757..e5872ed5e 100644 --- a/ui/src/localStorage.ts +++ b/ui/src/localStorage.ts @@ -57,6 +57,18 @@ export const saveToLocalStorage = ({ 'histogramData', 'queryCount', 'tableInfiniteData', + 'newRowsAdded', + 'searchStatus', + 'queryCount', + 'nextOlderUpperBound', + 'nextOlderLowerBound', + 'nextNewerUpperBound', + 'nextNewerLowerBound', + 'currentTailUpperBound', + 'nextTailLowerBound', + 'tailChunkDurationMs', + 'olderChunkDurationMs', + 'newerChunkDurationMs', ]) window.localStorage.setItem( diff --git a/ui/src/logs/components/LogsTable.tsx b/ui/src/logs/components/LogsTable.tsx index 2c589aea5..789f981a5 100644 --- a/ui/src/logs/components/LogsTable.tsx +++ b/ui/src/logs/components/LogsTable.tsx @@ -52,6 +52,7 @@ import { import {INITIAL_LIMIT} from 'src/logs/actions' interface Props { + queryCount: number filters: Filter[] data: TableData isTruncated: boolean @@ -86,7 +87,6 @@ interface State { isMessageVisible: boolean visibleColumnsCount: number searchPattern: string - infiniteLoaderQueryCount: number } const calculateScrollTop = scrollToRow => { @@ -168,7 +168,6 @@ class LogsTable extends Component { scrollTop: 0, scrollLeft: 0, currentMessageWidth: 0, - infiniteLoaderQueryCount: 0, isMessageVisible, visibleColumnsCount, } @@ -302,6 +301,7 @@ class LogsTable extends Component { onSectionRendered: this.handleRowRender(onRowsRendered), columnCount, columnWidth: this.getColumnWidth, + overscanRowCount: 50, ref: (ref: Grid) => { registerChild(ref) this.grid = ref @@ -353,33 +353,11 @@ class LogsTable extends Component { } private loadMoreAboveRows = async () => { - try { - this.incrementLoaderQueryCount() - await this.props.fetchNewer() - } finally { - this.decrementLoaderQueryCount() - } + await this.props.fetchNewer() } private loadMoreBelowRows = async () => { - try { - this.incrementLoaderQueryCount() - await this.props.fetchMore() - } finally { - this.decrementLoaderQueryCount() - } - } - - private incrementLoaderQueryCount() { - this.setState(({infiniteLoaderQueryCount}) => ({ - infiniteLoaderQueryCount: infiniteLoaderQueryCount + 1, - })) - } - - private decrementLoaderQueryCount() { - this.setState(({infiniteLoaderQueryCount}) => ({ - infiniteLoaderQueryCount: infiniteLoaderQueryCount - 1, - })) + await this.props.fetchMore() } private rowCount = (): number => { @@ -664,7 +642,7 @@ class LogsTable extends Component { } private get isLoadingMore(): boolean { - return this.state.infiniteLoaderQueryCount > 0 + return this.props.queryCount > 0 } private get scrollLoadingIndicator(): JSX.Element { diff --git a/ui/src/logs/containers/LogsPage.tsx b/ui/src/logs/containers/LogsPage.tsx index 0a31a23bf..91badf4c3 100644 --- a/ui/src/logs/containers/LogsPage.tsx +++ b/ui/src/logs/containers/LogsPage.tsx @@ -27,6 +27,7 @@ import {colorForSeverity} from 'src/logs/utils/colors' import { applyChangesToTableData, isEmptyInfiniteData, + findTimeOptionRow, } from 'src/logs/utils/table' import extentBy from 'src/utils/extentBy' import {computeTimeBounds} from 'src/logs/utils/timeBounds' @@ -160,6 +161,7 @@ interface State { histogramColors: HistogramColor[] hasScrolled: boolean isLoadingNewer: boolean + queryCount: number } class LogsPage extends Component { @@ -192,6 +194,7 @@ class LogsPage extends Component { isOverlayVisible: false, histogramColors: [], hasScrolled: false, + queryCount: 0, } } @@ -279,6 +282,7 @@ class LogsPage extends Component { isTruncated={this.isTruncated} /> { chunkOptions ) + this.updateQueryCount() + try { await this.currentNewerChunksGenerator.promise } catch (error) { @@ -449,6 +455,7 @@ class LogsPage extends Component { this.setState({isLoadingNewer: false}) this.currentNewerChunksGenerator = null + this.updateQueryCount() } private startFetchingOlder = async () => { @@ -462,6 +469,8 @@ class LogsPage extends Component { chunkOptions ) + this.updateQueryCount() + try { await this.currentOlderChunksGenerator.promise } catch (error) { @@ -469,6 +478,7 @@ class LogsPage extends Component { } this.currentOlderChunksGenerator = null + this.updateQueryCount() } private clearTailInterval = () => { @@ -491,14 +501,20 @@ class LogsPage extends Component { this.currentNewerChunksGenerator = null this.currentOlderChunksGenerator = null + this.setState({queryCount: 0}) } private get tableScrollToRow() { + const { + timeRange: {timeOption}, + } = this.props if (this.isLiveUpdating === true && !this.state.hasScrolled) { return 0 } - if (this.state.isLoadingNewer && this.props.newRowsAdded) { + if (!this.isLiveUpdating && timeOption && !this.state.hasScrolled) { + return findTimeOptionRow(timeOption, this.props.tableInfiniteData, 0) + } else if (this.state.isLoadingNewer && this.props.newRowsAdded) { return this.props.newRowsAdded || 0 } @@ -902,6 +918,9 @@ class LogsPage extends Component { private fetchNewDataset = async () => { if (this.isLiveUpdating && this.shouldLiveUpdate) { this.startLogsTailFetchingInterval() + } else if (!this.shouldLiveUpdate) { + this.props.executeHistogramQueryAsync() + this.handleFetchNewerChunk() } await this.handleFetchOlderChunk() @@ -1032,6 +1051,17 @@ class LogsPage extends Component { private get isMeasurementInNamespace(): boolean { return this.props.searchStatus !== SearchStatus.MeasurementMissing } + + private updateQueryCount() { + this.setState({queryCount: this.countCurrentQueries()}) + } + + private countCurrentQueries(): number { + return _.compact([ + this.currentNewerChunksGenerator, + this.currentOlderChunksGenerator, + ]).length + } } const mapStateToProps = ({ diff --git a/ui/src/logs/utils/table.ts b/ui/src/logs/utils/table.ts index 452b6ff29..af3c53803 100644 --- a/ui/src/logs/utils/table.ts +++ b/ui/src/logs/utils/table.ts @@ -197,3 +197,24 @@ export const isEmptyInfiniteData = (data: { const isEmptyTableData = (data: TableData): boolean => { return getDeep(data, 'values.length', 0) === 0 } + +export const findTimeOptionRow = ( + timeOption: string, + data: { + forward: TableData + backward: TableData + }, + defaultIndex: number = 0 +): number => { + const {forward, backward} = data + const selectedTime = new Date(timeOption).valueOf() + const timeColumn = forward.columns.indexOf('time') + const tableData = [...forward.values, ...backward.values] + const rowIndex = tableData.findIndex(row => row[timeColumn] <= selectedTime) + + if (rowIndex < 0) { + return defaultIndex + } + + return rowIndex +} diff --git a/ui/src/logs/utils/timeBounds.ts b/ui/src/logs/utils/timeBounds.ts index fc7f489cd..5b9dc6d6d 100644 --- a/ui/src/logs/utils/timeBounds.ts +++ b/ui/src/logs/utils/timeBounds.ts @@ -17,15 +17,19 @@ export const computeTimeBounds = ( const period = seconds * SECONDS_TO_MS const [lowerExtent] = extentTimes - if (!isValidExtent(extentTimes, period)) { + if (!isValidExtent(numberTimeOption, extentTimes, period)) { return centerTimeBounds(numberTimeOption, period) } else { return offsetTimeBounds(lowerExtent, numberTimeOption, period) } } -const isValidExtent = ([t0, t1]: number[], period: number): boolean => { - return t1 - t0 < period +export const isValidExtent = ( + numberTimeOption: number, + [t0, t1]: number[], + period: number +): boolean => { + return t1 - t0 < period && t0 <= numberTimeOption && t1 >= numberTimeOption } const centerTimeBounds = (center: number, period: number): TimeBounds => { diff --git a/ui/src/shared/components/HistogramChartAxes.tsx b/ui/src/shared/components/HistogramChartAxes.tsx index 346352c02..955c30635 100644 --- a/ui/src/shared/components/HistogramChartAxes.tsx +++ b/ui/src/shared/components/HistogramChartAxes.tsx @@ -53,9 +53,8 @@ class HistogramChartAxes extends PureComponent { private get xTickData() { const {margins, xScale, width, height} = this.props - - const y = height - margins.bottom + X_TICK_PADDING_TOP const formatTime = xScale.tickFormat() + const y = height - margins.bottom + X_TICK_PADDING_TOP return xScale .ticks(X_TICK_COUNT) diff --git a/ui/src/shared/components/HistogramChartBars.tsx b/ui/src/shared/components/HistogramChartBars.tsx index f6903b179..c8e8c16c1 100644 --- a/ui/src/shared/components/HistogramChartBars.tsx +++ b/ui/src/shared/components/HistogramChartBars.tsx @@ -77,6 +77,7 @@ const getBarGroups = ({ return timeGroups.map(timeGroup => { const time = timeGroup[0].time + const x = xScale(time) - barWidth / 2 const total = _.sumBy(timeGroup, 'value') diff --git a/ui/test/logs/utils/table.test.ts b/ui/test/logs/utils/table.test.ts new file mode 100644 index 000000000..dee19a1ce --- /dev/null +++ b/ui/test/logs/utils/table.test.ts @@ -0,0 +1,22 @@ +import {findTimeOptionRow} from 'src/logs/utils/table' + +describe('Logs.Utils.Table', () => { + const infiniteTableData = { + forward: { + columns: ['time', 'noise'], + values: [[8, 'beep'], [7, 'boop']], + }, + backward: { + columns: ['time', 'noise'], + values: [[6, 'bloop'], [5, 'bleep']], + }, + } + + it('can find the most recent row index', () => { + const timeOption = new Date(6).toISOString() + + const actual = findTimeOptionRow(timeOption, infiniteTableData, 0) + + expect(actual).toEqual(2) + }) +}) diff --git a/ui/test/logs/utils/timeBounds.test.ts b/ui/test/logs/utils/timeBounds.test.ts new file mode 100644 index 000000000..8c903b710 --- /dev/null +++ b/ui/test/logs/utils/timeBounds.test.ts @@ -0,0 +1,36 @@ +import {isValidExtent} from 'src/logs/utils/timeBounds' + +describe('Logs.Utils.TimeBounds', () => { + describe('isValidExtent', () => { + const extents = [1, 3] + const period = 4 + + it('can invalidate a timeOption less than the extent', () => { + const timeOption = 0 + const actual = isValidExtent(timeOption, extents, period) + + expect(actual).toEqual(false) + }) + + it('can invalidate a timeOption greater than the extent', () => { + const timeOption = 4 + const actual = isValidExtent(timeOption, extents, period) + + expect(actual).toEqual(false) + }) + + it('can validate an in bounds timeOption', () => { + const timeOption = 2 + const actual = isValidExtent(timeOption, extents, period) + + expect(actual).toEqual(true) + }) + + it('can invalidate an extent larger than the period', () => { + const timeOption = 2 + const actual = isValidExtent(timeOption, [0, 200], 1) + + expect(actual).toEqual(false) + }) + }) +})