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
This commit is contained in:
Kristina 2022-02-21 06:09:43 -06:00 committed by GitHub
parent 434697e0fd
commit 31090a75eb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 19 deletions

View File

@ -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(
<Logs
logRows={rows}
timeZone={'utc'}
width={50}
loading={false}
loadingState={LoadingState.Done}
absoluteRange={{
from: toUtc('2019-01-01 10:00:00').valueOf(),
to: toUtc('2019-01-01 16:00:00').valueOf(),
}}
addResultsToCache={() => {}}
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>): 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,
};
};

View File

@ -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<Props, State> {
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<Props, State> {
// 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<Props, State> {
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<Props, State> {
</InlineField>
</InlineFieldRow>
<div>
<Button
variant="secondary"
disabled={isFlipping}
title={logsSortOrder === LogsSortOrder.Ascending ? 'Change to newest first' : 'Change to oldest first'}
aria-label="Flip results order"
className={styles.headerButton}
onClick={this.onChangeLogsSortOrder}
>
{isFlipping ? 'Flipping...' : 'Flip results order'}
</Button>
<InlineField label="Display results" className={styles.horizontalInlineLabel} transparent>
<RadioButtonGroup
disabled={isFlipping}
options={[
{
label: 'Newest first',
value: LogsSortOrder.Descending,
description: 'Show results newest to oldest',
},
{
label: 'Oldest first',
value: LogsSortOrder.Ascending,
description: 'Show results oldest to newest',
},
]}
value={logsSortOrder}
onChange={this.onChangeLogsSortOrder}
className={styles.radioButtons}
/>
</InlineField>
</div>
</div>
<LogsMetaRow
@ -390,7 +400,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
clearDetectedFields={this.clearDetectedFields}
/>
<div className={styles.logsSection}>
<div className={styles.logRows}>
<div className={styles.logRows} data-testid="logRows">
<LogRows
logRows={logRows}
deduplicatedRows={dedupedRows}

View File

@ -74,8 +74,8 @@ export const plugin = new PanelPlugin<Options>(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,