mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Add fuzzy search to label browser (#36864)
This commit is contained in:
parent
a65975cca0
commit
b644ea9e6e
@ -1,16 +1,15 @@
|
|||||||
import React, { forwardRef, HTMLAttributes } from 'react';
|
import React, { forwardRef, HTMLAttributes, useCallback } from 'react';
|
||||||
import { cx, css } from '@emotion/css';
|
import { cx, css } from '@emotion/css';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { useTheme2 } from '@grafana/ui';
|
import { useTheme2 } from '../../themes';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import Highlighter from 'react-highlight-words';
|
import Highlighter from 'react-highlight-words';
|
||||||
|
import { PartialHighlighter } from '../Typeahead/PartialHighlighter';
|
||||||
|
import { HighlightPart } from '../../types';
|
||||||
|
|
||||||
/**
|
type OnLabelClick = (name: string, value: string | undefined, event: React.MouseEvent<HTMLElement>) => void;
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type OnLabelClick = (name: string, value: string | undefined, event: React.MouseEvent<HTMLElement>) => void;
|
|
||||||
|
|
||||||
export interface Props extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
|
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
|
||||||
name: string;
|
name: string;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
loading?: boolean;
|
loading?: boolean;
|
||||||
@ -18,23 +17,45 @@ export interface Props extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
|
|||||||
value?: string;
|
value?: string;
|
||||||
facets?: number;
|
facets?: number;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
highlightParts?: HighlightPart[];
|
||||||
onClick?: OnLabelClick;
|
onClick?: OnLabelClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO #33976: Create a common, shared component with public/app/plugins/datasource/loki/components/LokiLabel.tsx
|
* @internal
|
||||||
*/
|
*/
|
||||||
export const Label = forwardRef<HTMLElement, Props>(
|
export const Label = forwardRef<HTMLElement, Props>(
|
||||||
({ name, value, hidden, facets, onClick, className, loading, searchTerm, active, style, title, ...rest }, ref) => {
|
(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
hidden,
|
||||||
|
facets,
|
||||||
|
onClick,
|
||||||
|
className,
|
||||||
|
loading,
|
||||||
|
searchTerm,
|
||||||
|
active,
|
||||||
|
style,
|
||||||
|
title,
|
||||||
|
highlightParts,
|
||||||
|
...rest
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = getLabelStyles(theme);
|
const styles = getLabelStyles(theme);
|
||||||
const searchWords = searchTerm ? [searchTerm] : [];
|
const searchWords = searchTerm ? [searchTerm] : [];
|
||||||
|
|
||||||
const onLabelClick = (event: React.MouseEvent<HTMLElement>) => {
|
const onLabelClick = useCallback(
|
||||||
if (onClick && !hidden) {
|
(event: React.MouseEvent<HTMLElement>) => {
|
||||||
onClick(name, value, event);
|
if (onClick && !hidden) {
|
||||||
}
|
onClick(name, value, event);
|
||||||
};
|
}
|
||||||
|
},
|
||||||
|
[onClick, name, hidden, value]
|
||||||
|
);
|
||||||
|
|
||||||
// Using this component for labels and label values. If value is given use value for display text.
|
// Using this component for labels and label values. If value is given use value for display text.
|
||||||
let text = value || name;
|
let text = value || name;
|
||||||
if (facets) {
|
if (facets) {
|
||||||
@ -60,12 +81,16 @@ export const Label = forwardRef<HTMLElement, Props>(
|
|||||||
)}
|
)}
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<Highlighter
|
{highlightParts !== undefined ? (
|
||||||
textToHighlight={text}
|
<PartialHighlighter text={text} highlightClassName={styles.matchHighLight} highlightParts={highlightParts} />
|
||||||
searchWords={searchWords}
|
) : (
|
||||||
autoEscape
|
<Highlighter
|
||||||
highlightClassName={styles.matchHighLight}
|
textToHighlight={text}
|
||||||
/>
|
searchWords={searchWords}
|
||||||
|
autoEscape
|
||||||
|
highlightClassName={styles.matchHighLight}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -75,11 +100,11 @@ Label.displayName = 'Label';
|
|||||||
|
|
||||||
const getLabelStyles = (theme: GrafanaTheme2) => ({
|
const getLabelStyles = (theme: GrafanaTheme2) => ({
|
||||||
base: css`
|
base: css`
|
||||||
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: ${theme.typography.size.sm};
|
font-size: ${theme.typography.size.sm};
|
||||||
line-height: ${theme.typography.bodySmall.lineHeight};
|
line-height: ${theme.typography.bodySmall.lineHeight};
|
||||||
background-color: ${theme.colors.background.secondary};
|
background-color: ${theme.colors.background.secondary};
|
||||||
vertical-align: baseline;
|
|
||||||
color: ${theme.colors.text};
|
color: ${theme.colors.text};
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-shadow: none;
|
text-shadow: none;
|
@ -255,3 +255,4 @@ export { preparePlotFrame } from './GraphNG/utils';
|
|||||||
export { GraphNGLegendEvent } from './GraphNG/types';
|
export { GraphNGLegendEvent } from './GraphNG/types';
|
||||||
export * from './PanelChrome/types';
|
export * from './PanelChrome/types';
|
||||||
export { EmotionPerfTest } from './ThemeDemos/EmotionPerfTest';
|
export { EmotionPerfTest } from './ThemeDemos/EmotionPerfTest';
|
||||||
|
export { Label as BrowserLabel } from './BrowserLabel/Label';
|
||||||
|
@ -76,4 +76,15 @@ describe('Fuzzy search', () => {
|
|||||||
found: true,
|
found: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('ignores whitespace in needle', () => {
|
||||||
|
expect(fuzzyMatch('bbaarr_bar_bbarr', 'bb bar')).toEqual({
|
||||||
|
ranges: [
|
||||||
|
{ start: 0, end: 1 },
|
||||||
|
{ start: 7, end: 9 },
|
||||||
|
],
|
||||||
|
distance: 5,
|
||||||
|
found: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
@ -20,10 +20,15 @@ type FuzzyMatch = {
|
|||||||
*
|
*
|
||||||
* @param stack - main text to be searched
|
* @param stack - main text to be searched
|
||||||
* @param needle - partial text to find in the stack
|
* @param needle - partial text to find in the stack
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
export function fuzzyMatch(stack: string, needle: string): FuzzyMatch {
|
export function fuzzyMatch(stack: string, needle: string): FuzzyMatch {
|
||||||
let distance = 0,
|
let distance = 0,
|
||||||
searchIndex = stack.indexOf(needle);
|
searchIndex = stack.indexOf(needle);
|
||||||
|
// Remove whitespace from needle as a temporary solution to treat separate string
|
||||||
|
// queries as 'AND'
|
||||||
|
needle = needle.replace(/\s/g, '');
|
||||||
|
|
||||||
const ranges: HighlightPart[] = [];
|
const ranges: HighlightPart[] = [];
|
||||||
|
|
@ -16,3 +16,4 @@ export { renderOrCallToRender } from './renderOrCallToRender';
|
|||||||
export { createLogger } from './logger';
|
export { createLogger } from './logger';
|
||||||
export { attachDebugger } from './debug';
|
export { attachDebugger } from './debug';
|
||||||
export * from './nodeGraph';
|
export * from './nodeGraph';
|
||||||
|
export { fuzzyMatch } from './fuzzy';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CompletionItem, SearchFunction } from '../types';
|
import { CompletionItem, SearchFunction } from '../types';
|
||||||
import { fuzzyMatch } from '../slate-plugins/fuzzy';
|
import { fuzzyMatch } from './fuzzy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of auto-complete search function used by SuggestionsPlugin.handleTypeahead()
|
* List of auto-complete search function used by SuggestionsPlugin.handleTypeahead()
|
||||||
|
@ -1,125 +0,0 @@
|
|||||||
import React, { forwardRef, HTMLAttributes } from 'react';
|
|
||||||
import { cx, css } from '@emotion/css';
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
|
||||||
import { useTheme2 } from '@grafana/ui';
|
|
||||||
// @ts-ignore
|
|
||||||
import Highlighter from 'react-highlight-words';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export type OnLabelClick = (name: string, value: string | undefined, event: React.MouseEvent<HTMLElement>) => void;
|
|
||||||
|
|
||||||
export interface Props extends Omit<HTMLAttributes<HTMLElement>, 'onClick'> {
|
|
||||||
name: string;
|
|
||||||
active?: boolean;
|
|
||||||
loading?: boolean;
|
|
||||||
searchTerm?: string;
|
|
||||||
value?: string;
|
|
||||||
facets?: number;
|
|
||||||
onClick?: OnLabelClick;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LokiLabel = forwardRef<HTMLElement, Props>(
|
|
||||||
({ name, value, hidden, facets, onClick, className, loading, searchTerm, active, style, ...rest }, ref) => {
|
|
||||||
const theme = useTheme2();
|
|
||||||
const styles = getLabelStyles(theme);
|
|
||||||
const searchWords = searchTerm ? [searchTerm] : [];
|
|
||||||
|
|
||||||
const onLabelClick = (event: React.MouseEvent<HTMLElement>) => {
|
|
||||||
if (onClick && !hidden) {
|
|
||||||
onClick(name, value, event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Using this component for labels and label values. If value is given use value for display text.
|
|
||||||
let text = value || name;
|
|
||||||
if (facets) {
|
|
||||||
text = `${text} (${facets})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span
|
|
||||||
key={text}
|
|
||||||
ref={ref}
|
|
||||||
onClick={onLabelClick}
|
|
||||||
style={style}
|
|
||||||
title={text}
|
|
||||||
role="option"
|
|
||||||
aria-selected={!!active}
|
|
||||||
className={cx(
|
|
||||||
styles.base,
|
|
||||||
active && styles.active,
|
|
||||||
loading && styles.loading,
|
|
||||||
hidden && styles.hidden,
|
|
||||||
className,
|
|
||||||
onClick && !hidden && styles.hover
|
|
||||||
)}
|
|
||||||
{...rest}
|
|
||||||
>
|
|
||||||
<Highlighter
|
|
||||||
textToHighlight={text}
|
|
||||||
searchWords={searchWords}
|
|
||||||
autoEscape={true}
|
|
||||||
highlightClassName={styles.matchHighLight}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
LokiLabel.displayName = 'LokiLabel';
|
|
||||||
|
|
||||||
const getLabelStyles = (theme: GrafanaTheme2) => ({
|
|
||||||
base: css`
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: ${theme.typography.size.sm};
|
|
||||||
line-height: ${theme.typography.bodySmall.lineHeight};
|
|
||||||
background-color: ${theme.colors.background.secondary};
|
|
||||||
vertical-align: baseline;
|
|
||||||
color: ${theme.colors.text};
|
|
||||||
white-space: nowrap;
|
|
||||||
text-shadow: none;
|
|
||||||
padding: ${theme.spacing(0.5)};
|
|
||||||
border-radius: ${theme.shape.borderRadius()};
|
|
||||||
margin-right: ${theme.spacing(1)};
|
|
||||||
margin-bottom: ${theme.spacing(0.5)};
|
|
||||||
`,
|
|
||||||
loading: css`
|
|
||||||
font-weight: ${theme.typography.fontWeightMedium};
|
|
||||||
background-color: ${theme.colors.primary.shade};
|
|
||||||
color: ${theme.colors.text.primary};
|
|
||||||
animation: pulse 3s ease-out 0s infinite normal forwards;
|
|
||||||
@keyframes pulse {
|
|
||||||
0% {
|
|
||||||
color: ${theme.colors.text.primary};
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
color: ${theme.colors.text.secondary};
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
color: ${theme.colors.text.disabled};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
active: css`
|
|
||||||
font-weight: ${theme.typography.fontWeightMedium};
|
|
||||||
background-color: ${theme.colors.primary.main};
|
|
||||||
color: ${theme.colors.primary.contrastText};
|
|
||||||
`,
|
|
||||||
matchHighLight: css`
|
|
||||||
background: inherit;
|
|
||||||
color: ${theme.colors.primary.text};
|
|
||||||
background-color: ${theme.colors.primary.transparent};
|
|
||||||
`,
|
|
||||||
hidden: css`
|
|
||||||
opacity: 0.6;
|
|
||||||
cursor: default;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
`,
|
|
||||||
hover: css`
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.85;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
});
|
|
@ -246,8 +246,8 @@ describe('LokiLabelBrowser', () => {
|
|||||||
await screen.findByLabelText('Values for label2');
|
await screen.findByLabelText('Values for label2');
|
||||||
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4);
|
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(4);
|
||||||
// Typing '1' to filter for values
|
// Typing '1' to filter for values
|
||||||
userEvent.type(screen.getByLabelText('Filter expression for values'), '1');
|
userEvent.type(screen.getByLabelText('Filter expression for values'), 'val1');
|
||||||
expect(screen.getByLabelText('Filter expression for values')).toHaveValue('1');
|
expect(screen.getByLabelText('Filter expression for values')).toHaveValue('val1');
|
||||||
expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument();
|
expect(screen.queryByRole('option', { name: 'value2-2' })).not.toBeInTheDocument();
|
||||||
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3);
|
expect(await screen.findAllByRole('option', { name: /value/ })).toHaveLength(3);
|
||||||
});
|
});
|
||||||
|
@ -1,19 +1,29 @@
|
|||||||
import React, { ChangeEvent } from 'react';
|
import React, { ChangeEvent } from 'react';
|
||||||
import { Button, HorizontalGroup, Input, Label, LoadingPlaceholder, withTheme2 } from '@grafana/ui';
|
import {
|
||||||
|
Button,
|
||||||
|
HighlightPart,
|
||||||
|
HorizontalGroup,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
LoadingPlaceholder,
|
||||||
|
withTheme2,
|
||||||
|
BrowserLabel as LokiLabel,
|
||||||
|
fuzzyMatch,
|
||||||
|
} from '@grafana/ui';
|
||||||
import LokiLanguageProvider from '../language_provider';
|
import LokiLanguageProvider from '../language_provider';
|
||||||
import PromQlLanguageProvider from '../../prometheus/language_provider';
|
import PromQlLanguageProvider from '../../prometheus/language_provider';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
import { FixedSizeList } from 'react-window';
|
import { FixedSizeList } from 'react-window';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { LokiLabel } from './LokiLabel';
|
import { sortBy } from 'lodash';
|
||||||
|
|
||||||
// Hard limit on labels to render
|
// Hard limit on labels to render
|
||||||
const MAX_LABEL_COUNT = 1000;
|
const MAX_LABEL_COUNT = 1000;
|
||||||
const MAX_VALUE_COUNT = 10000;
|
const MAX_VALUE_COUNT = 10000;
|
||||||
const MAX_AUTO_SELECT = 4;
|
const MAX_AUTO_SELECT = 4;
|
||||||
const EMPTY_SELECTOR = '{}';
|
const EMPTY_SELECTOR = '{}';
|
||||||
|
|
||||||
export const LAST_USED_LABELS_KEY = 'grafana.datasources.loki.browser.labels';
|
export const LAST_USED_LABELS_KEY = 'grafana.datasources.loki.browser.labels';
|
||||||
|
|
||||||
export interface BrowserProps {
|
export interface BrowserProps {
|
||||||
@ -36,6 +46,8 @@ interface BrowserState {
|
|||||||
interface FacettableValue {
|
interface FacettableValue {
|
||||||
name: string;
|
name: string;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
|
highlightParts?: HighlightPart[];
|
||||||
|
order?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectableLabel {
|
export interface SelectableLabel {
|
||||||
@ -378,15 +390,42 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
|
|||||||
return <LoadingPlaceholder text="Loading labels..." />;
|
return <LoadingPlaceholder text="Loading labels..." />;
|
||||||
}
|
}
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
let selectedLabels = labels.filter((label) => label.selected && label.values);
|
|
||||||
if (searchTerm) {
|
|
||||||
selectedLabels = selectedLabels.map((label) => ({
|
|
||||||
...label,
|
|
||||||
values: label.values?.filter((value) => value.selected || value.name.includes(searchTerm)),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
const selector = buildSelector(this.state.labels);
|
const selector = buildSelector(this.state.labels);
|
||||||
const empty = selector === EMPTY_SELECTOR;
|
const empty = selector === EMPTY_SELECTOR;
|
||||||
|
|
||||||
|
let selectedLabels = labels.filter((label) => label.selected && label.values);
|
||||||
|
if (searchTerm) {
|
||||||
|
selectedLabels = selectedLabels.map((label) => {
|
||||||
|
const searchResults = label.values!.filter((value) => {
|
||||||
|
// Always return selected values
|
||||||
|
if (value.selected) {
|
||||||
|
value.highlightParts = undefined;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const fuzzyMatchResult = fuzzyMatch(value.name.toLowerCase(), searchTerm.toLowerCase());
|
||||||
|
if (fuzzyMatchResult.found) {
|
||||||
|
value.highlightParts = fuzzyMatchResult.ranges;
|
||||||
|
value.order = fuzzyMatchResult.distance;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...label,
|
||||||
|
values: sortBy(searchResults, (value) => (value.selected ? -Infinity : value.order)),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Clear highlight parts when searchTerm is cleared
|
||||||
|
selectedLabels = this.state.labels
|
||||||
|
.filter((label) => label.selected && label.values)
|
||||||
|
.map((label) => ({
|
||||||
|
...label,
|
||||||
|
values: label?.values ? label.values.map((value) => ({ ...value, highlightParts: undefined })) : [],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<div className={styles.section}>
|
<div className={styles.section}>
|
||||||
@ -431,7 +470,7 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
|
|||||||
<FixedSizeList
|
<FixedSizeList
|
||||||
height={200}
|
height={200}
|
||||||
itemCount={label.values?.length || 0}
|
itemCount={label.values?.length || 0}
|
||||||
itemSize={25}
|
itemSize={28}
|
||||||
itemKey={(i) => (label.values as FacettableValue[])[i].name}
|
itemKey={(i) => (label.values as FacettableValue[])[i].name}
|
||||||
width={200}
|
width={200}
|
||||||
className={styles.valueList}
|
className={styles.valueList}
|
||||||
@ -447,6 +486,7 @@ export class UnthemedLokiLabelBrowser extends React.Component<BrowserProps, Brow
|
|||||||
name={label.name}
|
name={label.name}
|
||||||
value={value?.name}
|
value={value?.name}
|
||||||
active={value?.selected}
|
active={value?.selected}
|
||||||
|
highlightParts={value?.highlightParts}
|
||||||
onClick={this.onClickValue}
|
onClick={this.onClickValue}
|
||||||
searchTerm={searchTerm}
|
searchTerm={searchTerm}
|
||||||
/>
|
/>
|
||||||
|
@ -1,12 +1,20 @@
|
|||||||
import React, { ChangeEvent } from 'react';
|
import React, { ChangeEvent } from 'react';
|
||||||
import { Button, HorizontalGroup, Input, Label, LoadingPlaceholder, stylesFactory, withTheme } from '@grafana/ui';
|
import {
|
||||||
|
Button,
|
||||||
|
HorizontalGroup,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
LoadingPlaceholder,
|
||||||
|
stylesFactory,
|
||||||
|
withTheme,
|
||||||
|
BrowserLabel as PromLabel,
|
||||||
|
} from '@grafana/ui';
|
||||||
import PromQlLanguageProvider from '../language_provider';
|
import PromQlLanguageProvider from '../language_provider';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
import { FixedSizeList } from 'react-window';
|
import { FixedSizeList } from 'react-window';
|
||||||
|
|
||||||
import { GrafanaTheme } from '@grafana/data';
|
import { GrafanaTheme } from '@grafana/data';
|
||||||
import { Label as PromLabel } from './Label';
|
|
||||||
|
|
||||||
// Hard limit on labels to render
|
// Hard limit on labels to render
|
||||||
const MAX_LABEL_COUNT = 10000;
|
const MAX_LABEL_COUNT = 10000;
|
||||||
@ -591,7 +599,7 @@ export class UnthemedPrometheusMetricsBrowser extends React.Component<BrowserPro
|
|||||||
<FixedSizeList
|
<FixedSizeList
|
||||||
height={Math.min(200, LIST_ITEM_SIZE * (label.values?.length || 0))}
|
height={Math.min(200, LIST_ITEM_SIZE * (label.values?.length || 0))}
|
||||||
itemCount={label.values?.length || 0}
|
itemCount={label.values?.length || 0}
|
||||||
itemSize={25}
|
itemSize={28}
|
||||||
itemKey={(i) => (label.values as FacettableValue[])[i].name}
|
itemKey={(i) => (label.values as FacettableValue[])[i].name}
|
||||||
width={200}
|
width={200}
|
||||||
className={styles.valueList}
|
className={styles.valueList}
|
||||||
|
@ -29,7 +29,7 @@ if [ ! -d "$REPORT_PATH" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
WARNINGS_COUNT="$(find "$REPORT_PATH" -type f -name \*.log -print0 | xargs -0 grep -o "Warning: " | wc -l | xargs)"
|
WARNINGS_COUNT="$(find "$REPORT_PATH" -type f -name \*.log -print0 | xargs -0 grep -o "Warning: " | wc -l | xargs)"
|
||||||
WARNINGS_COUNT_LIMIT=1072
|
WARNINGS_COUNT_LIMIT=1074
|
||||||
|
|
||||||
if [ "$WARNINGS_COUNT" -gt $WARNINGS_COUNT_LIMIT ]; then
|
if [ "$WARNINGS_COUNT" -gt $WARNINGS_COUNT_LIMIT ]; then
|
||||||
echo -e "API Extractor warnings/errors $WARNINGS_COUNT exceeded $WARNINGS_COUNT_LIMIT so failing build.\n"
|
echo -e "API Extractor warnings/errors $WARNINGS_COUNT exceeded $WARNINGS_COUNT_LIMIT so failing build.\n"
|
||||||
|
Loading…
Reference in New Issue
Block a user