QueryField: Handle autocomplete better (#81484)

* extract out function + add unit tests

* add feature toggle and default it to on
This commit is contained in:
Ashley Harrison
2024-01-31 10:01:20 +00:00
committed by GitHub
parent f896072cd7
commit 39057552dc
8 changed files with 118 additions and 8 deletions

View File

@@ -0,0 +1,53 @@
import { getNumCharsToDelete } from './suggestions';
describe('suggestions', () => {
describe('getNumCharsToDelete', () => {
describe('when slateAutocomplete is enabled', () => {
let originalBootData = window.grafanaBootData;
// hacky way to enable the feature toggle for this test
beforeEach(() => {
if (originalBootData) {
window.grafanaBootData = {
...originalBootData,
settings: {
...originalBootData.settings,
featureToggles: {
...originalBootData.settings.featureToggles,
slateAutocomplete: true,
},
},
};
}
});
afterEach(() => {
window.grafanaBootData = originalBootData;
});
const splunkCleanText = (s: string) => s.replace(/[{}[\]="(),!~+\-*/^%:\\]/g, '').trim();
it.each([
// | represents the caret position
['$query0 ', '', '', false, 0, undefined, { forward: 0, backward: 0 }], // "|" --> "$query0 |"
['$query0 ', '$que', '$que', false, 0, undefined, { forward: 0, backward: 4 }], // "$que|" --> "$query0 |"
['$query0 ', '$q', '$que', false, 0, undefined, { forward: 2, backward: 2 }], // "$q|ue" --> "$query0 |"
['$query0 ', '$que', '($que)', false, 0, splunkCleanText, { forward: 0, backward: 4 }], // "($que|)" --> "($query0 |)"
['$query0 ', '$que', 'esarvotionUsagePercent=$que', false, 0, undefined, { forward: 0, backward: 4 }], // "esarvotionUsagePercent=$que|" --> "esarvotionUsagePercent=$query0 |"
])(
'should calculate the correct number of characters to delete forwards and backwards',
(suggestionText, typeaheadPrefix, typeaheadText, preserveSuffix, deleteBackwards, cleanText, expected) => {
expect(
getNumCharsToDelete(
suggestionText,
typeaheadPrefix,
typeaheadText,
preserveSuffix,
deleteBackwards,
cleanText
)
).toEqual(expected);
}
);
});
});
});

View File

@@ -2,6 +2,8 @@ import { debounce, sortBy } from 'lodash';
import React from 'react';
import { Editor, Plugin as SlatePlugin } from 'slate-react';
import { BootData } from '@grafana/data';
import { Typeahead } from '../components/Typeahead/Typeahead';
import { CompletionItem, SuggestionsState, TypeaheadInput, TypeaheadOutput } from '../types';
import { makeFragment, SearchFunctionType } from '../utils';
@@ -11,6 +13,12 @@ import TOKEN_MARK from './slate-prism/TOKEN_MARK';
export const TYPEAHEAD_DEBOUNCE = 250;
declare global {
interface Window {
grafanaBootData?: BootData;
}
}
// Commands added to the editor by this plugin.
interface SuggestionsPluginCommands {
selectSuggestion: (suggestion: CompletionItem) => Editor;
@@ -157,13 +165,14 @@ export function SuggestionsPlugin({
});
}
// Remove the current, incomplete text and replace it with the selected suggestion
const backward = suggestion.deleteBackwards || typeaheadPrefix.length;
const text = cleanText ? cleanText(typeaheadText) : typeaheadText;
const suffixLength = text.length - typeaheadPrefix.length;
const offset = typeaheadText.indexOf(typeaheadPrefix);
const midWord = typeaheadPrefix && ((suffixLength > 0 && offset > -1) || suggestionText === typeaheadText);
const forward = midWord && !preserveSuffix ? suffixLength + offset : 0;
const { forward, backward } = getNumCharsToDelete(
suggestionText,
typeaheadPrefix,
typeaheadText,
preserveSuffix,
suggestion.deleteBackwards,
cleanText
);
// If new-lines, apply suggestion as block
if (suggestionText.match(/\n/)) {
@@ -337,3 +346,35 @@ const handleTypeahead = async (
// Bogus edit to force re-render
editor.blur().focus();
};
export function getNumCharsToDelete(
suggestionText: string,
typeaheadPrefix: string,
typeaheadText: string,
preserveSuffix: boolean,
deleteBackwards?: number,
cleanText?: (text: string) => string
) {
// remove the current, incomplete text and replace it with the selected suggestion
const backward = deleteBackwards || typeaheadPrefix.length;
const text = cleanText ? cleanText(typeaheadText) : typeaheadText;
const offset = typeaheadText.indexOf(typeaheadPrefix);
let forward: number;
if (window.grafanaBootData?.settings.featureToggles['slateAutocomplete']) {
const suffixLength =
offset > -1 ? text.length - offset - typeaheadPrefix.length : text.length - typeaheadPrefix.length;
const midWord = Boolean((typeaheadPrefix && suffixLength > 0) || suggestionText === typeaheadText);
forward = midWord && !preserveSuffix ? suffixLength + offset : 0;
} else {
const suffixLength = text.length - typeaheadPrefix.length;
const midWord = typeaheadPrefix && ((suffixLength > 0 && offset > -1) || suggestionText === typeaheadText);
forward = midWord && !preserveSuffix ? suffixLength + offset : 0;
}
return {
forward,
backward,
};
}