From 31090a75eb86cdfc39a00a65f39ae303eeabe0be Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 21 Feb 2022 06:09:43 -0600 Subject: [PATCH] Explore: Save log results sort order (#45423) * Explore: Save log results sort order * Change to radio group, add test and make language consistent with logs plugin --- public/app/features/explore/Logs.test.tsx | 87 +++++++++++++++++++++++ public/app/features/explore/Logs.tsx | 44 +++++++----- public/app/plugins/panel/logs/module.tsx | 4 +- 3 files changed, 116 insertions(+), 19 deletions(-) create mode 100644 public/app/features/explore/Logs.test.tsx diff --git a/public/app/features/explore/Logs.test.tsx b/public/app/features/explore/Logs.test.tsx new file mode 100644 index 00000000000..82d32ffbb29 --- /dev/null +++ b/public/app/features/explore/Logs.test.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { LoadingState, LogLevel, LogRowModel, MutableDataFrame, toUtc } from '@grafana/data'; + +import { Logs } from './Logs'; + +describe('Logs', () => { + const setup = (propOverrides?: object) => { + const rows = [ + makeLog({ uid: '1', timeEpochMs: 1 }), + makeLog({ uid: '2', timeEpochMs: 2 }), + makeLog({ uid: '3', timeEpochMs: 3 }), + ]; + + return render( + {}} + onChangeTime={() => {}} + clearCache={() => {}} + getFieldLinks={() => { + return []; + }} + /> + ); + }; + + beforeEach(() => { + jest.useFakeTimers('modern'); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + + it('should render logs', () => { + setup(); + const logsSection = screen.getByTestId('logRows'); + let logRows = logsSection.querySelectorAll('tr'); + expect(logRows.length).toBe(3); + expect(logRows[0].textContent).toContain('log message 3'); + expect(logRows[2].textContent).toContain('log message 1'); + }); + + it('should flip the order', () => { + setup(); + const oldestFirstSelection = screen.getByLabelText('Oldest first'); + fireEvent.click(oldestFirstSelection); + jest.advanceTimersByTime(1000); + const logsSection = screen.getByTestId('logRows'); + let logRows = logsSection.querySelectorAll('tr'); + expect(logRows.length).toBe(3); + expect(logRows[0].textContent).toContain('log message 1'); + expect(logRows[2].textContent).toContain('log message 3'); + }); +}); + +const makeLog = (overrides: Partial): LogRowModel => { + const uid = overrides.uid || '1'; + const entry = `log message ${uid}`; + return { + uid, + entryFieldIndex: 0, + rowIndex: 0, + dataFrame: new MutableDataFrame(), + logLevel: LogLevel.debug, + entry, + hasAnsi: false, + hasUnescapedContent: false, + labels: {}, + raw: entry, + timeFromNow: '', + timeEpochMs: 1, + timeEpochNs: '1000000', + timeLocal: '', + timeUtc: '', + ...overrides, + }; +}; diff --git a/public/app/features/explore/Logs.tsx b/public/app/features/explore/Logs.tsx index 9d06682c351..1d6d28b051d 100644 --- a/public/app/features/explore/Logs.tsx +++ b/public/app/features/explore/Logs.tsx @@ -43,6 +43,7 @@ const SETTINGS_KEYS = { showTime: 'grafana.explore.logs.showTime', wrapLogMessage: 'grafana.explore.logs.wrapLogMessage', prettifyLogMessage: 'grafana.explore.logs.prettifyLogMessage', + logsSortOrder: 'grafana.explore.logs.sortOrder', }; interface Props extends Themeable2 { @@ -96,7 +97,7 @@ class UnthemedLogs extends PureComponent { prettifyLogMessage: store.getBool(SETTINGS_KEYS.prettifyLogMessage, false), dedupStrategy: LogsDedupStrategy.none, hiddenLogLevels: [], - logsSortOrder: null, + logsSortOrder: store.get(SETTINGS_KEYS.logsSortOrder) || LogsSortOrder.Descending, isFlipping: false, showDetectedFields: [], forceEscape: false, @@ -117,10 +118,10 @@ class UnthemedLogs extends PureComponent { // we are using setTimeout here to make sure that disabled button is rendered before the rendering of reordered logs this.flipOrderTimer = window.setTimeout(() => { this.setState((prevState) => { - if (prevState.logsSortOrder === null || prevState.logsSortOrder === LogsSortOrder.Descending) { - return { logsSortOrder: LogsSortOrder.Ascending }; - } - return { logsSortOrder: LogsSortOrder.Descending }; + const newSortOrder = + prevState.logsSortOrder === LogsSortOrder.Descending ? LogsSortOrder.Ascending : LogsSortOrder.Descending; + store.set(SETTINGS_KEYS.logsSortOrder, newSortOrder); + return { logsSortOrder: newSortOrder }; }); }, 0); this.cancelFlippingTimer = window.setTimeout(() => this.setState({ isFlipping: false }), 1000); @@ -291,7 +292,6 @@ class UnthemedLogs extends PureComponent { const { dedupedRows, dedupCount } = this.dedupRows(filteredLogs, dedupStrategy); const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...'; - return ( <> {logsSeries && logsSeries.length ? ( @@ -366,16 +366,26 @@ class UnthemedLogs extends PureComponent {
- + + +
{ clearDetectedFields={this.clearDetectedFields} />
-
+
(LogsPanel) description: '', settings: { options: [ - { value: LogsSortOrder.Descending, label: 'Descending' }, - { value: LogsSortOrder.Ascending, label: 'Ascending' }, + { value: LogsSortOrder.Descending, label: 'Newest first' }, + { value: LogsSortOrder.Ascending, label: 'Oldest first' }, ], }, defaultValue: LogsSortOrder.Descending,