mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-19 23:09:18 +01:00
vmui: correct url encoding (#2067)
* fix: correct encode multi-line queries * fix: change autocomplete for correct arrows work * app/vmselect/vmui: `make vmui-update` * docs/CHANGELOG.md: document the bugfix for https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2039 Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
dcadec65b6
commit
70737ea4ac
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.79ff1ad2.css",
|
"main.css": "./static/css/main.79ff1ad2.css",
|
||||||
"main.js": "./static/js/main.b46d35b9.js",
|
"main.js": "./static/js/main.7d03dd65.js",
|
||||||
"static/js/27.cc1b69f7.chunk.js": "./static/js/27.cc1b69f7.chunk.js",
|
"static/js/27.cc1b69f7.chunk.js": "./static/js/27.cc1b69f7.chunk.js",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.79ff1ad2.css",
|
"static/css/main.79ff1ad2.css",
|
||||||
"static/js/main.b46d35b9.js"
|
"static/js/main.7d03dd65.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.b46d35b9.js"></script><link href="./static/css/main.79ff1ad2.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.7d03dd65.js"></script><link href="./static/css/main.79ff1ad2.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
2
app/vmselect/vmui/static/js/main.7d03dd65.js
Normal file
2
app/vmselect/vmui/static/js/main.7d03dd65.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,9 +1,12 @@
|
|||||||
import React, {FC, useEffect, useState} from "preact/compat";
|
import React, {FC, useEffect, useMemo, useRef, useState} from "preact/compat";
|
||||||
import {KeyboardEvent} from "react";
|
import {KeyboardEvent} from "react";
|
||||||
import {ErrorTypes} from "../../../../types";
|
import {ErrorTypes} from "../../../../types";
|
||||||
import Autocomplete from "@mui/material/Autocomplete";
|
import Popper from "@mui/material/Popper";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import {queryToBreakLine} from "../../../../utils/query-string";
|
import Box from "@mui/material/Box";
|
||||||
|
import Paper from "@mui/material/Paper";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import MenuList from "@mui/material/MenuList";
|
||||||
|
|
||||||
export interface QueryEditorProps {
|
export interface QueryEditorProps {
|
||||||
setHistoryIndex: (step: number, index: number) => void;
|
setHistoryIndex: (step: number, index: number) => void;
|
||||||
@ -28,18 +31,43 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||||||
queryOptions
|
queryOptions
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const [value, setValue] = useState(query);
|
|
||||||
const [downMetaKeys, setDownMetaKeys] = useState<string[]>([]);
|
const [downMetaKeys, setDownMetaKeys] = useState<string[]>([]);
|
||||||
|
const [focusField, setFocusField] = useState(false);
|
||||||
|
const [focusOption, setFocusOption] = useState(-1);
|
||||||
|
const autocompleteAnchorEl = useRef<HTMLDivElement>(null);
|
||||||
|
const wrapperEl = useRef<HTMLUListElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
const openAutocomplete = useMemo(() => {
|
||||||
setValue(queryToBreakLine(query));
|
return !(!autocomplete || downMetaKeys.length || query.length < 2 || !focusField);
|
||||||
}, [query]);
|
}, [query, downMetaKeys, autocomplete, focusField]);
|
||||||
|
|
||||||
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>): void => {
|
const actualOptions = useMemo(() => {
|
||||||
if (e.ctrlKey || e.metaKey) setDownMetaKeys([...downMetaKeys, e.key]);
|
if (!openAutocomplete) return [];
|
||||||
|
try {
|
||||||
|
const regexp = new RegExp(String(query), "i");
|
||||||
|
return queryOptions.filter((item) => regexp.test(item) && item !== query);
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}, [autocomplete, query, queryOptions]);
|
||||||
|
|
||||||
|
const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
const {key, ctrlKey, metaKey, shiftKey} = e;
|
||||||
|
if (ctrlKey || metaKey) setDownMetaKeys([...downMetaKeys, e.key]);
|
||||||
|
if (key === "ArrowUp" && openAutocomplete && actualOptions.length) {
|
||||||
|
e.preventDefault();
|
||||||
|
setFocusOption((prev) => prev === 0 ? 0 : prev - 1);
|
||||||
|
} else if (key === "ArrowDown" && openAutocomplete && actualOptions.length) {
|
||||||
|
e.preventDefault();
|
||||||
|
setFocusOption((prev) => prev >= actualOptions.length - 1 ? actualOptions.length - 1 : prev + 1);
|
||||||
|
} else if (key === "Enter" && openAutocomplete && actualOptions.length && !shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
setQuery(actualOptions[focusOption], index);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyUp = (e: KeyboardEvent<HTMLDivElement>): void => {
|
const handleKeyUp = (e: KeyboardEvent<HTMLDivElement>) => {
|
||||||
const {key, ctrlKey, metaKey} = e;
|
const {key, ctrlKey, metaKey} = e;
|
||||||
if (downMetaKeys.includes(key)) setDownMetaKeys(downMetaKeys.filter(k => k !== key));
|
if (downMetaKeys.includes(key)) setDownMetaKeys(downMetaKeys.filter(k => k !== key));
|
||||||
const ctrlMetaKey = ctrlKey || metaKey;
|
const ctrlMetaKey = ctrlKey || metaKey;
|
||||||
@ -52,25 +80,36 @@ const QueryEditor: FC<QueryEditorProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return <Autocomplete
|
useEffect(() => {
|
||||||
freeSolo
|
if (!wrapperEl.current) return;
|
||||||
fullWidth
|
const target = wrapperEl.current.childNodes[focusOption] as HTMLElement;
|
||||||
disableClearable
|
if (target?.scrollIntoView) target.scrollIntoView({block: "center"});
|
||||||
options={autocomplete && !downMetaKeys.length ? queryOptions : []}
|
}, [focusOption]);
|
||||||
onChange={(event, value) => setQuery(value, index)}
|
|
||||||
onKeyDown={handleKeyDown}
|
return <Box ref={autocompleteAnchorEl}>
|
||||||
onKeyUp={handleKeyUp}
|
<TextField
|
||||||
value={value}
|
defaultValue={query}
|
||||||
renderInput={(params) =>
|
fullWidth
|
||||||
<TextField
|
label={`Query ${index + 1}`}
|
||||||
{...params}
|
multiline
|
||||||
label={`Query ${index + 1}`}
|
error={!!error}
|
||||||
multiline
|
onFocus={() => setFocusField(true)}
|
||||||
error={!!error}
|
onBlur={() => setFocusField(false)}
|
||||||
onChange={(e) => setQuery(e.target.value, index)}
|
onKeyUp={handleKeyUp}
|
||||||
/>
|
onKeyDown={handleKeyDown}
|
||||||
}
|
onChange={(e) => setQuery(e.target.value, index)}
|
||||||
/>;
|
/>
|
||||||
|
<Popper open={openAutocomplete} anchorEl={autocompleteAnchorEl.current} placement="bottom-start">
|
||||||
|
<Paper elevation={3} sx={{ maxHeight: 300, overflow: "auto" }}>
|
||||||
|
<MenuList ref={wrapperEl} dense>
|
||||||
|
{actualOptions.map((item, i) =>
|
||||||
|
<MenuItem key={item} sx={{bgcolor: `rgba(0, 0, 0, ${i === focusOption ? 0.12 : 0})`}}>
|
||||||
|
{item}
|
||||||
|
</MenuItem>)}
|
||||||
|
</MenuList>
|
||||||
|
</Paper>
|
||||||
|
</Popper>
|
||||||
|
</Box>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueryEditor;
|
export default QueryEditor;
|
@ -11,7 +11,7 @@ import {
|
|||||||
} from "../../utils/time";
|
} from "../../utils/time";
|
||||||
import {getFromStorage} from "../../utils/storage";
|
import {getFromStorage} from "../../utils/storage";
|
||||||
import {getDefaultServer} from "../../utils/default-server-url";
|
import {getDefaultServer} from "../../utils/default-server-url";
|
||||||
import {breakLineToQuery, getQueryArray, getQueryStringValue} from "../../utils/query-string";
|
import {getQueryArray, getQueryStringValue} from "../../utils/query-string";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
export interface TimeState {
|
export interface TimeState {
|
||||||
@ -88,7 +88,7 @@ export function reducer(state: AppState, action: Action): AppState {
|
|||||||
case "SET_QUERY":
|
case "SET_QUERY":
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
query: action.payload.map(q => breakLineToQuery(q))
|
query: action.payload.map(q => q)
|
||||||
};
|
};
|
||||||
case "SET_QUERY_HISTORY":
|
case "SET_QUERY_HISTORY":
|
||||||
return {
|
return {
|
||||||
|
@ -48,7 +48,7 @@ export const setQueryStringValue = (newValue: Record<string, unknown>): void =>
|
|||||||
newQsValue.push(`g${i}.${queryKey}=${valueEncoded}`);
|
newQsValue.push(`g${i}.${queryKey}=${valueEncoded}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
newQsValue.push(`g${i}.expr=${breakLineToQuery(q)}`);
|
newQsValue.push(`g${i}.expr=${encodeURIComponent(q)}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
setQueryStringWithoutPageReload(newQsValue.join("&"));
|
setQueryStringWithoutPageReload(newQsValue.join("&"));
|
||||||
@ -69,7 +69,3 @@ export const getQueryArray = (): string[] => {
|
|||||||
return getQueryStringValue(`g${i}.expr`, "") as string;
|
return getQueryStringValue(`g${i}.expr`, "") as string;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const breakLineToQuery = (q: string): string => q.replace(/\n/g, "%20");
|
|
||||||
|
|
||||||
export const queryToBreakLine = (q: string): string => q.replace(/%20/g, "\n");
|
|
||||||
|
@ -39,7 +39,8 @@ sort: 15
|
|||||||
* BUGFIX: fix possible data race when searching for time series matching `{key=~"value|"}` filter over time range covering multipe days. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2032). Thanks to @waldoweng for the provided fix.
|
* BUGFIX: fix possible data race when searching for time series matching `{key=~"value|"}` filter over time range covering multipe days. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/2032). Thanks to @waldoweng for the provided fix.
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): do not send staleness markers on graceful shutdown. This follows Prometheus behavior. See [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2013#issuecomment-1006994079).
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): do not send staleness markers on graceful shutdown. This follows Prometheus behavior. See [this comment](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2013#issuecomment-1006994079).
|
||||||
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly set `__address__` label in `dockerswarm_sd_config`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2038). Thanks to @ashtuchkin for the fix.
|
* BUGFIX: [vmagent](https://docs.victoriametrics.com/vmagent.html): properly set `__address__` label in `dockerswarm_sd_config`. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2038). Thanks to @ashtuchkin for the fix.
|
||||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix incorrect calculations of graph limits on y axis. This could result in incorrect graph rendering in some cases. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2037).
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix incorrect calculations for graph limits on y axis. This could result in incorrect graph rendering in some cases. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2037).
|
||||||
|
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix handling for multi-line queries. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2039).
|
||||||
|
|
||||||
|
|
||||||
## [v1.71.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.71.0)
|
## [v1.71.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.71.0)
|
||||||
|
Loading…
Reference in New Issue
Block a user