[ES|QL] Add line breaks redesign (#173596)

## Summary

Part of https://github.com/elastic/kibana/issues/171831

Replaces the single boolean button with two ever-present buttons that
allow the user to "Add line breaks on pipes" and "Remove line breaks on
pipes"

<img width="435" alt="image"
src="a7042e15-f5b4-4a24-aa68-a7b7ca980895">


### Note
I had to use the TooltipWrapper and realized we are using this in many
places and every time we are duplicating the code. I moved it to
visualization-utils and changed the occurences.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Stratoula Kalafateli 2023-12-21 11:01:15 +02:00 committed by GitHub
parent 1f3d3eaaa7
commit b40b566e99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 96 additions and 149 deletions

View file

@ -8,11 +8,11 @@
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import { TooltipWrapper } from '@kbn/visualization-utils';
import React, { useCallback, Dispatch, useContext } from 'react';
import { EuiFlexGroup, EuiButtonEmpty, EuiFlexItem } from '@elastic/eui';
import { DistributeEquallyIcon } from '../assets/distribute_equally';
import { TooltipWrapper } from '../tooltip_wrapper';
import type { ColorRangesActions } from './types';
import { ColorRangesContext } from './color_ranges_context';

View file

@ -10,11 +10,11 @@ import React, { Dispatch, useCallback, useContext } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButtonIcon, EuiIconProps } from '@elastic/eui';
import { TooltipWrapper } from '@kbn/visualization-utils';
import type { PaletteContinuity, CustomPaletteParams } from '../../../palettes';
import { isLastItem } from './utils';
import { TooltipWrapper } from '../tooltip_wrapper';
import type { ColorRangesActions, ColorRange, ColorRangeAccessor } from './types';
import { ColorRangesContext } from './color_ranges_context';

View file

