UI: Add toggle to enable/disable metric autocomplete (#8134)

* UI: Add toggle to enable/disable metric autocomplete

This change adds a toggle to enable or disable the metric autocomplete
functionality. By default it is enabled. This is a port of a change I
did in [Thanos][1].

[1]: https://github.com/thanos-io/thanos/pull/3381

Signed-off-by: Jarod Watkins <jarod@42lines.net>

* Adding full variable name

Signed-off-by: Jarod Watkins <jarod@42lines.net>
This commit is contained in:
Jarod Watkins 2020-11-02 10:16:54 -05:00 committed by GitHub
parent 7558e9d3c3
commit 388a2d20c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 34 deletions

View file

@ -30,6 +30,7 @@ describe('ExpressionInput', () => {
// Do nothing.
},
loading: false,
enableMetricAutocomplete: true,
};
let expressionInput: ReactWrapper;
@ -174,8 +175,27 @@ describe('ExpressionInput', () => {
instance.createAutocompleteSection({ closeMenu: spyCloseMenu });
setTimeout(() => expect(spyCloseMenu).toHaveBeenCalled());
});
it('should not render list if enableMetricAutocomplete is false', () => {
const input = mount(
<ExpressionInput
autocompleteSections={{ title: ['foo', 'bar', 'baz'] }}
{...({} as any)}
enableMetricAutocomplete={false}
/>
);
const instance: any = input.instance();
const spyCloseMenu = jest.fn();
instance.createAutocompleteSection({ closeMenu: spyCloseMenu });
setTimeout(() => expect(spyCloseMenu).toHaveBeenCalled());
});
it('should render autosuggest-dropdown', () => {
const input = mount(<ExpressionInput autocompleteSections={{ title: ['foo', 'bar', 'baz'] }} {...({} as any)} />);
const input = mount(
<ExpressionInput
autocompleteSections={{ title: ['foo', 'bar', 'baz'] }}
{...({} as any)}
enableMetricAutocomplete={true}
/>
);
const instance: any = input.instance();
const spyGetMenuProps = jest.fn();
const sections = instance.createAutocompleteSection({

View file

@ -14,6 +14,7 @@ interface ExpressionInputProps {
autocompleteSections: { [key: string]: string[] };
executeQuery: () => void;
loading: boolean;
enableMetricAutocomplete: boolean;
}
interface ExpressionInputState {
@ -76,38 +77,39 @@ class ExpressionInput extends Component<ExpressionInputProps, ExpressionInputSta
const { inputValue = '', closeMenu, highlightedIndex } = downshift;
const { autocompleteSections } = this.props;
let index = 0;
const sections = inputValue!.length
? Object.entries(autocompleteSections).reduce((acc, [title, items]) => {
const matches = this.getSearchMatches(inputValue!, items);
return !matches.length
? acc
: [
...acc,
<ul className="autosuggest-dropdown-list" key={title}>
<li className="autosuggest-dropdown-header">{title}</li>
{matches
.slice(0, 100) // Limit DOM rendering to 100 results, as DOM rendering is sloooow.
.map(({ original, string: text }) => {
const itemProps = downshift.getItemProps({
key: original,
index,
item: original,
style: {
backgroundColor: highlightedIndex === index++ ? 'lightgray' : 'white',
},
});
return (
<li
key={title}
{...itemProps}
dangerouslySetInnerHTML={{ __html: sanitizeHTML(text, { allowedTags: ['strong'] }) }}
/>
);
})}
</ul>,
];
}, [] as JSX.Element[])
: [];
const sections =
inputValue!.length && this.props.enableMetricAutocomplete
? Object.entries(autocompleteSections).reduce((acc, [title, items]) => {
const matches = this.getSearchMatches(inputValue!, items);
return !matches.length
? acc
: [
...acc,
<ul className="autosuggest-dropdown-list" key={title}>
<li className="autosuggest-dropdown-header">{title}</li>
{matches
.slice(0, 100) // Limit DOM rendering to 100 results, as DOM rendering is sloooow.
.map(({ original, string: text }) => {
const itemProps = downshift.getItemProps({
key: original,
index,
item: original,
style: {
backgroundColor: highlightedIndex === index++ ? 'lightgray' : 'white',
},
});
return (
<li
key={title}
{...itemProps}
dangerouslySetInnerHTML={{ __html: sanitizeHTML(text, { allowedTags: ['strong'] }) }}
/>
);
})}
</ul>,
];
}, [] as JSX.Element[])
: [];
if (!sections.length) {
// This is ugly but is needed in order to sync state updates.

View file

@ -32,6 +32,7 @@ const defaultProps = {
onExecuteQuery: (): void => {
// Do nothing.
},
enableMetricAutocomplete: true,
};
describe('Panel', () => {

View file

@ -22,6 +22,7 @@ interface PanelProps {
removePanel: () => void;
onExecuteQuery: (query: string) => void;
pathPrefix: string;
enableMetricAutocomplete: boolean;
}
interface PanelState {
@ -233,6 +234,7 @@ class Panel extends Component<PanelProps, PanelState> {
onExpressionChange={this.handleExpressionChange}
executeQuery={this.executeQuery}
loading={this.state.loading}
enableMetricAutocomplete={this.props.enableMetricAutocomplete}
autocompleteSections={{
'Query History': pastQueries,
'Metric Names': metricNames,

View file

@ -22,9 +22,16 @@ interface PanelListProps extends RouteComponentProps {
metrics: string[];
useLocalTime: boolean;
queryHistoryEnabled: boolean;
enableMetricAutocomplete: boolean;
}
export const PanelListContent: FC<PanelListProps> = ({ metrics = [], useLocalTime, queryHistoryEnabled, ...rest }) => {
export const PanelListContent: FC<PanelListProps> = ({
metrics = [],
useLocalTime,
queryHistoryEnabled,
enableMetricAutocomplete,
...rest
}) => {
const [panels, setPanels] = useState(rest.panels);
const [historyItems, setLocalStorageHistoryItems] = useLocalStorage<string[]>('history', []);
@ -95,6 +102,7 @@ export const PanelListContent: FC<PanelListProps> = ({ metrics = [], useLocalTim
useLocalTime={useLocalTime}
metricNames={metrics}
pastQueries={queryHistoryEnabled ? historyItems : []}
enableMetricAutocomplete={enableMetricAutocomplete}
/>
))}
<Button className="mb-3" color="primary" onClick={addPanel}>
@ -108,6 +116,7 @@ const PanelList: FC<RouteComponentProps> = () => {
const [delta, setDelta] = useState(0);
const [useLocalTime, setUseLocalTime] = useLocalStorage('use-local-time', false);
const [enableQueryHistory, setEnableQueryHistory] = useLocalStorage('enable-query-history', false);
const [enableMetricAutocomplete, setEnableMetricAutocomplete] = useLocalStorage('enable-metric-autocomplete', true);
const pathPrefix = usePathPrefix();
const { response: metricsRes, error: metricsErr } = useFetch<string[]>(`${pathPrefix}/${API_PATH}/label/__name__/values`);
@ -148,6 +157,14 @@ const PanelList: FC<RouteComponentProps> = () => {
>
Use local time
</Checkbox>
<Checkbox
wrapperStyles={{ marginLeft: 20, display: 'inline-block' }}
id="metric-autocomplete"
onChange={({ target }) => setEnableMetricAutocomplete(target.checked)}
defaultChecked={enableMetricAutocomplete}
>
Enable metric autocomplete
</Checkbox>
{(delta > 30 || timeErr) && (
<Alert color="danger">
<strong>Warning: </strong>
@ -167,6 +184,7 @@ const PanelList: FC<RouteComponentProps> = () => {
useLocalTime={useLocalTime}
metrics={metricsRes.data}
queryHistoryEnabled={enableQueryHistory}
enableMetricAutocomplete={enableMetricAutocomplete}
/>
</>
);