Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-line kql bar #70140

Merged
merged 29 commits into from
Jul 8, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6ebe65b
Multiline kql bar
snide Jun 26, 2020
5fc0bc4
fix id
snide Jun 26, 2020
657f624
use visibility rather than display to hide stuff, cross fingers for t…
snide Jun 29, 2020
8117fcd
Merge remote-tracking branch 'upstream/master' into kql/multiline
snide Jun 29, 2020
4c227b2
another vis trick for tests
snide Jun 30, 2020
e94cc02
quasi fix tests, still some failures
snide Jun 30, 2020
9b8b57a
caroline feedback
snide Jun 30, 2020
5e4551a
fun!
snide Jun 30, 2020
de5401d
Merge remote-tracking branch 'upstream/master' into kql/multiline
snide Jul 1, 2020
a90b77b
fix for mouse
snide Jul 1, 2020
7ce67a9
fix test
XavierM Jul 1, 2020
5c98f58
Merge branch 'master' into kql/multiline
elasticmachine Jul 1, 2020
5c2e5ce
Merge branch 'master' of github.com:elastic/kibana into kql/multiline
XavierM Jul 2, 2020
00f297b
check api
XavierM Jul 2, 2020
156621f
Merge branch 'master' of github.com:elastic/kibana into kql/multiline
XavierM Jul 2, 2020
61b1087
Merge branch 'master' into kql/multiline
elasticmachine Jul 2, 2020
1b72ca4
fix unit test on query_string_input
XavierM Jul 2, 2020
8d686b0
Merge branch 'master' of github.com:elastic/kibana into kql/multiline
XavierM Jul 3, 2020
1c75023
Fix cypress test
XavierM Jul 3, 2020
7ee38c1
Merge branch 'master' of github.com:elastic/kibana into kql/multiline
XavierM Jul 3, 2020
98b256c
Merge branch 'master' into kql/multiline
elasticmachine Jul 5, 2020
9be2de5
Merge branch 'master' of github.com:elastic/kibana into kql/multiline
XavierM Jul 6, 2020
35cb802
handle the resize of the height of the textarea when the window have …
XavierM Jul 6, 2020
95683af
Merge branch 'master' of github.com:elastic/kibana into kql/multiline
XavierM Jul 6, 2020
8a0ba6b
Merge branch 'master' into kql/multiline
elasticmachine Jul 7, 2020
709caaf
Merge branch 'master' into kql/multiline
elasticmachine Jul 7, 2020
dc0cade
Merge branch 'master' of github.com:elastic/kibana into pr/70140
Jul 8, 2020
101b10e
Merge branch 'master' into kql/multiline
elasticmachine Jul 8, 2020
fc9852d
Merge branch 'master' into kql/multiline
elasticmachine Jul 8, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions src/plugins/data/public/ui/query_string_input/_query_bar.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
.kbnQueryBar__wrap {
max-width: 100%;
z-index: $euiZContentMenu;
}

.kbnQueryBar__textarea {
z-index: $euiZContentMenu;
resize: none !important; // When in the group, it will autosize
height: $euiSizeXXL;
// Unlike most inputs within layout control groups, the text area still needs a border.
// These adjusts help it sit above the control groups shadow to line up correctly.
padding-top: $euiSizeS + 3px !important;
transform: translateY(-2px);
padding: $euiSizeS - 1px;

&:not(:focus) {
@include euiYScrollWithShadows;
white-space: nowrap;
overflow-y: hidden;
overflow-x: hidden;
border: none;
box-shadow: none;
}

// When focused, let it scroll
&:focus {
overflow-x: auto;
overflow-y: auto;
width: calc(100% + 1px); // To overtake the group's fake border
white-space: normal;
XavierM marked this conversation as resolved.
Show resolved Hide resolved
}
}

