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

[ESQL] Add chart switch #177790

Merged
merged 13 commits into from
Mar 8, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export const textBasedLanguagedEditorStyles = (
transform: 'translate(0, -50%)',
},
bottomContainer: {
border: euiTheme.border.thin,
borderLeft: editorIsInline ? 'none' : euiTheme.border.thin,
borderRight: editorIsInline ? 'none' : euiTheme.border.thin,
borderTop:
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

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

  • It looks like the jumpy accordion animation wasn't resolved, as I'm still seeing it in local testing. Possible to give it another look?

    CleanShot 2024-03-08 at 10 21 23

  • There are currently some issues present in the flyout when the user has greatly increased the height of the ES|QL editor and/or they have reduced the height of their browser's viewport:

    • There are overflow issues present with the accordion arrows (see screenshot below).
    • Additionally, open accordions may also be rendered so short in height that they would be unusable in some extreme scenarios. Should we attempt to prevent open accordions from getting that small (via a min-height or some other method)? Also, for the sake of responsiveness, at very small viewport heights, should we remove the accordions' existing inner content scrolling styles (allowing them to grow as tall as the height of their contents) and move the scrolling styles to a parent element of the accordions? This one may be a can of worms, so feel free to split off as a separate PR if you wish.

    CleanShot 2024-03-08 at 10 25 29

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you Michael! As you say, it is a can of worms – I fixed the jump before but I broke another thing and then I thought I fixed it again, but it's not fixed for all the cases and broke the second thing you mention... I will submit another issue to make sure it's tested in separation 🙏🏼

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MichaelMarcialis thank you for your comments! I'll keep the responses here so we can take advantage of @markov00 proposal to keep the context in one place. First issue posted by you:

The configuration toolbar currently scrolls with any overflowing layer panel(s). Would it be possible to restructure the markup or CSS in a way that would limit the scrolling container to the layer panel(s) only, thus allowing the toolbar to always be visible to the user?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure it's a way to go. Here's how it looks right now in my Macbook 16'. The scrolling area is already extremely small for ESQL charts and making it even smaller makes it not even fit one layer configuration. I think it makes it worse for the user and I'd prefer to keep scroll as it is. Whether we decide to change it or not, I can add an issue and keep it separate from this PR.

Mar-07-2024 13-40-51

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a fair point. Yeah, let's split it off as a separate issue to tackle, since it will likely require more design thought and possible layout shuffling (beyond the scope of this PR). Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done here: #178234

* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@MichaelMarcialis

  • In reading the Google doc comments, perhaps changing the current warning icon to the dot icon (similar to how we do for incompatible quick functions in Lens) would make it appear less scary and distracting. Similarly, it might also be nice to reduce the "Technical preview" badge to a simple beaker icon with accompanying tooltip. And perhaps both can be moved to be adjacent to text (thus avoiding the jumping of icons on focus). Thoughts? I've placed a quick example mockup below.

    CleanShot 2024-03-06 at 12 45 48

Copy link
Contributor Author

@mbondyra mbondyra Mar 7, 2024

Choose a reason for hiding this comment

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

I love the beaker idea! Regarding the other one, but shouldn't be just a little scary though? We don't offer any undo functionality and user can loose a big chunk of their configuration with clicking this button. I am happy to implement it though, it's just my thoughts.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I submitted an issue here: #178232

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for splitting off as a separate issue. And I do share your concern about making a potentially destructive action less scary while we continue to operate without undo functionality. On the other hand, the warning icon redundancy was so overwhelming in some scenarios, and something that has been bugging me for a while. Perhaps a good interim solution (until we have an undo) is to hit the user with a confirmation modal that they can optionally choose to "Never show again", similar to how we do when deleting layers? That way we ensure the user knows they are taking a destructive action without making the visualization selector overly noisy. Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like it, let's do it! I think we can also make the warning more specific. For example.

  1. We can know which columns will be persisted
  2. Which layers will be persisted

Maybe we could use this information while thinking about this new design? I will create a small POC (outside of this PR) and link it to the issue

Copy link
Contributor

Choose a reason for hiding this comment

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

I really like this proposal 😍

* or more contributor license agreements. Licensed under the Elastic License
Copy link
Contributor Author

@mbondyra mbondyra Mar 7, 2024

Choose a reason for hiding this comment

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

@MichaelMarcialis

  • Also per the Google doc comments, I agree that it's a bit problematic that deleting a required dimensions doesn't properly inform the user. Would it be possible to show the standard Lens "Requires field" validation message in red as we typically do for users that outright deleted required dimensions?

    CleanShot 2024-03-06 at 12 31 58

We still do it, we just don't do it when the visualization is completely cleared out. Do you think we should change it? It's possible, but I'd do it separately 🙏🏼

Screenshot 2024-03-07 at 15 30 34 Screenshot 2024-03-07 at 15 30 42

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, I think it'd be a better user experience to show that sort of "required" error on the appropriate dimensions even when the entire configuration is cleared out. I'm fine with splitting it out as a separate issue as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done: #178237

* 2.0 and the Server Side Public License, v 1; you may not use this file except
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import { EuiIcon, EuiText, EuiFlexGroup, EuiFlexItem, IconType } from '@elastic/eui';
import { ToolbarButton } from '@kbn/shared-ux-button-toolbar';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';

export const ChartSwitchTrigger = function ({
label,
icon,
onClick,
dataTestSubj,
size = 's',
}: {
label: string;
icon?: IconType;
onClick: () => void;
dataTestSubj?: string;
size?: 's' | 'm';
}) {
return (
<ToolbarButton
data-test-subj={dataTestSubj}
aria-label={label}
onClick={onClick}
fullWidth
size={size}
fontWeight="bold"
label={
size === 'm' ? (
<ChartSwitchLabel label={label} icon={icon} />
) : (
<LayerChartSwitchLabel label={label} icon={icon} />
)
}
/>
);
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
};

const ChartSwitchLabel = function ({ label, icon }: { label: string; icon?: IconType }) {
return (
<>
{icon && <EuiIcon size="l" className="lnsChartSwitch__summaryIcon" type={icon} />}
<span className="lnsChartSwitch__summaryText">{label}</span>
</>
);
};

const LayerChartSwitchLabel = function ({ label, icon }: { label: string; icon?: IconType }) {
return (
<EuiFlexGroup gutterSize="none" alignItems="center" responsive={false}>
{icon && (
<EuiFlexItem grow={false}>
<EuiIcon type={icon} />
</EuiFlexItem>
)}

<EuiFlexItem grow={false}>
<EuiText
size="s"
css={css`
font-weight: 600;
display: inline;
padding-left: ${euiThemeVars.euiSizeS};
`}
>
{label}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export * from './line_style_settings';

export * from './text_decoration_setting';

export * from './chart_switch_trigger';

export type { AccessorConfig } from './dimension_buttons';

export type { FieldOptionValue, FieldOption, DataType } from './field_picker';
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-visualization-ui-components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export {
LineStyleSettings,
TextDecorationSetting,
emptyTitleText,
ChartSwitchTrigger,
} from './components';

export { isFieldLensCompatible } from './util';
Expand Down
3 changes: 2 additions & 1 deletion packages/kbn-visualization-ui-components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@kbn/field-formats-plugin",
"@kbn/field-utils",
"@kbn/calculate-width-from-char-count",
"@kbn/visualization-utils"
"@kbn/visualization-utils",
"@kbn/shared-ux-button-toolbar"
],
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe('interpreter/functions#metric', () => {
expect(() => fn(context, args)).toThrowErrorMatchingSnapshot();
});

it('returns error if several metrics and colorFullBackground specified', () => {
it('ignores colorFullBackground setting if several metrics are specified', () => {
args.colorFullBackground = true;
args.metric.push({
type: 'vis_dimension',
Expand All @@ -105,13 +105,13 @@ describe('interpreter/functions#metric', () => {
},
});

expect(() => fn(context, args)).toThrowErrorMatchingSnapshot();
expect(fn(context, args).value.visConfig.metric.colorFullBackground).toBeFalsy();
});

it('returns error if data includes several rows and colorFullBackground specified', () => {
it('ignores colorFullBackground if data includes several rows', () => {
args.colorFullBackground = true;
context.rows.push({ 'col-0-1': 0 });

expect(() => fn(context, args)).toThrowErrorMatchingSnapshot();
expect(fn(context, args).value.visConfig.metric.colorFullBackground).toBeFalsy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,6 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({
if (args.bucket) {
throw new Error(errors.splitByBucketAndColorFullBackgroundSpecifiedError());
}

if (args.metric.length > 1 || input.rows.length > 1) {
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(errors.severalMetricsAndColorFullBackgroundSpecifiedError());
}
}

args.metric.forEach((metric) => validateAccessor(metric, input.columns));
Expand Down Expand Up @@ -197,7 +193,8 @@ export const metricVisFunction = (): MetricVisExpressionFunctionDefinition => ({
...args.labelFont,
},
},
colorFullBackground: args.colorFullBackground,
colorFullBackground:
args.metric.length > 1 || input.rows.length > 1 ? false : args.colorFullBackground,
style: {
bgColor: args.colorMode === ColorMode.Background,
labelColor: args.colorMode === ColorMode.Labels,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export function LayerConfiguration({
hasPadding,
setIsInlineFlyoutVisible,
getUserMessages,
shouldDisplayChartSwitch,
}: LayerConfigurationProps) {
const dispatch = useLensDispatch();
const { euiTheme } = useEuiTheme();
Expand Down Expand Up @@ -57,22 +58,32 @@ export function LayerConfiguration({
dataViews: startDependencies.dataViews,
uiActions: startDependencies.uiActions,
hideLayerHeader: datasourceId === 'textBased',
// TODO: remove this prop once we display the chart switch in Discover
shouldDisplayChartSwitch,
indexPatternService,
setIsInlineFlyoutVisible,
getUserMessages,
};
return (
<div
css={css`
padding: ${hasPadding ? euiTheme.size.s : 0};
padding-left: ${euiTheme.size.base};
padding-right: ${euiTheme.size.base};
`}
>
<VisualizationToolbar
activeVisualization={activeVisualization}
framePublicAPI={framePublicAPI}
/>
<EuiSpacer size="m" />
<ConfigPanelWrapper {...layerPanelsProps} />
<div
css={css`
padding: ${hasPadding ? euiTheme.size.s : 0};
`}
>
<EuiSpacer size="xs" />
<VisualizationToolbar
activeVisualization={activeVisualization}
framePublicAPI={framePublicAPI}
/>
<EuiSpacer size="m" />
<ConfigPanelWrapper {...layerPanelsProps} />
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EuiTitle,
EuiAccordion,
useEuiTheme,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
euiScrollBarStyles,
Expand Down Expand Up @@ -360,7 +361,7 @@ export function LensEditConfigurationFlyout({
onCancel={onCancel}
navigateToLensEditor={navigateToLensEditor}
onApply={onApply}
isScrollable={true}
isScrollable
isNewPanel={isNewPanel}
isSaveable={isSaveable}
>
Expand All @@ -372,7 +373,7 @@ export function LensEditConfigurationFlyout({
visualizationMap={visualizationMap}
datasourceMap={datasourceMap}
datasourceId={datasourceId}
hasPadding={true}
hasPadding
framePublicAPI={framePublicAPI}
setIsInlineFlyoutVisible={setIsInlineFlyoutVisible}
/>
Expand Down Expand Up @@ -412,6 +413,7 @@ export function LensEditConfigurationFlyout({
${euiScrollBarStyles(euiTheme)}
padding-left: ${euiThemeVars.euiFormMaxWidth};
margin-left: -${euiThemeVars.euiFormMaxWidth};

.euiAccordion-isOpen & {
block-size: auto !important;
flex: 1;
Expand Down Expand Up @@ -451,22 +453,25 @@ export function LensEditConfigurationFlyout({
}
}}
isDisabled={false}
allowQueryCancellation={true}
allowQueryCancellation
/>
</EuiFlexItem>
)}
<EuiFlexItem
grow={isLayerAccordionOpen ? 1 : false}
css={css`
padding-left: ${euiThemeVars.euiSize};
padding-right: ${euiThemeVars.euiSize};
.euiAccordion__childWrapper {
flex: ${isLayerAccordionOpen ? 1 : 'none'}
}
}
`}
>
<EuiAccordion
css={css`
.euiAccordion__triggerWrapper {
padding: 0 ${euiThemeVars.euiSize};
}
`}
id="layer-configuration"
buttonContent={
<EuiTitle
Expand All @@ -477,8 +482,8 @@ export function LensEditConfigurationFlyout({
`}
>
<h5>
{i18n.translate('xpack.lens.config.layerConfigurationLabel', {
defaultMessage: 'Layer configuration',
{i18n.translate('xpack.lens.config.visualizationConfigurationLabel', {
defaultMessage: 'Visualization configuration',
})}
</h5>
</EuiTitle>
Expand All @@ -495,17 +500,22 @@ export function LensEditConfigurationFlyout({
setIsLayerAccordionOpen(!isLayerAccordionOpen);
}}
>
<LayerConfiguration
attributes={attributes}
getUserMessages={getUserMessages}
coreStart={coreStart}
startDependencies={startDependencies}
visualizationMap={visualizationMap}
datasourceMap={datasourceMap}
datasourceId={datasourceId}
framePublicAPI={framePublicAPI}
setIsInlineFlyoutVisible={setIsInlineFlyoutVisible}
/>
<>
<LayerConfiguration
// TODO: remove this prop once we add support for multiple layers (form-based mode)
shouldDisplayChartSwitch={!!textBasedMode}
attributes={attributes}
getUserMessages={getUserMessages}
coreStart={coreStart}
startDependencies={startDependencies}
visualizationMap={visualizationMap}
datasourceMap={datasourceMap}
datasourceId={datasourceId}
framePublicAPI={framePublicAPI}
setIsInlineFlyoutVisible={setIsInlineFlyoutVisible}
/>
<EuiSpacer />
</>
</EuiAccordion>
</EuiFlexItem>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,5 @@ export interface LayerConfigurationProps {
hasPadding?: boolean;
setIsInlineFlyoutVisible: (flag: boolean) => void;
getUserMessages: UserMessagesGetter;
shouldDisplayChartSwitch?: boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,3 @@
.lnsChangeIndexPatternPopover__trigger {
padding: 0 $euiSize;
}

.lnsLayerPanelChartSwitch_title {
font-weight: 600;
display: inline;
padding-left: 8px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -603,9 +603,12 @@ function createNewLayerWithMetricAggregation(

export function getDatasourceSuggestionsFromCurrentState(
state: FormBasedPrivateState,
indexPatterns: IndexPatternMap,
indexPatterns?: IndexPatternMap,
filterLayers: (layerId: string) => boolean = () => true
): Array<DatasourceSuggestion<FormBasedPrivateState>> {
if (!indexPatterns) {
return [];
}
const layers = Object.entries(state.layers || {}).filter(([layerId]) => filterLayers(layerId));

if (layers.length > 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { DatasourceDimensionEditorProps, DataType } from '../../../types';
import { FieldSelect } from './field_select';
import type { TextBasedPrivateState } from '../types';
import { retrieveLayerColumnsFromCache, getColumnsFromCache } from '../fieldlist_cache';
import { isNotNumeric, isNumeric } from '../utils';

export type TextBasedDimensionEditorProps =
DatasourceDimensionEditorProps<TextBasedPrivateState> & {
Expand All @@ -28,7 +29,7 @@ export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) {
query
);
const allFields = query ? getColumnsFromCache(query) : [];
const hasNumberTypeColumns = allColumns?.some((c) => c?.meta?.type === 'number');
const hasNumberTypeColumns = allColumns?.some(isNumeric);
const fields = allFields.map((col) => {
return {
id: col.id,
Expand All @@ -38,7 +39,7 @@ export function TextBasedDimensionEditor(props: TextBasedDimensionEditorProps) {
props.isMetricDimension && hasNumberTypeColumns
? props.filterOperations({
dataType: col?.meta?.type as DataType,
isBucketed: Boolean(col?.meta?.type !== 'number'),
isBucketed: Boolean(isNotNumeric(col)),
scale: 'ordinal',
})
: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { isOperation } from '../../../types';
import type { TextBasedPrivateState } from '../types';
import type { GetDropPropsArgs } from '../../../types';
import { isDraggedField, isOperationFromTheSameGroup } from '../../../utils';
import { canColumnBeDroppedInMetricDimension } from '../utils';
import { canColumnBeDroppedInMetricDimension, isNotNumeric } from '../utils';
import { retrieveLayerColumnsFromCache } from '../fieldlist_cache';

export const getDropProps = (
Expand All @@ -28,7 +28,7 @@ export const getDropProps = (

if (isDraggedField(source)) {
const nextLabel = source.humanData.label;
if (target?.isMetricDimension && sourceField?.meta?.type !== 'number') {
if (target?.isMetricDimension && sourceField && isNotNumeric(sourceField)) {
return;
}
return {
Expand Down
Loading