mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Prometheus: Metric encyclopedia modal redesign (#64816)
* feat: metric encyclopedia modal redesign * test: update failing tests * refactor: add suggestions from pr review * test: fix failing test
This commit is contained in:
@@ -10,7 +10,7 @@ import { EmptyLanguageProviderMock } from '../../language_provider.mock';
|
|||||||
import { PromOptions } from '../../types';
|
import { PromOptions } from '../../types';
|
||||||
import { PromVisualQuery } from '../types';
|
import { PromVisualQuery } from '../types';
|
||||||
|
|
||||||
import { MetricEncyclopediaModal, testIds, placeholders } from './MetricEncyclopediaModal';
|
import { MetricEncyclopediaModal, testIds } from './MetricEncyclopediaModal';
|
||||||
|
|
||||||
// don't care about interaction tracking in our unit tests
|
// don't care about interaction tracking in our unit tests
|
||||||
jest.mock('@grafana/runtime', () => ({
|
jest.mock('@grafana/runtime', () => ({
|
||||||
@@ -96,7 +96,7 @@ describe('MetricEncyclopediaModal', () => {
|
|||||||
setup(defaultQuery, listOfMetrics);
|
setup(defaultQuery, listOfMetrics);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const selectType = screen.getByText(placeholders.type);
|
const selectType = screen.getByText('Filter by type');
|
||||||
expect(selectType).toBeInTheDocument();
|
expect(selectType).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -119,7 +119,7 @@ describe('MetricEncyclopediaModal', () => {
|
|||||||
setup(defaultQuery, listOfMetrics);
|
setup(defaultQuery, listOfMetrics);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
const selectType = screen.getByText(placeholders.variables);
|
const selectType = screen.getByText('Select template variables');
|
||||||
expect(selectType).toBeInTheDocument();
|
expect(selectType).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,17 +1,17 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import uFuzzy from '@leeoniya/ufuzzy';
|
import uFuzzy from '@leeoniya/ufuzzy';
|
||||||
import debounce from 'debounce-promise';
|
import debounce from 'debounce-promise';
|
||||||
import { debounce as debounceLodash } from 'lodash';
|
import { debounce as debounceLodash } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||||
|
import { EditorField } from '@grafana/experimental';
|
||||||
import { reportInteraction } from '@grafana/runtime';
|
import { reportInteraction } from '@grafana/runtime';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Collapse,
|
Collapse,
|
||||||
InlineField,
|
InlineField,
|
||||||
InlineLabel,
|
|
||||||
InlineSwitch,
|
InlineSwitch,
|
||||||
Input,
|
Input,
|
||||||
Modal,
|
Modal,
|
||||||
@@ -73,12 +73,12 @@ const promTypes: PromFilterOption[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const placeholders = {
|
export const placeholders = {
|
||||||
browse: 'Browse metric names by text',
|
browse: 'Search metrics by name',
|
||||||
metadataSearchSwicth: 'Browse by metadata type and description in addition to metric name',
|
metadataSearchSwitch: 'Search by metadata type and description in addition to name',
|
||||||
type: 'Counter, gauge, histogram, or summary',
|
type: 'Select...',
|
||||||
variables: 'Select a template variable for your metric',
|
variables: 'Select...',
|
||||||
excludeNoMetadata: 'Exclude results with no metadata when filtering',
|
excludeNoMetadata: 'Exclude results with no metadata',
|
||||||
setUseBackend: 'Use the backend to browse metrics and disable fuzzy search metadata browsing',
|
setUseBackend: 'Use the backend to browse metrics',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_RESULTS_PER_PAGE = 10;
|
export const DEFAULT_RESULTS_PER_PAGE = 10;
|
||||||
@@ -397,6 +397,19 @@ export const MetricEncyclopediaModal = (props: Props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAXIMUM_RESULTS_PER_PAGE = 1000;
|
||||||
|
const calculateResultsPerPage = (results: number) => {
|
||||||
|
if (results < 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (results > MAXIMUM_RESULTS_PER_PAGE) {
|
||||||
|
return MAXIMUM_RESULTS_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return results ?? 10;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
data-testid={testIds.metricModal}
|
data-testid={testIds.metricModal}
|
||||||
@@ -404,98 +417,47 @@ export const MetricEncyclopediaModal = (props: Props) => {
|
|||||||
title="Browse Metrics"
|
title="Browse Metrics"
|
||||||
onDismiss={onClose}
|
onDismiss={onClose}
|
||||||
aria-label="Metric Encyclopedia"
|
aria-label="Metric Encyclopedia"
|
||||||
|
className={styles.modal}
|
||||||
>
|
>
|
||||||
<FeedbackLink feedbackUrl="https://forms.gle/DEMAJHoAMpe3e54CA" />
|
<div className={styles.inputWrapper}>
|
||||||
<div className={styles.spacing}>
|
<div className={cx(styles.inputItem, styles.inputItemFirst)}>
|
||||||
Browse {totalMetricCount} metric{totalMetricCount > 1 ? 's' : ''} by text, by type, alphabetically or select a
|
<EditorField label="Search metrics">
|
||||||
variable.
|
<Input
|
||||||
{isLoading && (
|
data-testid={testIds.searchMetric}
|
||||||
<div className={styles.inlineSpinner}>
|
placeholder={placeholders.browse}
|
||||||
<Spinner></Spinner>
|
value={fuzzySearchQuery}
|
||||||
</div>
|
onInput={(e) => {
|
||||||
)}
|
const value = e.currentTarget.value ?? '';
|
||||||
</div>
|
setFuzzySearchQuery(value);
|
||||||
{query.labels.length > 0 && (
|
if (useBackend && value === '') {
|
||||||
<div className={styles.spacing}>
|
// get all metrics data if a user erases everything in the input
|
||||||
<i>These metrics have been pre-filtered by labels chosen in the label filters.</i>
|
updateMetricsMetadata();
|
||||||
</div>
|
} else if (useBackend) {
|
||||||
)}
|
debouncedBackendSearch(value);
|
||||||
<div className="gf-form">
|
} else {
|
||||||
<Input
|
// search either the names or all metadata
|
||||||
data-testid={testIds.searchMetric}
|
// fuzzy search go!
|
||||||
placeholder={placeholders.browse}
|
|
||||||
value={fuzzySearchQuery}
|
|
||||||
autoFocus
|
|
||||||
onInput={(e) => {
|
|
||||||
const value = e.currentTarget.value ?? '';
|
|
||||||
setFuzzySearchQuery(value);
|
|
||||||
if (useBackend && value === '') {
|
|
||||||
// get all metrics data if a user erases everything in the input
|
|
||||||
updateMetricsMetadata();
|
|
||||||
} else if (useBackend) {
|
|
||||||
debouncedBackendSearch(value);
|
|
||||||
} else {
|
|
||||||
// search either the names or all metadata
|
|
||||||
// fuzzy search go!
|
|
||||||
|
|
||||||
if (fullMetaSearch) {
|
if (fullMetaSearch) {
|
||||||
debouncedFuzzySearch(metaHaystack, value, setFuzzyMetaSearchResults);
|
debouncedFuzzySearch(metaHaystack, value, setFuzzyMetaSearchResults);
|
||||||
} else {
|
} else {
|
||||||
debouncedFuzzySearch(nameHaystack, value, setFuzzyNameSearchResults);
|
debouncedFuzzySearch(nameHaystack, value, setFuzzyNameSearchResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPageNum(1);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{hasMetadata && !useBackend && (
|
|
||||||
<InlineField label="" className={styles.labelColor} tooltip={<div>{placeholders.metadataSearchSwicth}</div>}>
|
|
||||||
<InlineSwitch
|
|
||||||
data-testid={testIds.searchWithMetadata}
|
|
||||||
showLabel={true}
|
|
||||||
value={fullMetaSearch}
|
|
||||||
onChange={() => {
|
|
||||||
setFullMetaSearch(!fullMetaSearch);
|
|
||||||
setPageNum(1);
|
setPageNum(1);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</InlineField>
|
</EditorField>
|
||||||
)}
|
</div>
|
||||||
<InlineField label="" className={styles.labelColor} tooltip={<div>{placeholders.setUseBackend}</div>}>
|
<div className={styles.inputItem}>
|
||||||
<InlineSwitch
|
<EditorField label="Filter by type">
|
||||||
data-testid={testIds.setUseBackend}
|
|
||||||
showLabel={true}
|
|
||||||
value={useBackend}
|
|
||||||
onChange={() => {
|
|
||||||
const newVal = !useBackend;
|
|
||||||
setUseBackend(newVal);
|
|
||||||
if (newVal === false) {
|
|
||||||
// rebuild the metrics metadata if we turn off useBackend
|
|
||||||
updateMetricsMetadata();
|
|
||||||
} else {
|
|
||||||
// check if there is text in the browse search and update
|
|
||||||
if (fuzzySearchQuery !== '') {
|
|
||||||
debouncedBackendSearch(fuzzySearchQuery);
|
|
||||||
}
|
|
||||||
// otherwise wait for user typing
|
|
||||||
}
|
|
||||||
|
|
||||||
setPageNum(1);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InlineField>
|
|
||||||
</div>
|
|
||||||
{hasMetadata && !useBackend && (
|
|
||||||
<>
|
|
||||||
<div className="gf-form">
|
|
||||||
<h6>Filter by Type</h6>
|
|
||||||
</div>
|
|
||||||
<div className="gf-form">
|
|
||||||
<MultiSelect
|
<MultiSelect
|
||||||
data-testid={testIds.selectType}
|
data-testid={testIds.selectType}
|
||||||
inputId="my-select"
|
inputId="my-select"
|
||||||
options={typeOptions}
|
options={typeOptions}
|
||||||
value={selectedTypes}
|
value={selectedTypes}
|
||||||
|
disabled={!hasMetadata || useBackend}
|
||||||
placeholder={placeholders.type}
|
placeholder={placeholders.type}
|
||||||
onChange={(v) => {
|
onChange={(v) => {
|
||||||
// *** Filter by type
|
// *** Filter by type
|
||||||
@@ -505,137 +467,204 @@ export const MetricEncyclopediaModal = (props: Props) => {
|
|||||||
setPageNum(1);
|
setPageNum(1);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{hasMetadata && (
|
</EditorField>
|
||||||
<InlineField label="" className={styles.labelColor} tooltip={<div>{placeholders.excludeNoMetadata}</div>}>
|
</div>
|
||||||
<InlineSwitch
|
<div className={styles.inputItem}>
|
||||||
showLabel={true}
|
<EditorField label="Select template variables">
|
||||||
value={excludeNullMetadata}
|
<Select
|
||||||
onChange={() => {
|
inputId="my-select"
|
||||||
setExcludeNullMetadata(!excludeNullMetadata);
|
options={variables}
|
||||||
setPageNum(1);
|
value={''}
|
||||||
}}
|
placeholder={placeholders.variables}
|
||||||
/>
|
onChange={(v) => {
|
||||||
</InlineField>
|
const value: string = v.value ?? '';
|
||||||
)}
|
onChange({ ...query, metric: value });
|
||||||
</div>
|
onClose();
|
||||||
</>
|
}}
|
||||||
)}
|
/>
|
||||||
<div className="gf-form">
|
</EditorField>
|
||||||
<h6>Variables</h6>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="gf-form">
|
|
||||||
<Select
|
<div className={styles.selectWrapper}>
|
||||||
inputId="my-select"
|
<EditorField label="Search Settings">
|
||||||
options={variables}
|
<>
|
||||||
value={''}
|
<div className={styles.selectItem}>
|
||||||
placeholder={placeholders.variables}
|
<InlineSwitch
|
||||||
onChange={(v) => {
|
data-testid={testIds.searchWithMetadata}
|
||||||
const value: string = v.value ?? '';
|
value={fullMetaSearch}
|
||||||
onChange({ ...query, metric: value });
|
disabled={useBackend || !hasMetadata}
|
||||||
onClose();
|
onChange={() => {
|
||||||
}}
|
setFullMetaSearch(!fullMetaSearch);
|
||||||
/>
|
setPageNum(1);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p className={styles.selectItemLabel}>{placeholders.metadataSearchSwitch}</p>
|
||||||
|
</div>
|
||||||
|
{/* <div className={styles.selectItem}>
|
||||||
|
<InlineSwitch data-testid={'im not sure what this toggle does.'} value={false} onChange={() => {}} />
|
||||||
|
<p className={styles.selectItemLabel}>Disable fuzzy search metadata browsing (HELP!)</p>
|
||||||
|
</div> */}
|
||||||
|
<div className={styles.selectItem}>
|
||||||
|
<InlineSwitch
|
||||||
|
data-testid={testIds.setUseBackend}
|
||||||
|
value={useBackend}
|
||||||
|
onChange={() => {
|
||||||
|
const newVal = !useBackend;
|
||||||
|
setUseBackend(newVal);
|
||||||
|
if (newVal === false) {
|
||||||
|
// rebuild the metrics metadata if we turn off useBackend
|
||||||
|
updateMetricsMetadata();
|
||||||
|
} else {
|
||||||
|
// check if there is text in the browse search and update
|
||||||
|
if (fuzzySearchQuery !== '') {
|
||||||
|
debouncedBackendSearch(fuzzySearchQuery);
|
||||||
|
}
|
||||||
|
// otherwise wait for user typing
|
||||||
|
}
|
||||||
|
|
||||||
|
setPageNum(1);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<p className={styles.selectItemLabel}>{placeholders.setUseBackend}</p>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</EditorField>
|
||||||
</div>
|
</div>
|
||||||
<h5 className={`${styles.center} ${styles.topPadding}`}>{filteredMetricCount} Results</h5>
|
|
||||||
<div className={`${styles.center} ${styles.bottomPadding}`}>{letterSearchComponent()}</div>
|
<h4 className={styles.resultsHeading}>Results</h4>
|
||||||
{metrics &&
|
<div className={styles.resultsData}>
|
||||||
displayedMetrics(metrics).map((metric: MetricData, idx) => {
|
<div className={styles.resultsDataCount}>
|
||||||
return (
|
Showing {filteredMetricCount} of {totalMetricCount} total metrics.{' '}
|
||||||
<Collapse
|
{isLoading && <Spinner className={styles.loadingSpinner} />}
|
||||||
aria-label={`open and close ${metric.value} query starter card`}
|
</div>
|
||||||
data-testid={testIds.metricCard}
|
{query.labels.length > 0 && (
|
||||||
key={metric.value}
|
<p className={styles.resultsDataFiltered}>
|
||||||
label={metric.value}
|
These metrics have been pre-filtered by labels chosen in the label filters.
|
||||||
isOpen={openTabs.includes(metric.value)}
|
</p>
|
||||||
collapsible={true}
|
)}
|
||||||
onToggle={() =>
|
</div>
|
||||||
setOpenTabs((tabs) =>
|
|
||||||
// close tab if it's already open, otherwise open it
|
<div className={styles.alphabetRow}>
|
||||||
tabs.includes(metric.value) ? tabs.filter((t) => t !== metric.value) : [...tabs, metric.value]
|
<div>{letterSearchComponent()}</div>
|
||||||
)
|
<div className={styles.selectItem}>
|
||||||
}
|
<InlineSwitch
|
||||||
>
|
value={excludeNullMetadata}
|
||||||
<div className={styles.cardsContainer}>
|
disabled={useBackend || !hasMetadata}
|
||||||
<Card className={styles.card}>
|
onChange={() => {
|
||||||
<Card.Description>
|
setExcludeNullMetadata(!excludeNullMetadata);
|
||||||
{metric.description && metric.type ? (
|
setPageNum(1);
|
||||||
<>
|
}}
|
||||||
Type: <span className={styles.metadata}>{metric.type}</span>
|
/>
|
||||||
<br />
|
<p className={styles.selectItemLabel}>{placeholders.excludeNoMetadata}</p>
|
||||||
Description: <span className={styles.metadata}>{metric.description}</span>
|
</div>
|
||||||
</>
|
</div>
|
||||||
) : (
|
|
||||||
<i>No metadata available</i>
|
<div className={styles.results}>
|
||||||
)}
|
{metrics &&
|
||||||
</Card.Description>
|
displayedMetrics(metrics).map((metric: MetricData, idx) => {
|
||||||
<Card.Actions>
|
return (
|
||||||
{/* *** Make selecting a metric easier, consider click on text */}
|
<Collapse
|
||||||
<Button
|
aria-label={`open and close ${metric.value} query starter card`}
|
||||||
size="sm"
|
data-testid={testIds.metricCard}
|
||||||
aria-label="use this metric button"
|
key={metric.value}
|
||||||
data-testid={testIds.useMetric}
|
label={metric.value}
|
||||||
onClick={() => {
|
isOpen={openTabs.includes(metric.value)}
|
||||||
onChange({ ...query, metric: metric.value });
|
collapsible={true}
|
||||||
reportInteraction('grafana_prom_metric_encycopedia_tracking', {
|
onToggle={() =>
|
||||||
metric: metric.value,
|
setOpenTabs((tabs) =>
|
||||||
hasVariables: variables.length > 0,
|
// close tab if it's already open, otherwise open it
|
||||||
hasMetadata: hasMetadata,
|
tabs.includes(metric.value) ? tabs.filter((t) => t !== metric.value) : [...tabs, metric.value]
|
||||||
totalMetricCount: metrics.length,
|
)
|
||||||
fuzzySearchQuery: fuzzySearchQuery,
|
}
|
||||||
fullMetaSearch: fullMetaSearch,
|
>
|
||||||
selectedTypes: selectedTypes,
|
<div className={styles.cardsContainer}>
|
||||||
letterSearch: letterSearch,
|
<Card className={styles.card}>
|
||||||
});
|
<Card.Description>
|
||||||
onClose();
|
{metric.description && metric.type ? (
|
||||||
}}
|
<>
|
||||||
>
|
Type: <span className={styles.metadata}>{metric.type}</span>
|
||||||
Use this metric
|
<br />
|
||||||
</Button>
|
Description: <span className={styles.metadata}>{metric.description}</span>
|
||||||
</Card.Actions>
|
</>
|
||||||
</Card>
|
) : (
|
||||||
</div>
|
<i>No metadata available</i>
|
||||||
</Collapse>
|
)}
|
||||||
);
|
</Card.Description>
|
||||||
})}
|
<Card.Actions>
|
||||||
<br />
|
{/* *** Make selecting a metric easier, consider click on text */}
|
||||||
<div className="gf-form">
|
<Button
|
||||||
<InlineLabel width={20} className="query-keyword">
|
size="sm"
|
||||||
Select Page
|
aria-label="use this metric button"
|
||||||
</InlineLabel>
|
data-testid={testIds.useMetric}
|
||||||
<Select
|
onClick={() => {
|
||||||
data-testid={testIds.searchPage}
|
onChange({ ...query, metric: metric.value });
|
||||||
options={calculatePageList(metrics, resultsPerPage).map((p) => {
|
reportInteraction('grafana_prom_metric_encycopedia_tracking', {
|
||||||
return { value: p, label: '' + p };
|
metric: metric.value,
|
||||||
|
hasVariables: variables.length > 0,
|
||||||
|
hasMetadata: hasMetadata,
|
||||||
|
totalMetricCount: metrics.length,
|
||||||
|
fuzzySearchQuery: fuzzySearchQuery,
|
||||||
|
fullMetaSearch: fullMetaSearch,
|
||||||
|
selectedTypes: selectedTypes,
|
||||||
|
letterSearch: letterSearch,
|
||||||
|
});
|
||||||
|
onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Use this metric
|
||||||
|
</Button>
|
||||||
|
</Card.Actions>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</Collapse>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
value={pageNum ?? 1}
|
|
||||||
placeholder="select page"
|
|
||||||
onChange={(e) => {
|
|
||||||
const value = e.value ?? 1;
|
|
||||||
setPageNum(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<InlineLabel width={20} className="query-keyword">
|
|
||||||
# results per page
|
|
||||||
</InlineLabel>
|
|
||||||
<Input
|
|
||||||
data-testid={testIds.resultsPerPage}
|
|
||||||
value={resultsPerPage ?? 10}
|
|
||||||
placeholder="results per page"
|
|
||||||
onInput={(e) => {
|
|
||||||
const value = +e.currentTarget.value;
|
|
||||||
|
|
||||||
if (isNaN(value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setResultsPerPage(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
<Button aria-label="close metric encyclopedia modal" variant="secondary" onClick={onClose}>
|
<div className={styles.pageSettingsWrapper}>
|
||||||
Close
|
<div className={styles.pageSettings}>
|
||||||
</Button>
|
<InlineField label="Select page" labelWidth={20} className="query-keyword">
|
||||||
|
<Select
|
||||||
|
data-testid={testIds.searchPage}
|
||||||
|
options={calculatePageList(metrics, resultsPerPage).map((p) => {
|
||||||
|
return { value: p, label: '' + p };
|
||||||
|
})}
|
||||||
|
value={pageNum ?? 1}
|
||||||
|
placeholder="select page"
|
||||||
|
width={20}
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.value ?? 1;
|
||||||
|
setPageNum(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InlineField>
|
||||||
|
|
||||||
|
<InlineField
|
||||||
|
label="# results per page"
|
||||||
|
tooltip={'The maximum results per page is ' + MAXIMUM_RESULTS_PER_PAGE}
|
||||||
|
labelWidth={20}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
data-testid={testIds.resultsPerPage}
|
||||||
|
value={calculateResultsPerPage(resultsPerPage)}
|
||||||
|
placeholder="results per page"
|
||||||
|
width={20}
|
||||||
|
onInput={(e) => {
|
||||||
|
const value = +e.currentTarget.value;
|
||||||
|
|
||||||
|
if (isNaN(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setResultsPerPage(value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</InlineField>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FeedbackLink feedbackUrl="https://forms.gle/DEMAJHoAMpe3e54CA" />
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -671,33 +700,83 @@ function alphabetically(ascending: boolean, metadataFilters: boolean) {
|
|||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
|
modal: css`
|
||||||
|
width: 85vw;
|
||||||
|
${theme.breakpoints.down('md')} {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
inputWrapper: css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: ${theme.spacing(2)};
|
||||||
|
margin-bottom: ${theme.spacing(2)};
|
||||||
|
`,
|
||||||
|
inputItemFirst: css`
|
||||||
|
flex-basis: 40%;
|
||||||
|
`,
|
||||||
|
inputItem: css`
|
||||||
|
flex-grow: 1;
|
||||||
|
`,
|
||||||
|
selectWrapper: css`
|
||||||
|
margin-bottom: ${theme.spacing(2)};
|
||||||
|
`,
|
||||||
|
selectItem: css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
`,
|
||||||
|
selectItemLabel: css`
|
||||||
|
margin: 0 0 0 ${theme.spacing(1)};
|
||||||
|
align-self: center;
|
||||||
|
color: ${theme.colors.text.secondary};
|
||||||
|
`,
|
||||||
|
resultsHeading: css`
|
||||||
|
margin: 0 0 0 0;
|
||||||
|
`,
|
||||||
|
resultsData: css`
|
||||||
|
margin: 0 0 ${theme.spacing(1)} 0;
|
||||||
|
`,
|
||||||
|
resultsDataCount: css`
|
||||||
|
margin: 0;
|
||||||
|
`,
|
||||||
|
resultsDataFiltered: css`
|
||||||
|
margin: 0;
|
||||||
|
color: ${theme.colors.warning.main};
|
||||||
|
`,
|
||||||
|
alphabetRow: css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`,
|
||||||
|
results: css`
|
||||||
|
height: 300px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
`,
|
||||||
|
pageSettingsWrapper: css`
|
||||||
|
padding-top: ${theme.spacing(1.5)};
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`,
|
||||||
|
pageSettings: css`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
`,
|
||||||
cardsContainer: css`
|
cardsContainer: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
`,
|
`,
|
||||||
spacing: css`
|
|
||||||
margin-bottom: ${theme.spacing(1)};
|
|
||||||
`,
|
|
||||||
center: css`
|
|
||||||
text-align: center;
|
|
||||||
padding: 4px;
|
|
||||||
width: 100%;
|
|
||||||
`,
|
|
||||||
topPadding: css`
|
|
||||||
padding: 10px 0 0 0;
|
|
||||||
`,
|
|
||||||
bottomPadding: css`
|
|
||||||
padding: 0 0 4px 0;
|
|
||||||
`,
|
|
||||||
card: css`
|
card: css`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
`,
|
`,
|
||||||
selAlpha: css`
|
selAlpha: css`
|
||||||
font-style: italic;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: #6e9fff;
|
color: #6e9fff;
|
||||||
`,
|
`,
|
||||||
@@ -710,10 +789,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
metadata: css`
|
metadata: css`
|
||||||
color: rgb(204, 204, 220);
|
color: rgb(204, 204, 220);
|
||||||
`,
|
`,
|
||||||
labelColor: css`
|
loadingSpinner: css`
|
||||||
color: #6e9fff;
|
|
||||||
`,
|
|
||||||
inlineSpinner: css`
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user