@include euiBreakpoint('xs', 's') {
.kbnQueryBar--withDatePicker {
> :first-child {
Expand All @@ -16,5 +49,12 @@
// sass-lint:disable-block no-important
flex-grow: 0 !important;
flex-basis: auto !important;

&.kbnQueryBar__datePickerWrapper-isHidden {
width: 0;
overflow: hidden;
margin-right: 0;
margin-left: 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ interface Props {

export function QueryBarTopRow(props: Props) {
const [isDateRangeInvalid, setIsDateRangeInvalid] = useState(false);
const [isQueryInputFocused, setIsQueryInputFocused] = useState(false);

const kibana = useKibana<IDataPluginServices>();
const { uiSettings, notifications, storage, appName, docLinks } = kibana.services;
Expand Down Expand Up @@ -105,6 +106,10 @@ export function QueryBarTopRow(props: Props) {
});
}

function onChangeQueryInputFocus(isFocused: boolean) {
setIsQueryInputFocused(isFocused);
}

function onTimeChange({
start,
end,
Expand Down Expand Up @@ -180,6 +185,7 @@ export function QueryBarTopRow(props: Props) {
query={props.query!}
screenTitle={props.screenTitle}
onChange={onQueryChange}
onChangeQueryInputFocus={onChangeQueryInputFocus}
onSubmit={onInputSubmit}
persistedLog={persistedLog}
dataTestSubj={props.dataTestSubj}
Expand Down Expand Up @@ -264,8 +270,12 @@ export function QueryBarTopRow(props: Props) {
};
});

const wrapperClasses = classNames('kbnQueryBar__datePickerWrapper', {
'kbnQueryBar__datePickerWrapper-isHidden': isQueryInputFocused,
});

return (
<EuiFlexItem className="kbnQueryBar__datePickerWrapper">
<EuiFlexItem className={wrapperClasses}>
<EuiSuperDatePicker
start={props.dateRangeFrom}
end={props.dateRangeTo}
Expand Down
149 changes: 102 additions & 47 deletions src/plugins/data/public/ui/query_string_input/query_string_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import React from 'react';
import { i18n } from '@kbn/i18n';

import {
EuiFieldText,
EuiTextArea,
EuiOutsideClickDetector,
PopoverAnchorPosition,
EuiFlexGroup,
EuiFlexItem,
EuiButton,
EuiLink,
htmlIdGenerator,
} from '@elastic/eui';

import { FormattedMessage } from '@kbn/i18n/react';
Expand All @@ -49,12 +50,13 @@ interface Props {
query: Query;
disableAutoFocus?: boolean;
screenTitle?: string;
prepend?: React.ComponentProps<typeof EuiFieldText>['prepend'];
prepend?: any;
persistedLog?: PersistedLog;
bubbleSubmitEvent?: boolean;
placeholder?: string;
languageSwitcherPopoverAnchorPosition?: PopoverAnchorPosition;
onChange?: (query: Query) => void;
onChangeQueryInputFocus?: (isFocused: boolean) => void;
onSubmit?: (query: Query) => void;
dataTestSubj?: string;
}
Expand Down Expand Up @@ -92,7 +94,7 @@ export class QueryStringInputUI extends Component<Props, State> {
indexPatterns: [],
};

public inputRef: HTMLInputElement | null = null;
public inputRef: HTMLTextAreaElement | null = null;

private persistedLog: PersistedLog | undefined;
private abortController: AbortController | undefined;
Expand Down Expand Up @@ -222,27 +224,32 @@ export class QueryStringInputUI extends Component<Props, State> {
this.onChange({ query: value, language: this.props.query.language });
};

private onInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
private onInputChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
this.onQueryStringChange(event.target.value);
if (event.target.value === '') {
this.handleRemoveHeight();
} else {
this.handleAutoHeight();
}
};

private onClickInput = (event: React.MouseEvent<HTMLInputElement>) => {
if (event.target instanceof HTMLInputElement) {
private onClickInput = (event: React.MouseEvent<HTMLTextAreaElement>) => {
if (event.target instanceof HTMLTextAreaElement) {
this.onQueryStringChange(event.target.value);
}
};

private onKeyUp = (event: React.KeyboardEvent<HTMLInputElement>) => {
private onKeyUp = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if ([KEY_CODES.LEFT, KEY_CODES.RIGHT, KEY_CODES.HOME, KEY_CODES.END].includes(event.keyCode)) {
this.setState({ isSuggestionsVisible: true });
if (event.target instanceof HTMLInputElement) {
if (event.target instanceof HTMLTextAreaElement) {
this.onQueryStringChange(event.target.value);
}
}
};

private onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.target instanceof HTMLInputElement) {
private onKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (event.target instanceof HTMLTextAreaElement) {
const { isSuggestionsVisible, index } = this.state;
const preventDefault = event.preventDefault.bind(event);
const { target, key, metaKey } = event;
Expand Down Expand Up @@ -438,6 +445,10 @@ export class QueryStringInputUI extends Component<Props, State> {
if (this.state.isSuggestionsVisible) {
this.setState({ isSuggestionsVisible: false, index: null });
}
this.handleBlurHeight();
if (this.props.onChangeQueryInputFocus) {
this.props.onChangeQueryInputFocus(false);
}
};

private onClickSuggestion = (suggestion: QuerySuggestion) => {
Expand All @@ -459,6 +470,8 @@ export class QueryStringInputUI extends Component<Props, State> {
this.setState({ index });
};

textareaId = htmlIdGenerator()();

public componentDidMount() {
const parsedQuery = fromUser(toUser(this.props.query.query));
if (!isEqual(this.props.query.query, parsedQuery)) {
Expand Down Expand Up @@ -493,6 +506,11 @@ export class QueryStringInputUI extends Component<Props, State> {
selectionStart: null,
selectionEnd: null,
});
if (document.activeElement !== null && document.activeElement.id === this.textareaId) {
this.handleAutoHeight();
} else {
this.handleRemoveHeight();
}
}
}

Expand All @@ -501,6 +519,34 @@ export class QueryStringInputUI extends Component<Props, State> {
this.componentIsUnmounting = true;
}

handleAutoHeight = () => {
if (this.inputRef !== null) {
this.inputRef.style.setProperty('height', `${this.inputRef.scrollHeight}px`, 'important');
}
XavierM marked this conversation as resolved.
Show resolved Hide resolved
};

handleRemoveHeight = () => {
if (this.inputRef !== null) {
this.inputRef.style.removeProperty('height');
}
};

handleBlurHeight = () => {
if (this.inputRef !== null) {
this.handleRemoveHeight();
this.inputRef.scrollTop = 0;
}
};

handleOnFocus = () => {
if (this.props.onChangeQueryInputFocus) {
this.props.onChangeQueryInputFocus(true);
}
requestAnimationFrame(() => {
this.handleAutoHeight();
});
};

public render() {
const isSuggestionsVisible = this.state.isSuggestionsVisible && {
'aria-controls': 'kbnTypeahead__items',
Expand All @@ -509,20 +555,24 @@ export class QueryStringInputUI extends Component<Props, State> {
const ariaCombobox = { ...isSuggestionsVisible, role: 'combobox' };

return (
<EuiOutsideClickDetector onOutsideClick={this.onOutsideClick}>
<div
{...ariaCombobox}
style={{ position: 'relative' }}
aria-label={i18n.translate('data.query.queryBar.comboboxAriaLabel', {
defaultMessage: 'Search and filter the {pageType} page',
values: { pageType: this.services.appName },
})}
aria-haspopup="true"
aria-expanded={this.state.isSuggestionsVisible}
>
<div role="search">
<div className="kuiLocalSearchAssistedInput">
<EuiFieldText
<div className="euiFormControlLayout euiFormControlLayout--group kbnQueryBar__wrap">
{this.props.prepend}
<EuiOutsideClickDetector onOutsideClick={this.onOutsideClick}>
<div
{...ariaCombobox}
style={{ position: 'relative', width: '100%' }}
aria-label={i18n.translate('data.query.queryBar.comboboxAriaLabel', {
defaultMessage: 'Search and filter the {pageType} page',
values: { pageType: this.services.appName },
})}
aria-haspopup="true"
aria-expanded={this.state.isSuggestionsVisible}
>
<div
role="search"
className="euiFormControlLayout__childrenWrapper kuiLocalSearchAssistedInput"
>
<EuiTextArea
placeholder={
this.props.placeholder ||
i18n.translate('data.query.queryBar.searchInputPlaceholder', {
Expand All @@ -534,9 +584,15 @@ export class QueryStringInputUI extends Component<Props, State> {
onKeyUp={this.onKeyUp}
onChange={this.onInputChange}
onClick={this.onClickInput}
onFocus={this.handleOnFocus}
className="kbnQueryBar__textarea"
fullWidth
autoFocus={!this.props.disableAutoFocus}
inputRef={(node) => {
rows={1}
id={this.textareaId}
autoFocus={
this.props.onChangeQueryInputFocus ? false : !this.props.disableAutoFocus
}
inputRef={(node: any) => {
if (node) {
this.inputRef = node;
}
Expand All @@ -547,7 +603,6 @@ export class QueryStringInputUI extends Component<Props, State> {
defaultMessage: 'Start typing to search and filter the {pageType} page',
values: { pageType: this.services.appName },
})}
type="text"
aria-autocomplete="list"
aria-controls={this.state.isSuggestionsVisible ? 'kbnTypeahead__items' : undefined}
aria-activedescendant={
Expand All @@ -556,29 +611,29 @@ export class QueryStringInputUI extends Component<Props, State> {
: undefined
}
role="textbox"
prepend={this.props.prepend}
append={
<QueryLanguageSwitcher
language={this.props.query.language}
anchorPosition={this.props.languageSwitcherPopoverAnchorPosition}
onSelectLanguage={this.onSelectLanguage}
/>
}
data-test-subj={this.props.dataTestSubj || 'queryInput'}
/>
>
{this.getQueryString()}
</EuiTextArea>
</div>
</div>

<SuggestionsComponent
show={this.state.isSuggestionsVisible}
suggestions={this.state.suggestions.slice(0, this.state.suggestionLimit)}
index={this.state.index}
onClick={this.onClickSuggestion}
onMouseEnter={this.onMouseEnterSuggestion}
loadMore={this.increaseLimit}
/>
</div>
</EuiOutsideClickDetector>
<SuggestionsComponent
show={this.state.isSuggestionsVisible}
suggestions={this.state.suggestions.slice(0, this.state.suggestionLimit)}
index={this.state.index}
onClick={this.onClickSuggestion}
onMouseEnter={this.onMouseEnterSuggestion}
loadMore={this.increaseLimit}
/>
</div>
</EuiOutsideClickDetector>

<QueryLanguageSwitcher
language={this.props.query.language}
anchorPosition={this.props.languageSwitcherPopoverAnchorPosition}
onSelectLanguage={this.onSelectLanguage}
/>
</div>
);
}
}
Expand Down
9 changes: 3 additions & 6 deletions src/plugins/data/public/ui/typeahead/_suggestion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ $kbnTypeaheadTypes: (
color: $euiTextColor;
background-color: $euiColorEmptyShade;
position: absolute;
top: -1px;
top: -2px;
z-index: $euiZContentMenu;
width: 100%;
border-bottom-left-radius: $euiBorderRadius;
Expand Down Expand Up @@ -56,7 +56,6 @@ $kbnTypeaheadTypes: (
.kbnTypeahead__item.active {
background-color: $euiColorLightestShade;


.kbnSuggestionItem__callout {
background: $euiColorEmptyShade;
}
Expand Down Expand Up @@ -130,7 +129,6 @@ $kbnTypeaheadTypes: (
align-items: center;
}


.kbnSuggestionItem__text {
flex-grow: 0; /* 2 */
flex-basis: auto; /* 2 */
Expand All @@ -142,16 +140,15 @@ $kbnTypeaheadTypes: (
color: $euiTextColor;
}


.kbnSuggestionItem__description {
color: $euiColorDarkShade;
overflow: hidden;
text-overflow: ellipsis;
margin-left: $euiSizeXL;

&:empty {
flex-grow: 0;
margin-left:0;
margin-left: 0;
}
}

Expand Down