diff --git a/packages/grafana-ui/src/components/Typeahead/Typeahead.test.tsx b/packages/grafana-ui/src/components/Typeahead/Typeahead.test.tsx
new file mode 100644
index 00000000000..34cac0ba896
--- /dev/null
+++ b/packages/grafana-ui/src/components/Typeahead/Typeahead.test.tsx
@@ -0,0 +1,46 @@
+import React from 'react';
+import { mount } from 'enzyme';
+
+import { Typeahead, State } from './Typeahead';
+import { TypeaheadItem } from './TypeaheadItem';
+import { CompletionItemKind } from '../../types';
+
+describe('Typeahead', () => {
+ const completionItemGroups = [{ label: 'my group', items: [{ label: 'first item' }] }];
+ describe('when closed', () => {
+ it('renders nothing when no items given', () => {
+ const component = mount();
+ expect(component.find('.typeahead')).toHaveLength(0);
+ });
+ it('renders nothing when items given', () => {
+ const component = mount();
+ expect(component.find('.typeahead')).toHaveLength(0);
+ });
+ });
+ describe('when open', () => {
+ it('renders given items and nothing is selected', () => {
+ const component = mount();
+ expect(component.find('.typeahead')).toHaveLength(1);
+ const items = component.find(TypeaheadItem);
+ expect(items).toHaveLength(2);
+ expect(items.get(0).props.item.kind).toEqual(CompletionItemKind.GroupTitle);
+ expect(items.get(0).props.isSelected).toBeFalsy();
+ expect(items.get(1).props.item.label).toEqual('first item');
+ expect(items.get(1).props.isSelected).toBeFalsy();
+ });
+ });
+ it('selected the first non-group item on moving to first item', () => {
+ const component = mount();
+ expect(component.find('.typeahead')).toHaveLength(1);
+ let items = component.find(TypeaheadItem);
+
+ expect(items).toHaveLength(2);
+ expect((component.state() as State).typeaheadIndex).toBe(null);
+ (component.instance() as Typeahead).moveMenuIndex(1);
+ expect((component.state() as State).typeaheadIndex).toBe(1);
+ component.setProps({});
+ items = component.find(TypeaheadItem);
+ expect(items.get(0).props.isSelected).toBeFalsy();
+ expect(items.get(1).props.isSelected).toBeTruthy();
+ });
+});
diff --git a/packages/grafana-ui/src/components/Typeahead/Typeahead.tsx b/packages/grafana-ui/src/components/Typeahead/Typeahead.tsx
index 10f5c3d8736..20526e17d9c 100644
--- a/packages/grafana-ui/src/components/Typeahead/Typeahead.tsx
+++ b/packages/grafana-ui/src/components/Typeahead/Typeahead.tsx
@@ -20,13 +20,13 @@ interface Props {
isOpen?: boolean;
}
-interface State {
+export interface State {
allItems: CompletionItem[];
listWidth: number;
listHeight: number;
itemHeight: number;
hoveredItem: number | null;
- typeaheadIndex: number;
+ typeaheadIndex: number | null;
}
export class Typeahead extends React.PureComponent {
@@ -34,7 +34,14 @@ export class Typeahead extends React.PureComponent {
context!: React.ContextType;
listRef = createRef();
- state: State = { hoveredItem: null, typeaheadIndex: 1, allItems: [], listWidth: -1, listHeight: -1, itemHeight: -1 };
+ state: State = {
+ hoveredItem: null,
+ typeaheadIndex: null,
+ allItems: [],
+ listWidth: -1,
+ listHeight: -1,
+ itemHeight: -1,
+ };
componentDidMount = () => {
if (this.props.menuRef) {
@@ -63,7 +70,12 @@ export class Typeahead extends React.PureComponent {
};
componentDidUpdate = (prevProps: Readonly, prevState: Readonly) => {
- if (prevState.typeaheadIndex !== this.state.typeaheadIndex && this.listRef && this.listRef.current) {
+ if (
+ this.state.typeaheadIndex !== null &&
+ prevState.typeaheadIndex !== this.state.typeaheadIndex &&
+ this.listRef &&
+ this.listRef.current
+ ) {
if (this.state.typeaheadIndex === 1) {
this.listRef.current.scrollToItem(0); // special case for handling the first group label
return;
@@ -75,7 +87,7 @@ export class Typeahead extends React.PureComponent {
const allItems = flattenGroupItems(this.props.groupedItems);
const longestLabel = calculateLongestLabel(allItems);
const { listWidth, listHeight, itemHeight } = calculateListSizes(this.context, allItems, longestLabel);
- this.setState({ listWidth, listHeight, itemHeight, allItems });
+ this.setState({ listWidth, listHeight, itemHeight, allItems, typeaheadIndex: null });
}
};
@@ -95,7 +107,8 @@ export class Typeahead extends React.PureComponent {
const itemCount = this.state.allItems.length;
if (itemCount) {
// Select next suggestion
- let newTypeaheadIndex = modulo(this.state.typeaheadIndex + moveAmount, itemCount);
+ const typeaheadIndex = this.state.typeaheadIndex || 0;
+ let newTypeaheadIndex = modulo(typeaheadIndex + moveAmount, itemCount);
if (this.state.allItems[newTypeaheadIndex].kind === CompletionItemKind.GroupTitle) {
newTypeaheadIndex = modulo(newTypeaheadIndex + moveAmount, itemCount);
@@ -110,7 +123,7 @@ export class Typeahead extends React.PureComponent {
};
insertSuggestion = () => {
- if (this.props.onSelectSuggestion) {
+ if (this.props.onSelectSuggestion && this.state.typeaheadIndex !== null) {
this.props.onSelectSuggestion(this.state.allItems[this.state.typeaheadIndex]);
}
};
@@ -144,6 +157,7 @@ export class Typeahead extends React.PureComponent {
const { allItems, listWidth, listHeight, itemHeight, hoveredItem, typeaheadIndex } = this.state;
const showDocumentation = hoveredItem || typeaheadIndex;
+ const documentationItem = allItems[hoveredItem ? hoveredItem : typeaheadIndex || 0];
return (
@@ -169,7 +183,7 @@ export class Typeahead extends React.PureComponent {
return (
(this.props.onSelectSuggestion ? this.props.onSelectSuggestion(item) : {})}
- isSelected={allItems[typeaheadIndex] === item}
+ isSelected={typeaheadIndex === null ? false : allItems[typeaheadIndex] === item}
item={item}
prefix={prefix}
style={style}
@@ -181,9 +195,7 @@ export class Typeahead extends React.PureComponent {
- {showDocumentation && (
-
- )}
+ {showDocumentation && }
);
}
diff --git a/public/sass/components/_slate_editor.scss b/public/sass/components/_slate_editor.scss
index 1870794c774..010180aa4e6 100644
--- a/public/sass/components/_slate_editor.scss
+++ b/public/sass/components/_slate_editor.scss
@@ -35,7 +35,6 @@
border: $panel-border;
max-height: calc(66vh);
overflow-y: scroll;
- max-width: calc(66%);
overflow-x: hidden;
outline: none;
list-style: none;