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

[UI Framework] Add support for multi-line queries to LocalNavSearch component of UI Framework. #11975

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
35 changes: 35 additions & 0 deletions ui_framework/components/local_nav/_local_search.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
/**
* 1. If we're using an expandable input, make sure that it doesn't force sibling elements to expand
* with it.
*/
.kuiLocalSearch {
display: flex;
align-items: flex-start; /* 1 */
width: 100%;
}

Expand Down Expand Up @@ -52,6 +57,36 @@
border-radius: 0;
}

/**
* 1. When it expands, the search input should lie on top of the content.
* 2. Expand as the user enters text.
* 3. Cap the height so that we can offer auto-completion suggestions below the input.
* 4. When the input is collapsed, we want text input to be truncated with an ellipsis.
*/
.kuiLocalSearchInput--expandable {
height: $localSearchHeight;
line-height: $globalLineHeight;
word-break: break-all; /* 4 */
white-space: nowrap; /* 4 */
overflow: hidden; /* 4 */
text-overflow: ellipsis; /* 4 */

&:focus {
z-index: 1; /* 1 */
height: auto; /* 2 */
max-height: 180px; /* 3 */
white-space: normal;
overflow: auto; /* 3 */
box-shadow: 0 5px 22px rgba(#000000, 0.25); /* 1 */
}

p {
margin: 0;
font-size: inherit;
line-height: inherit;
}
}

/**
* 1. Override inherited styles.
*/
Expand Down
43 changes: 43 additions & 0 deletions ui_framework/dist/ui_framework.css
Original file line number Diff line number Diff line change
Expand Up @@ -1771,11 +1771,20 @@ body {
align-items: flex-start;
/* 1 */ }

/**
* 1. If we're using an expandable input, make sure that it doesn't force sibling elements to expand
* with it.
*/
.kuiLocalSearch {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: start;
-webkit-align-items: flex-start;
-ms-flex-align: start;
align-items: flex-start;
/* 1 */
width: 100%; }

.kuiLocalSearchInput {
Expand Down Expand Up @@ -1914,6 +1923,40 @@ body {
background-image: url('data:image/svg+xml;utf8,<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1408 704q0 26-19 45l-448 448q-19 19-45 19t-45-19l-448-448q-19-19-19-45t19-45 45-19h896q26 0 45 19t19 45z" fill="#CECECE"/></svg>');
/* 1 */ }

/**
* 1. When it expands, the search input should lie on top of the content.
* 2. Expand as the user enters text.
* 3. Cap the height so that we can offer auto-completion suggestions below the input.
* 4. When the input is collapsed, we want text input to be truncated with an ellipsis.
*/
.kuiLocalSearchInput--expandable {
height: 30px;
line-height: 1.5;
word-break: break-all;
/* 4 */
white-space: nowrap;
/* 4 */
overflow: hidden;
/* 4 */
text-overflow: ellipsis;
/* 4 */ }
.kuiLocalSearchInput--expandable:focus {
z-index: 1;
/* 1 */
height: auto;
/* 2 */
max-height: 180px;
/* 3 */
white-space: normal;
overflow: auto;
/* 3 */
box-shadow: 0 5px 22px rgba(0, 0, 0, 0.25);
/* 1 */ }
.kuiLocalSearchInput--expandable p {
margin: 0;
font-size: inherit;
line-height: inherit; }

/**
* 1. Override inherited styles.
*/
Expand Down
26 changes: 26 additions & 0 deletions ui_framework/doc_site/src/views/local_nav/local_nav_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import { renderToHtml } from '../../services';

import {
GuideCode,
GuideDemo,
GuidePage,
GuideSection,
Expand Down Expand Up @@ -42,6 +43,8 @@ import { LocalNavWithTabs } from './local_nav_tabs';
import localNavWithTabsSource from '!!raw!./local_nav_tabs';
const localNavWithTabsHtml = renderToHtml(LocalNavWithTabs);

const searchMultiLineHtml = require('./local_nav_search_multi_line.html');
const searchMultiLineJs = require('raw!./local_nav_search_multi_line.js');
const datePickerHtml = require('./local_nav_date_picker.html');

export default props => (
Expand Down Expand Up @@ -124,6 +127,29 @@ export default props => (
</GuideDemo>
</GuideSection>

<GuideSection
title="Multi-line search"
source={[{
type: GuideSectionTypes.HTML,
code: searchMultiLineHtml,
}]}
>
<GuideText>
By using <GuideCode>contenteditable</GuideCode>, you can support multi-line queries. You&rsquo;ll
have to supply your own logic for switching between single-line and multi-line input values.
</GuideText>

<GuideDemo
html={searchMultiLineHtml}
js={searchMultiLineJs}
/>

<GuideDemo
html={searchMultiLineHtml}
isDarkTheme={true}
/>
</GuideSection>

<GuideSection
title="Invalid Search"
source={[
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<div class="kuiLocalNav">
<div class="kuiLocalNavRow">
<div class="kuiLocalNavRow__section">
<div class="kuiLocalBreadcrumbs">
<div class="kuiLocalBreadcrumb">
<a class="kuiLocalBreadcrumb__link" href="#">
Discover
</a>
</div>

<div class="kuiLocalBreadcrumb">
<a class="kuiLocalBreadcrumb__link" href="#">
<span class="kuiLocalBreadcrumb__emphasis">0</span> hits
</a>
</div>
</div>
</div>

<div class="kuiLocalNavRow__section">
<div class="kuiLocalMenu">
<button class="kuiLocalMenuItem">
New
</button>

<button class="kuiLocalMenuItem">
Save
</button>

<button class="kuiLocalMenuItem">
Open
</button>

<button class="kuiLocalMenuItem">
<div class="kuiLocalMenuItem__icon kuiIcon fa-clock-o"></div>
Last 5 minutes
</button>
</div>
</div>
</div>

<div class="kuiLocalNavRow kuiLocalNavRow--secondary">
<div class="kuiLocalSearch">
<div
id="expandableSearchInput"
class="kuiLocalSearchInput kuiLocalSearchInput--expandable"
contenteditable
>
</div>

<input
class="kuiLocalSearchInput kuiLocalSearchInput--secondary"
type="text"
placeholder="Another input"
autocomplete="off"
style="width: 150px"
>

<select class="kuiLocalSearchSelect">
<option>Alligator</option>
<option>Balaclava</option>
<option>Castanets</option>
</select>

<button class="kuiLocalSearchButton">
<span class="kuiIcon fa-search"></span>
</button>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* eslint-disable */

const $searchInput = $('#expandableSearchInput');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want to provide the examples using React components?

let multiLineInputValue = '(.es(\'"this&nbsp;&nbsp;&nbsp;&nbsp;"&nbsp;AND&nbsp;&nbsp;" that"&nbsp;&nbsp;\',&nbsp;metric=sum:bytes),&nbsp;.es(\'foo OR bar\')).divide(.es(-foo)).multiply(100).holt(0.1,0.1,0.1,1w),<br><br>.es(*).label(\'This is everything\')';

function formatMultiLineExpression(html) {
// Preserve whitespace because people can use it to organize parts of a query to make it easier
// to understand.
const encodedWhitespace = html.replace(/ /g, '&nbsp;');

// Replace divs with linebreaks.
const removeClosingDivs = html.replace(/<\/div>/g, '');
const breakOnOpeningDivs = removeClosingDivs.replace(/<div>/g, '<br>');

// Preserve line-breaks.
const splitOnBreaks = breakOnOpeningDivs.split(/\r?\n/g);
return splitOnBreaks.join('<br>');
}

function formatSingleLineExpression(html) {
// Allow whitespace to collapse.
const encodedWhitespace = html.replace(/&nbsp;/g, ' ');

// Replace divs with linebreaks.
const removeClosingDivs = encodedWhitespace.replace(/<\/div>/g, '');
const breakOnOpeningDivs = removeClosingDivs.replace(/<div>/g, '\n');

// Preserve line-breaks.
const splitOnBreaks = breakOnOpeningDivs.split(/<br>/g);
return splitOnBreaks.join('\n');
}

// We have to sanitize pasted data into a contenteditable.
$searchInput.on('paste', e => {
e.preventDefault();

// If we're using jQuery, we need to dig in and get the original event. IE puts clipboardData
// on the window.
const data = (e.originalEvent || e).clipboardData || window.clipboardData;

// Strip out all HTML by just getting the text.
const text = data.getData('text');

const html = formatMultiLineExpression(text);

document.execCommand('insertHTML', false, html);
});

$searchInput.on('blur', () => {
// If the search input has been scrolled, we should make sure only the first line is visible
// if it loses focus and collapses.
$searchInput.scrollTop(0);

// Store input value.
multiLineInputValue = formatMultiLineExpression($searchInput.html());

// Display "preview mode" of input value.
const previewInput = formatSingleLineExpression($searchInput.html());
$searchInput.text(previewInput);
});

$searchInput.on('focus', () => {
// Display true input value.
$searchInput.html(multiLineInputValue);
});

// Init.
$searchInput.html(formatSingleLineExpression(multiLineInputValue));