grafana/public/app/features/explore/RichHistory/RichHistory.tsx
Piotr Jamróz 5cb5141c72
Query History: Move local storage specific settings to persistence layer (#47500)
* Load Rich History when the container is opened

* Store rich history for each pane separately

* Do not update currently opened query history when an item is added

It's impossible to figure out if the item should be added or not, because filters are applied in the backend. We don't want to replicate that filtering logic in frontend. One way to make it work could be by refreshing both panes.

* Test starring and deleting query history items when both panes are open

* Remove e2e dependency on ExploreId

* Fix unit test

* Assert exact queries

* Simplify test

* Fix e2e tests

* Fix toolbar a11y

* Reload the history after an item is added

* Fix unit test

* Remove references to Explore from generic PageToolbar component

* Update test name

* Fix test assertion

* Add issue item to TODO

* Improve test assertion

* Simplify test setup

* Move query history settings to persistence layer

* Fix test import

* Fix unit test

* Fix unit test

* Test local storage settings API

* Code formatting

* Fix linting errors

* Add an integration test

* Add missing aria role

* Fix a11y issues

* Fix a11y issues

* Use divs instead of ul/li

Otherwis,e pa11y-ci reports the error below claiming there are no children with role=tab:

Certain ARIA roles must contain particular children
   (https://dequeuniversity.com/rules/axe/4.3/aria-required-children?application=axeAPI)

   (#reactRoot > div > main > div:nth-child(3) > div > div:nth-child(1) > div >
   div:nth-child(1) > div > div > nav > div:nth-child(2) > ul)

   <ul class="css-af3vye" role="tablist"><li class="css-1ciwanz"><a href...</ul>

* Clean up settings tab

* Remove redundant aria label

* Remove redundant container

* Clean up test assertions
2022-04-12 18:55:39 +02:00

163 lines
5.7 KiB
TypeScript

import React, { PureComponent } from 'react';
//Services & Utils
import { SortOrder } from 'app/core/utils/richHistory';
import { Themeable, withTheme, TabbedContainer, TabConfig } from '@grafana/ui';
//Types
import { RichHistoryQuery, ExploreId } from 'app/types/explore';
import { SelectableValue } from '@grafana/data';
//Components
import { RichHistorySettingsTab } from './RichHistorySettingsTab';
import { RichHistoryQueriesTab } from './RichHistoryQueriesTab';
import { RichHistoryStarredTab } from './RichHistoryStarredTab';
import { RichHistorySearchFilters, RichHistorySettings } from '../../../core/utils/richHistoryTypes';
export enum Tabs {
RichHistory = 'Query history',
Starred = 'Starred',
Settings = 'Settings',
}
export const sortOrderOptions = [
{ label: 'Newest first', value: SortOrder.Descending },
{ label: 'Oldest first', value: SortOrder.Ascending },
{ label: 'Data source A-Z', value: SortOrder.DatasourceAZ },
{ label: 'Data source Z-A', value: SortOrder.DatasourceZA },
];
export interface RichHistoryProps extends Themeable {
richHistory: RichHistoryQuery[];
richHistorySettings: RichHistorySettings;
richHistorySearchFilters: RichHistorySearchFilters;
updateHistorySettings: (settings: RichHistorySettings) => void;
updateHistorySearchFilters: (exploreId: ExploreId, filters: RichHistorySearchFilters) => void;
deleteRichHistory: () => void;
activeDatasourceInstance?: string;
firstTab: Tabs;
exploreId: ExploreId;
height: number;
onClose: () => void;
}
class UnThemedRichHistory extends PureComponent<RichHistoryProps> {
updateSettings = (settingsToUpdate: Partial<RichHistorySettings>) => {
this.props.updateHistorySettings({ ...this.props.richHistorySettings, ...settingsToUpdate });
};
updateFilters = (filtersToUpdate: Partial<RichHistorySearchFilters>) => {
this.props.updateHistorySearchFilters(this.props.exploreId, {
...this.props.richHistorySearchFilters,
...filtersToUpdate,
});
};
onChangeRetentionPeriod = (retentionPeriod: SelectableValue<number>) => {
if (retentionPeriod.value !== undefined) {
this.updateSettings({ retentionPeriod: retentionPeriod.value });
}
};
toggleStarredTabAsFirstTab = () =>
this.updateSettings({ starredTabAsFirstTab: !this.props.richHistorySettings.starredTabAsFirstTab });
toggleActiveDatasourceOnly = () =>
this.updateSettings({ activeDatasourceOnly: !this.props.richHistorySettings.activeDatasourceOnly });
onSelectDatasourceFilters = (datasourceFilters: SelectableValue[]) => this.updateFilters({ datasourceFilters });
onChangeSortOrder = (sortOrder: SortOrder) => this.updateFilters({ sortOrder });
/* If user selects activeDatasourceOnly === true, set datasource filter to currently active datasource.
* Filtering based on datasource won't be available. Otherwise set to null, as filtering will be
* available for user.
*/
initFilters() {
if (this.props.richHistorySettings.activeDatasourceOnly && this.props.activeDatasourceInstance) {
this.onSelectDatasourceFilters([
{ label: this.props.activeDatasourceInstance, value: this.props.activeDatasourceInstance },
]);
}
}
componentDidMount() {
this.initFilters();
}
/**
* Updating filters on didMount and didUpdate because we don't know when activeDatasourceInstance is ready
*/
componentDidUpdate(prevProps: RichHistoryProps) {
if (this.props.activeDatasourceInstance !== prevProps.activeDatasourceInstance) {
this.initFilters();
}
}
render() {
const { activeDatasourceOnly, retentionPeriod } = this.props.richHistorySettings;
const { datasourceFilters, sortOrder } = this.props.richHistorySearchFilters;
const { richHistory, height, exploreId, deleteRichHistory, onClose, firstTab } = this.props;
const QueriesTab: TabConfig = {
label: 'Query history',
value: Tabs.RichHistory,
content: (
<RichHistoryQueriesTab
queries={richHistory}
sortOrder={sortOrder}
datasourceFilters={datasourceFilters}
activeDatasourceOnly={activeDatasourceOnly}
retentionPeriod={retentionPeriod}
onChangeSortOrder={this.onChangeSortOrder}
onSelectDatasourceFilters={this.onSelectDatasourceFilters}
exploreId={exploreId}
height={height}
/>
),
icon: 'history',
};
const StarredTab: TabConfig = {
label: 'Starred',
value: Tabs.Starred,
content: (
<RichHistoryStarredTab
queries={richHistory}
sortOrder={sortOrder}
datasourceFilters={datasourceFilters}
activeDatasourceOnly={activeDatasourceOnly}
onChangeSortOrder={this.onChangeSortOrder}
onSelectDatasourceFilters={this.onSelectDatasourceFilters}
exploreId={exploreId}
/>
),
icon: 'star',
};
const SettingsTab: TabConfig = {
label: 'Settings',
value: Tabs.Settings,
content: (
<RichHistorySettingsTab
retentionPeriod={this.props.richHistorySettings.retentionPeriod}
starredTabAsFirstTab={this.props.richHistorySettings.starredTabAsFirstTab}
activeDatasourceOnly={this.props.richHistorySettings.activeDatasourceOnly}
onChangeRetentionPeriod={this.onChangeRetentionPeriod}
toggleStarredTabAsFirstTab={this.toggleStarredTabAsFirstTab}
toggleactiveDatasourceOnly={this.toggleActiveDatasourceOnly}
deleteRichHistory={deleteRichHistory}
/>
),
icon: 'sliders-v-alt',
};
let tabs = [QueriesTab, StarredTab, SettingsTab];
return (
<TabbedContainer tabs={tabs} onClose={onClose} defaultTab={firstTab} closeIconTooltip="Close query history" />
);
}
}
export const RichHistory = withTheme(UnThemedRichHistory);