mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactor: Decouple Label Browser from LocalStorage (#40449)
* Refactor: Decouple Label Browser from LocalStorage * Clean up
This commit is contained in:
@@ -3,8 +3,8 @@ import store from '../../store';
|
||||
|
||||
export interface Props<T> {
|
||||
storageKey: string;
|
||||
defaultValue?: T;
|
||||
children: (value: T, onSaveToStore: (value: T) => void) => React.ReactNode;
|
||||
defaultValue: T;
|
||||
children: (value: T, onSaveToStore: (value: T) => void, onDeleteFromStore: () => void) => React.ReactNode;
|
||||
}
|
||||
|
||||
interface State<T> {
|
||||
@@ -32,10 +32,20 @@ export class LocalStorageValueProvider<T> extends PureComponent<Props<T>, State<
|
||||
this.setState({ value });
|
||||
};
|
||||
|
||||
onDeleteFromStore = () => {
|
||||
const { storageKey, defaultValue } = this.props;
|
||||
try {
|
||||
store.delete(storageKey);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
this.setState({ value: defaultValue });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
const { value } = this.state;
|
||||
|
||||
return <>{children(value, this.onSaveToStore)}</>;
|
||||
return <>{children(value, this.onSaveToStore, this.onDeleteFromStore)}</>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,9 @@ describe('LokiLabelBrowser', () => {
|
||||
onChange: () => {},
|
||||
autoSelect: 0,
|
||||
languageProvider: (mockLanguageProvider as unknown) as LokiLanguageProvider,
|
||||
lastUsedLabels: [],
|
||||
storeLastUsedLabels: () => {},
|
||||
deleteLastUsedLabels: () => {},
|
||||
};
|
||||
|
||||
return defaults;
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import LokiLanguageProvider from '../language_provider';
|
||||
import PromQlLanguageProvider from '../../prometheus/language_provider';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import store from 'app/core/store';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { sortBy } from 'lodash';
|
||||
@@ -24,8 +23,6 @@ const MAX_VALUE_COUNT = 10000;
|
||||
const MAX_AUTO_SELECT = 4;
|
||||
const EMPTY_SELECTOR = '{}';
|
||||
|
||||
export const LAST_USED_LABELS_KEY = 'grafana.datasources.loki.browser.labels';
|
||||
|
||||
export interface BrowserProps {
|
||||
// TODO #33976: Is it possible to use a common interface here? For example: LabelsLanguageProvider
|
||||
languageProvider: LokiLanguageProvider | PromQlLanguageProvider;
|
||||
@@ -33,6 +30,9 @@ export interface BrowserProps {
|
||||
theme: GrafanaTheme2;
|
||||
autoSelect?: number;
|
||||
hide?: () => void;
|
||||
lastUsedLabels: string[];
|
||||
storeLastUsedLabels: (labels: string[]) => void;
|
||||
deleteLastUsedLabels: () => void;
|
||||
}
|
||||
|
||||
interface BrowserState {
|
||||
@@ -208,7 +208,7 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
|
||||
}));
|
||||
return { labels, searchTerm: '', status: '', error: '', validationStatus: '' };
|
||||
});
|
||||
store.delete(LAST_USED_LABELS_KEY);
|
||||
this.props.deleteLastUsedLabels();
|
||||
};
|
||||
|
||||
onClickLabel = (name: string, value: string | undefined, event: React.MouseEvent<HTMLElement>) => {
|
||||
@@ -261,9 +261,9 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { languageProvider, autoSelect = MAX_AUTO_SELECT } = this.props;
|
||||
const { languageProvider, autoSelect = MAX_AUTO_SELECT, lastUsedLabels } = this.props;
|
||||
if (languageProvider) {
|
||||
const selectedLabels: string[] = store.getObject(LAST_USED_LABELS_KEY, []);
|
||||
const selectedLabels: string[] = lastUsedLabels;
|
||||
languageProvider.start().then(() => {
|
||||
let rawLabels: string[] = languageProvider.getLabelKeys();
|
||||
if (rawLabels.length > MAX_LABEL_COUNT) {
|
||||
@@ -295,7 +295,7 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
|
||||
return;
|
||||
}
|
||||
const selectedLabels = this.state.labels.filter((label) => label.selected).map((label) => label.name);
|
||||
store.setObject(LAST_USED_LABELS_KEY, selectedLabels);
|
||||
this.props.storeLastUsedLabels(selectedLabels);
|
||||
if (label.selected) {
|
||||
// Refetch values for newly selected label...
|
||||
if (!label.values) {
|
||||
|
||||
@@ -17,6 +17,9 @@ import { LanguageMap, languages as prismLanguages } from 'prismjs';
|
||||
import LokiLanguageProvider from '../language_provider';
|
||||
import { shouldRefreshLabels } from '../language_utils';
|
||||
import LokiDatasource from '../datasource';
|
||||
import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider';
|
||||
|
||||
const LAST_USED_LABELS_KEY = 'grafana.datasources.loki.browser.labels';
|
||||
|
||||
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) {
|
||||
if (!hasSyntax) {
|
||||
@@ -159,42 +162,54 @@ export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, Lok
|
||||
const buttonDisabled = !(labelsLoaded && hasLogLabels);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<button
|
||||
className="gf-form-label query-keyword pointer"
|
||||
onClick={this.onClickChooserButton}
|
||||
disabled={buttonDisabled}
|
||||
>
|
||||
{chooserText}
|
||||
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
|
||||
</button>
|
||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
||||
<QueryField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={cleanText}
|
||||
query={query.expr}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onChange={this.onChangeQuery}
|
||||
onBlur={this.props.onBlur}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder={placeholder}
|
||||
portalOrigin="loki"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{labelBrowserVisible && (
|
||||
<div className="gf-form">
|
||||
<LokiLabelBrowser languageProvider={lokiLanguageProvider} onChange={this.onChangeLabelBrowser} />
|
||||
</div>
|
||||
)}
|
||||
<LocalStorageValueProvider<string[]> storageKey={LAST_USED_LABELS_KEY} defaultValue={[]}>
|
||||
{(lastUsedLabels, onLastUsedLabelsSave, onLastUsedLabelsDelete) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<button
|
||||
className="gf-form-label query-keyword pointer"
|
||||
onClick={this.onClickChooserButton}
|
||||
disabled={buttonDisabled}
|
||||
>
|
||||
{chooserText}
|
||||
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
|
||||
</button>
|
||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
||||
<QueryField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={cleanText}
|
||||
query={query.expr}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onChange={this.onChangeQuery}
|
||||
onBlur={this.props.onBlur}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder={placeholder}
|
||||
portalOrigin="loki"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{labelBrowserVisible && (
|
||||
<div className="gf-form">
|
||||
<LokiLabelBrowser
|
||||
languageProvider={lokiLanguageProvider}
|
||||
onChange={this.onChangeLabelBrowser}
|
||||
lastUsedLabels={lastUsedLabels || []}
|
||||
storeLastUsedLabels={onLastUsedLabelsSave}
|
||||
deleteLastUsedLabels={onLastUsedLabelsDelete}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ExtraFieldElement}
|
||||
</>
|
||||
{ExtraFieldElement}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</LocalStorageValueProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,10 @@ import { QueryEditorProps, QueryHint, isDataFrame, toLegacyResponseData, TimeRan
|
||||
import { PrometheusDatasource } from '../datasource';
|
||||
import { PrometheusMetricsBrowser } from './PrometheusMetricsBrowser';
|
||||
import { MonacoQueryFieldLazy } from './monaco-query-field/MonacoQueryFieldLazy';
|
||||
import { LocalStorageValueProvider } from 'app/core/components/LocalStorageValueProvider';
|
||||
|
||||
export const RECORDING_RULES_GROUP = '__recording_rules__';
|
||||
const LAST_USED_LABELS_KEY = 'grafana.datasources.prometheus.browser.labels';
|
||||
|
||||
function getChooserText(metricsLookupDisabled: boolean, hasSyntax: boolean, hasMetrics: boolean) {
|
||||
if (metricsLookupDisabled) {
|
||||
@@ -277,66 +279,78 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
const isMonacoEditorEnabled = config.featureToggles.prometheusMonaco;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<button
|
||||
className="gf-form-label query-keyword pointer"
|
||||
onClick={this.onClickChooserButton}
|
||||
disabled={buttonDisabled}
|
||||
>
|
||||
{chooserText}
|
||||
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
|
||||
</button>
|
||||
<LocalStorageValueProvider<string[]> storageKey={LAST_USED_LABELS_KEY} defaultValue={[]}>
|
||||
{(lastUsedLabels, onLastUsedLabelsSave, onLastUsedLabelsDelete) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1"
|
||||
data-testid={this.props['data-testid']}
|
||||
>
|
||||
<button
|
||||
className="gf-form-label query-keyword pointer"
|
||||
onClick={this.onClickChooserButton}
|
||||
disabled={buttonDisabled}
|
||||
>
|
||||
{chooserText}
|
||||
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
|
||||
</button>
|
||||
|
||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
||||
{isMonacoEditorEnabled ? (
|
||||
<MonacoQueryFieldLazy
|
||||
languageProvider={languageProvider}
|
||||
history={history}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
initialValue={query.expr ?? ''}
|
||||
/>
|
||||
) : (
|
||||
<QueryField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={cleanText}
|
||||
query={query.expr}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onBlur={this.props.onBlur}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder={placeholder}
|
||||
portalOrigin="prometheus"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{labelBrowserVisible && (
|
||||
<div className="gf-form">
|
||||
<PrometheusMetricsBrowser languageProvider={languageProvider} onChange={this.onChangeLabelBrowser} />
|
||||
</div>
|
||||
)}
|
||||
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
|
||||
{isMonacoEditorEnabled ? (
|
||||
<MonacoQueryFieldLazy
|
||||
languageProvider={languageProvider}
|
||||
history={history}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
initialValue={query.expr ?? ''}
|
||||
/>
|
||||
) : (
|
||||
<QueryField
|
||||
additionalPlugins={this.plugins}
|
||||
cleanText={cleanText}
|
||||
query={query.expr}
|
||||
onTypeahead={this.onTypeahead}
|
||||
onWillApplySuggestion={willApplySuggestion}
|
||||
onBlur={this.props.onBlur}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder={placeholder}
|
||||
portalOrigin="prometheus"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{labelBrowserVisible && (
|
||||
<div className="gf-form">
|
||||
<PrometheusMetricsBrowser
|
||||
languageProvider={languageProvider}
|
||||
onChange={this.onChangeLabelBrowser}
|
||||
lastUsedLabels={lastUsedLabels || []}
|
||||
storeLastUsedLabels={onLastUsedLabelsSave}
|
||||
deleteLastUsedLabels={onLastUsedLabelsDelete}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{ExtraFieldElement}
|
||||
{hint ? (
|
||||
<div className="query-row-break">
|
||||
<div className="prom-query-field-info text-warning">
|
||||
{hint.label}{' '}
|
||||
{hint.fix ? (
|
||||
<a className="text-link muted" onClick={this.onClickHintFix}>
|
||||
{hint.fix.label}
|
||||
</a>
|
||||
{ExtraFieldElement}
|
||||
{hint ? (
|
||||
<div className="query-row-break">
|
||||
<div className="prom-query-field-info text-warning">
|
||||
{hint.label}{' '}
|
||||
{hint.fix ? (
|
||||
<a className="text-link muted" onClick={this.onClickHintFix}>
|
||||
{hint.fix.label}
|
||||
</a>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</LocalStorageValueProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,6 +133,9 @@ describe('PrometheusMetricsBrowser', () => {
|
||||
onChange: () => {},
|
||||
autoSelect: 0,
|
||||
languageProvider: (mockLanguageProvider as unknown) as PromQlLanguageProvider,
|
||||
lastUsedLabels: [],
|
||||
storeLastUsedLabels: () => {},
|
||||
deleteLastUsedLabels: () => {},
|
||||
};
|
||||
|
||||
return defaults;
|
||||
|
||||
@@ -12,7 +12,6 @@ import {
|
||||
import PromQlLanguageProvider from '../language_provider';
|
||||
import { escapeLabelValueInExactSelector, escapeLabelValueInRegexSelector } from '../language_utils';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import store from 'app/core/store';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
@@ -24,14 +23,15 @@ const EMPTY_SELECTOR = '{}';
|
||||
const METRIC_LABEL = '__name__';
|
||||
const LIST_ITEM_SIZE = 25;
|
||||
|
||||
export const LAST_USED_LABELS_KEY = 'grafana.datasources.prometheus.browser.labels';
|
||||
|
||||
export interface BrowserProps {
|
||||
languageProvider: PromQlLanguageProvider;
|
||||
onChange: (selector: string) => void;
|
||||
theme: GrafanaTheme;
|
||||
autoSelect?: number;
|
||||
hide?: () => void;
|
||||
lastUsedLabels: string[];
|
||||
storeLastUsedLabels: (labels: string[]) => void;
|
||||
deleteLastUsedLabels: () => void;
|
||||
}
|
||||
|
||||
interface BrowserState {
|
||||
@@ -243,7 +243,7 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
valueSearchTerm: '',
|
||||
};
|
||||
});
|
||||
store.delete(LAST_USED_LABELS_KEY);
|
||||
this.props.deleteLastUsedLabels();
|
||||
// Get metrics
|
||||
this.fetchValues(METRIC_LABEL, EMPTY_SELECTOR);
|
||||
};
|
||||
@@ -316,9 +316,9 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { languageProvider } = this.props;
|
||||
const { languageProvider, lastUsedLabels } = this.props;
|
||||
if (languageProvider) {
|
||||
const selectedLabels: string[] = store.getObject(LAST_USED_LABELS_KEY, []);
|
||||
const selectedLabels: string[] = lastUsedLabels;
|
||||
languageProvider.start().then(() => {
|
||||
let rawLabels: string[] = languageProvider.getLabelKeys();
|
||||
// TODO too-many-metrics
|
||||
@@ -353,7 +353,7 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
||||
return;
|
||||
}
|
||||
const selectedLabels = this.state.labels.filter((label) => label.selected).map((label) => label.name);
|
||||
store.setObject(LAST_USED_LABELS_KEY, selectedLabels);
|
||||
this.props.storeLastUsedLabels(selectedLabels);
|
||||
if (label.selected) {
|
||||
// Refetch values for newly selected label...
|
||||
if (!label.values) {
|
||||
|
||||
Reference in New Issue
Block a user