mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Fix typeahead behaviour for QueryField
These changes were originally intended to address a bug whereby a suggestion for an already selected label value continues to appear. However, they also appear to fix several other problems in the area: - Wrong suggestions when using negated label matching operators - Misaligned label value suggestion replacements Related: #13484
This commit is contained in:
parent
b7599212e0
commit
a8c5ab76b3
@ -111,7 +111,7 @@ export function willApplySuggestion(
|
|||||||
|
|
||||||
case 'context-label-values': {
|
case 'context-label-values': {
|
||||||
// Always add quotes and remove existing ones instead
|
// Always add quotes and remove existing ones instead
|
||||||
if (!(typeaheadText.startsWith('="') || typeaheadText.startsWith('"'))) {
|
if (!typeaheadText.match(/^(!?=~?"|")/)) {
|
||||||
suggestion = `"${suggestion}`;
|
suggestion = `"${suggestion}`;
|
||||||
}
|
}
|
||||||
if (getNextCharacter() !== '"') {
|
if (getNextCharacter() !== '"') {
|
||||||
@ -421,7 +421,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
|||||||
const containsMetric = selector.indexOf('__name__=') > -1;
|
const containsMetric = selector.indexOf('__name__=') > -1;
|
||||||
const existingKeys = parsedSelector ? parsedSelector.labelKeys : [];
|
const existingKeys = parsedSelector ? parsedSelector.labelKeys : [];
|
||||||
|
|
||||||
if ((text && text.startsWith('=')) || _.includes(wrapperClasses, 'attr-value')) {
|
if ((text && text.match(/^!?=~?/)) || _.includes(wrapperClasses, 'attr-value')) {
|
||||||
// Label values
|
// Label values
|
||||||
if (labelKey && this.state.labelValues[selector] && this.state.labelValues[selector][labelKey]) {
|
if (labelKey && this.state.labelValues[selector] && this.state.labelValues[selector][labelKey]) {
|
||||||
const labelValues = this.state.labelValues[selector][labelKey];
|
const labelValues = this.state.labelValues[selector][labelKey];
|
||||||
@ -571,10 +571,10 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
|||||||
<button className="btn navbar-button navbar-button--tight">Log labels</button>
|
<button className="btn navbar-button navbar-button--tight">Log labels</button>
|
||||||
</Cascader>
|
</Cascader>
|
||||||
) : (
|
) : (
|
||||||
<Cascader options={metricsOptions} onChange={this.onChangeMetrics}>
|
<Cascader options={metricsOptions} onChange={this.onChangeMetrics}>
|
||||||
<button className="btn navbar-button navbar-button--tight">Metrics</button>
|
<button className="btn navbar-button navbar-button--tight">Metrics</button>
|
||||||
</Cascader>
|
</Cascader>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="prom-query-field-wrapper">
|
<div className="prom-query-field-wrapper">
|
||||||
<div className="slate-query-field-wrapper">
|
<div className="slate-query-field-wrapper">
|
||||||
|
@ -228,7 +228,13 @@ class QueryField extends React.PureComponent<TypeaheadFieldProps, TypeaheadField
|
|||||||
const offset = range.startOffset;
|
const offset = range.startOffset;
|
||||||
const text = selection.anchorNode.textContent;
|
const text = selection.anchorNode.textContent;
|
||||||
let prefix = text.substr(0, offset);
|
let prefix = text.substr(0, offset);
|
||||||
if (cleanText) {
|
|
||||||
|
// Label values could have valid characters erased if `cleanText()` is
|
||||||
|
// blindly applied, which would undesirably interfere with suggestions
|
||||||
|
const labelValueMatch = prefix.match(/(?:!?=~?"?|")(.*)/);
|
||||||
|
if (labelValueMatch) {
|
||||||
|
prefix = labelValueMatch[1];
|
||||||
|
} else if (cleanText) {
|
||||||
prefix = cleanText(prefix);
|
prefix = cleanText(prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export const cleanText = s => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
|||||||
|
|
||||||
// const cleanSelectorRegexp = /\{(\w+="[^"\n]*?")(,\w+="[^"\n]*?")*\}/;
|
// const cleanSelectorRegexp = /\{(\w+="[^"\n]*?")(,\w+="[^"\n]*?")*\}/;
|
||||||
const selectorRegexp = /\{[^}]*?\}/;
|
const selectorRegexp = /\{[^}]*?\}/;
|
||||||
const labelRegexp = /\b\w+="[^"\n]*?"/g;
|
const labelRegexp = /\b(\w+)(!?=~?)("[^"\n]*?")/g;
|
||||||
export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any[]; selector: string } {
|
export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any[]; selector: string } {
|
||||||
if (!query.match(selectorRegexp)) {
|
if (!query.match(selectorRegexp)) {
|
||||||
// Special matcher for metrics
|
// Special matcher for metrics
|
||||||
@ -66,11 +66,8 @@ export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any
|
|||||||
// Extract clean labels to form clean selector, incomplete labels are dropped
|
// Extract clean labels to form clean selector, incomplete labels are dropped
|
||||||
const selector = query.slice(prefixOpen, suffixClose);
|
const selector = query.slice(prefixOpen, suffixClose);
|
||||||
const labels = {};
|
const labels = {};
|
||||||
selector.replace(labelRegexp, match => {
|
selector.replace(labelRegexp, (_, key, operator, value) => {
|
||||||
const delimiterIndex = match.indexOf('=');
|
labels[key] = { value, operator };
|
||||||
const key = match.slice(0, delimiterIndex);
|
|
||||||
const value = match.slice(delimiterIndex + 1, match.length);
|
|
||||||
labels[key] = value;
|
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -78,12 +75,12 @@ export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any
|
|||||||
const metricPrefix = query.slice(0, prefixOpen);
|
const metricPrefix = query.slice(0, prefixOpen);
|
||||||
const metricMatch = metricPrefix.match(/[A-Za-z:][\w:]*$/);
|
const metricMatch = metricPrefix.match(/[A-Za-z:][\w:]*$/);
|
||||||
if (metricMatch) {
|
if (metricMatch) {
|
||||||
labels['__name__'] = `"${metricMatch[0]}"`;
|
labels['__name__'] = { value: `"${metricMatch[0]}"`, operator: '=' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build sorted selector
|
// Build sorted selector
|
||||||
const labelKeys = Object.keys(labels).sort();
|
const labelKeys = Object.keys(labels).sort();
|
||||||
const cleanSelector = labelKeys.map(key => `${key}=${labels[key]}`).join(',');
|
const cleanSelector = labelKeys.map(key => `${key}${labels[key].operator}${labels[key].value}`).join(',');
|
||||||
|
|
||||||
const selectorString = ['{', cleanSelector, '}'].join('');
|
const selectorString = ['{', cleanSelector, '}'].join('');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user