@ -22,6 +22,7 @@
"@kbn/test-jest-helpers",
"@kbn/data-plugin",
"@kbn/ui-theme",
"@kbn/visualization-utils",
],
"exclude": [
"target/**/*",

View file

@ -22,6 +22,7 @@ import { getAggregateQueryMode, getLanguageDisplayName } from '@kbn/es-query';
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
import type { ExpressionsStart } from '@kbn/expressions-plugin/public';
import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/public';
import { TooltipWrapper } from '@kbn/visualization-utils';
import {
type LanguageDocumentationSections,
LanguageDocumentationPopover,
@ -172,7 +173,6 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({
const [showLineNumbers, setShowLineNumbers] = useState(isCodeEditorExpanded);
const [isCompactFocused, setIsCompactFocused] = useState(isCodeEditorExpanded);
const [isCodeEditorExpandedFocused, setIsCodeEditorExpandedFocused] = useState(false);
const [isWordWrapped, setIsWordWrapped] = useState(false);
const [editorMessages, setEditorMessages] = useState<{
errors: MonacoMessage[];
@ -478,15 +478,14 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({
}
}, [calculateVisibleCode, code, isCompactFocused, queryString]);
useEffect(() => {
if (isCodeEditorExpanded && !isWordWrapped) {
const pipes = code?.split('|');
const pipesWithNewLine = code?.split('\n|');
if (pipes?.length === pipesWithNewLine?.length) {
setIsWordWrapped(true);
}
}
}, [code, isCodeEditorExpanded, isWordWrapped]);
const linesBreaksButtonsStatus = useMemo(() => {
const pipes = code?.split('|');
const pipesWithNewLine = code?.split('\n|');
return {
addLineBreaksDisabled: pipes?.length === pipesWithNewLine?.length,
removeLineBreaksDisabled: pipesWithNewLine?.length === 1,
};
}, [code]);
const onResize = ({ width }: { width: number }) => {
setIsSpaceReduced(Boolean(editorIsInline && width < BREAKPOINT_WIDTH));
@ -499,7 +498,6 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({
const onQueryUpdate = useCallback(
(value: string) => {
setCode(value);
setIsWordWrapped(false);
onTextLangQueryChange({ [language]: value } as AggregateQuery);
},
[language, onTextLangQueryChange]
@ -561,58 +559,72 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({
responsive={false}
>
<EuiFlexItem grow={false}>
<EuiToolTip
position="top"
content={
isWordWrapped
? i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.disableWordWrapLabel',
{
defaultMessage: 'Disable wrap with pipes',
}
)
: i18n.translate(
<EuiFlexGroup responsive={false} gutterSize="none" alignItems="center">
<EuiFlexItem grow={false}>
<TooltipWrapper
tooltipContent={i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.EnableWordWrapLabel',
{
defaultMessage: 'Add line breaks on pipes',
}
)}
condition={!linesBreaksButtonsStatus.addLineBreaksDisabled}
>
<EuiButtonIcon
iconType="pipeBreaks"
color="text"
size="s"
data-test-subj="TextBasedLangEditor-toggleWordWrap"
aria-label={i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.EnableWordWrapLabel',
{
defaultMessage: 'Wrap with pipes',
defaultMessage: 'Add line breaks on pipes',
}
)
}
>
<EuiButtonIcon
iconType={isWordWrapped ? 'wordWrap' : 'wordWrapDisabled'}
color="text"
size="s"
data-test-subj="TextBasedLangEditor-toggleWordWrap"
aria-label={
isWordWrapped
? i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.disableWordWrapLabel',
{
defaultMessage: 'Disable wrap with pipes',
}
)
: i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.EnableWordWrapLabel',
{
defaultMessage: 'Wrap with pipes',
}
)
}
isSelected={!isWordWrapped}
onClick={() => {
editor1.current?.updateOptions({
wordWrap: isWordWrapped ? 'off' : 'on',
});
setIsWordWrapped(!isWordWrapped);
const updatedCode = getWrappedInPipesCode(code, isWordWrapped);
if (code !== updatedCode) {
setCode(updatedCode);
onTextLangQueryChange({ [language]: updatedCode } as AggregateQuery);
}
}}
/>
</EuiToolTip>
)}
isDisabled={linesBreaksButtonsStatus.addLineBreaksDisabled}
onClick={() => {
const updatedCode = getWrappedInPipesCode(code, false);
if (code !== updatedCode) {
setCode(updatedCode);
onTextLangQueryChange({ [language]: updatedCode } as AggregateQuery);
}
}}
/>
</TooltipWrapper>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<TooltipWrapper
tooltipContent={i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.disableWordWrapLabel',
{
defaultMessage: 'Remove line breaks on pipes',
}
)}
condition={!linesBreaksButtonsStatus.removeLineBreaksDisabled}
>
<EuiButtonIcon
iconType="pipeNoBreaks"
color="text"
size="s"
data-test-subj="TextBasedLangEditor-toggleWordWrap"
aria-label={i18n.translate(
'textBasedEditor.query.textBasedLanguagesEditor.disableWordWrapLabel',
{
defaultMessage: 'Remove line breaks on pipes',
}
)}
isDisabled={linesBreaksButtonsStatus.removeLineBreaksDisabled}
onClick={() => {
const updatedCode = getWrappedInPipesCode(code, true);
if (code !== updatedCode) {
setCode(updatedCode);
onTextLangQueryChange({ [language]: updatedCode } as AggregateQuery);
}
}}
/>
</TooltipWrapper>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup responsive={false} gutterSize="none" alignItems="center">

View file

@ -23,7 +23,8 @@
"@kbn/data-plugin",
"@kbn/expressions-plugin",
"@kbn/data-views-plugin",
"@kbn/index-management-plugin"
"@kbn/index-management-plugin",
"@kbn/visualization-utils"
],
"exclude": [
"target/**/*",

View file

@ -8,6 +8,7 @@
import React, { useEffect, useRef, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { TooltipWrapper } from '@kbn/visualization-utils';
import {
EuiFormRow,
EuiColorPicker,
@ -17,7 +18,6 @@ import {
euiPaletteColorBlind,
} from '@elastic/eui';
import { getColorAlpha, makeColorWithAlpha } from '@kbn/coloring';
import { TooltipWrapper } from './tooltip_wrapper';
const tooltipContent = {
auto: i18n.translate('visualizationUiComponents.colorPicker.tooltip.auto', {

View file

@ -16,7 +16,7 @@ import {
EuiPanel,
useEuiTheme,
} from '@elastic/eui';
import { TooltipWrapper } from '../tooltip_wrapper';
import { TooltipWrapper } from '@kbn/visualization-utils';
import type { BucketContainerProps } from './types';
export const DefaultBucketContainer = ({

View file

@ -16,7 +16,7 @@ import {
EuiPanel,
useEuiTheme,
} from '@elastic/eui';
import { TooltipWrapper } from '../tooltip_wrapper';
import { TooltipWrapper } from '@kbn/visualization-utils';
import type { BucketContainerProps } from './types';
export const FieldsBucketContainer = ({

View file

@ -14,8 +14,6 @@ export * from './debounced_input';
export * from './debounced_value';
export * from './tooltip_wrapper';
export * from './color_picker';
export * from './icon_select';

View file

@ -11,7 +11,6 @@ export {
NameInput,
DebouncedInput,
useDebouncedValue,
TooltipWrapper,
ColorPicker,
IconSelect,
IconSelectSetting,

View file

@ -31,6 +31,7 @@
"@kbn/coloring",
"@kbn/field-formats-plugin",
"@kbn/field-utils",
"@kbn/calculate-width-from-char-count"
"@kbn/calculate-width-from-char-count",
"@kbn/visualization-utils"
],
}

View file

@ -7,3 +7,4 @@
*/
export { getTimeZone } from './src/get_timezone';
export { TooltipWrapper } from './src/tooltip_wrapper';

View file

@ -8,7 +8,7 @@
import { EuiFlexGroup, EuiFlexItem, EuiRange, EuiText, useEuiTheme } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { TooltipWrapper } from './tooltip_wrapper';
import { TooltipWrapper } from '@kbn/visualization-utils';
export interface ControlSliderProps {
/** Allowed values to show on the Control Slider */

View file

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiToolTip, EuiToolTipProps } from '@elastic/eui';
export type TooltipWrapperProps = Partial<Omit<EuiToolTipProps, 'content'>> & {
tooltipContent: string;
/** When the condition is truthy, the tooltip will be shown */
condition: boolean;
};
export const TooltipWrapper: React.FunctionComponent<TooltipWrapperProps> = ({
children,
condition,
tooltipContent,
...tooltipProps
}) => {
return (
<>
{condition ? (
<EuiToolTip content={tooltipContent} delay="long" {...tooltipProps}>
<>{children}</>
</EuiToolTip>
) : (
children
)}
</>
);
};

View file

@ -13,6 +13,7 @@
],
"kbn_references": [
"@kbn/i18n-react",
"@kbn/visualization-utils",
],
"exclude": [
"target/**/*",

View file

@ -12,6 +12,7 @@ import { i18n } from '@kbn/i18n';
import { connect } from 'react-redux';
import { toElasticsearchQuery, fromKueryExpression, Query } from '@kbn/es-query';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { TooltipWrapper } from '@kbn/visualization-utils';
import { QueryStringInput } from '@kbn/unified-search-plugin/public';
import type { DataView } from '@kbn/data-views-plugin/public';
import { IUnifiedSearchPluginServices } from '@kbn/unified-search-plugin/public/types';
@ -27,8 +28,6 @@ import {
selectedFieldsSelector,
} from '../state_management';
import { TooltipWrapper } from './tooltip_wrapper';
export interface SearchBarProps {
isLoading: boolean;
urlQuery: string | null;

View file

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiToolTip, EuiToolTipProps } from '@elastic/eui';
export type TooltipWrapperProps = Partial<Omit<EuiToolTipProps, 'content'>> & {
tooltipContent: string;
/** When the condition is truthy, the tooltip will be shown */
condition: boolean;
};
export const TooltipWrapper: React.FunctionComponent<TooltipWrapperProps> = ({
children,
condition,
tooltipContent,
...tooltipProps
}) => {
return (
<>
{condition ? (
<EuiToolTip content={tooltipContent} delay="long" {...tooltipProps}>
<>{children}</>
</EuiToolTip>
) : (
children
)}
</>
);
};

View file

@ -49,6 +49,7 @@
"@kbn/content-management-utils",
"@kbn/logging",
"@kbn/content-management-table-list-view-common",
"@kbn/visualization-utils",
],
"exclude": [
"target/**/*",

View file

@ -29,7 +29,7 @@ import {
} from '@kbn/data-plugin/public';
import { extendedBoundsToAst, intervalOptions } from '@kbn/data-plugin/common';
import { buildExpressionFunction } from '@kbn/expressions-plugin/public';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import { updateColumnParam } from '../layer_helpers';
import { OperationDefinition, ParamEditorProps } from '.';
import { FieldBasedIndexPatternColumn } from './column_types';

View file

@ -25,7 +25,7 @@ import {
} from '@kbn/coloring';
import { GaugeTicksPositions, GaugeColorModes } from '@kbn/expression-gauge-plugin/common';
import { getMaxValue, getMinValue } from '@kbn/expression-gauge-plugin/public';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import { isNumericFieldForDatatable } from '../../../common/expressions/datatable/utils';
import { applyPaletteParams, PalettePanelContainer } from '../../shared_components';
import type { VisualizationDimensionEditorProps } from '../../types';

View file

@ -11,7 +11,7 @@ import { Position } from '@elastic/charts';
import { i18n } from '@kbn/i18n';
import { LegendSize } from '@kbn/visualizations-plugin/public';
import { EuiIconAxisLeft, EuiIconAxisBottom } from '@kbn/chart-icons';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import type { VisualizationToolbarProps } from '../../types';
import {
LegendSettingsPopover,

View file

@ -7,7 +7,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import { ToolbarPopover } from '../../../shared_components';
import { TitlePositionOptions } from './title_position_option';
import { FramePublicAPI } from '../../../types';

View file

@ -11,7 +11,7 @@ import { Position, ScaleType } from '@elastic/charts';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { AxisExtentConfig } from '@kbn/expression-xy-plugin/common';
import { LegendSize } from '@kbn/visualizations-plugin/public';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import type { LegendSettingsPopoverProps } from '../../../shared_components/legend/legend_settings_popover';
import type { VisualizationToolbarProps, FramePublicAPI } from '../../../types';
import { State, XYState, AxesSettingsConfig } from '../types';

View file

@ -9,7 +9,7 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
import { IconPosition } from '@kbn/expression-xy-plugin/common';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import { YAxisMode } from '../../types';
import { idPrefix } from '../dimension_editor';

View file

@ -7,7 +7,7 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
import { TooltipWrapper } from '@kbn/visualization-ui-components';
import { TooltipWrapper } from '@kbn/visualization-utils';
import { ToolbarPopover, ValueLabelsSettings } from '../../../../shared_components';
import { MissingValuesOptions } from './missing_values_option';
import { LineCurveOption } from './line_curve_option';

View file

@ -97,7 +97,8 @@
"@kbn/shared-ux-button-toolbar",
"@kbn/cell-actions",
"@kbn/calculate-width-from-char-count",
"@kbn/discover-utils"
"@kbn/discover-utils",
"@kbn/visualization-utils"
],
"exclude": [
"target/**/*"