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

[Lens] Provide single-value functions to show the "Last" value of some field #83437

Merged
merged 34 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e2354f5
feat: last value sketch
mbondyra Nov 16, 2020
6a6dd4e
feat: rollup indices
mbondyra Nov 17, 2020
f807858
added disabledStatus
mbondyra Nov 18, 2020
4555d51
update old tests
mbondyra Nov 18, 2020
2e3708f
tests added
mbondyra Nov 18, 2020
1f7b713
Merge branch 'master' into lens/last_value
kibanamachine Nov 18, 2020
df8c5ee
fix i18n message
mbondyra Nov 18, 2020
0c14e4c
cr
mbondyra Nov 20, 2020
dba38a5
(experimental) don't add icon
mbondyra Nov 20, 2020
1357868
fix transferable
mbondyra Nov 23, 2020
b6f3be9
Merge branch 'master' into lens/last_value
kibanamachine Nov 23, 2020
3c76ab4
error handling, tests in progress
mbondyra Nov 23, 2020
7ee756f
tests for hasInvalidReferences
mbondyra Nov 23, 2020
3f1991e
change name
mbondyra Nov 23, 2020
de88213
correct displayName
mbondyra Nov 23, 2020
a4b76d6
test corrected last value
mbondyra Nov 23, 2020
86ee016
fix tests and types
mbondyra Nov 24, 2020
3455a7d
no need to make a function obligatory
mbondyra Nov 24, 2020
b54c73d
hasInvalidreferenced not necessary
mbondyra Nov 24, 2020
8b90db7
eslint
mbondyra Nov 24, 2020
71e8264
addded warnings for pie and xy
mbondyra Nov 24, 2020
3e4b5f7
Update x-pack/plugins/lens/public/indexpattern_datasource/dimension_p…
mbondyra Nov 24, 2020
b3218a6
Merge branch 'master' into lens/last_value
kibanamachine Nov 25, 2020
5be70e4
Merge branch 'lens/accessibility-suggestions-name-focus' into lens/la…
mbondyra Nov 26, 2020
2f844b8
refactor hasInvalidReferences to getErrorMessage
mbondyra Nov 26, 2020
85f3503
ts
mbondyra Nov 26, 2020
f79cafa
Merge branch 'master' into lens/last_value
kibanamachine Nov 30, 2020
808bc29
copy change to more human
mbondyra Nov 30, 2020
8f1ea88
Merge commit '67564b9776abd608434c95375968598bfa082849' into lens/las…
mbondyra Dec 1, 2020
15ba028
design changes
mbondyra Dec 1, 2020
b42d0c5
designs
mbondyra Dec 1, 2020
af5f00e
design
mbondyra Dec 2, 2020
f84c9b5
remove sortOrder
mbondyra Dec 2, 2020
51e0830
Merge commit 'c73de2677337e4054954c068744f8b29ab3ce29c' into lens/las…
mbondyra Dec 2, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import './workspace_panel_wrapper.scss';

import React, { useState } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiPopover, EuiText, EuiButtonEmpty, EuiHorizontalRule } from '@elastic/eui';

export const WarningsPopover = ({
children,
}: {
children?: React.ReactNode | React.ReactNode[];
}) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);

if (!children) {
return null;
}

const onButtonClick = () => setIsPopoverOpen((isOpen) => !isOpen);
const closePopover = () => setIsPopoverOpen(false);
const warningsCount = React.Children.count(children);
return (
<EuiPopover
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
button={
<EuiButtonEmpty
onClick={onButtonClick}
iconType="alert"
className="lnsWorkspaceWarningButton"
>
{i18n.translate('xpack.lens.chartWarnings.number', {
defaultMessage: `{warningsCount} {warningsCount, plural, one {warning} other {warnings}}`,
values: {
warningsCount,
},
})}
</EuiButtonEmpty>
}
isOpen={isPopoverOpen}
closePopover={closePopover}
>
{React.Children.map(children, (child, index) => (
<>
<EuiText style={{ width: 280 }}>{child}</EuiText>
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
{warningsCount - 1 !== index && <EuiHorizontalRule margin="s" />}
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
</>
))}
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
</EuiPopover>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,7 @@
75% { transform: translateY(15%); }
100% { transform: translateY(10%); }
}

.lnsWorkspaceWarningButton {
color: $euiColorWarningText;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { Datasource, FramePublicAPI, Visualization } from '../../../types';
import { NativeRenderer } from '../../../native_renderer';
import { Action } from '../state_management';
import { ChartSwitch } from './chart_switch';
import { WarningsPopover } from './warnings_popover';

export interface WorkspacePanelWrapperProps {
children: React.ReactNode | React.ReactNode[];
Expand Down Expand Up @@ -64,40 +65,59 @@ export function WorkspacePanelWrapper({
},
[dispatch, activeVisualization]
);
const warningMessages =
activeVisualization?.getWarningMessages &&
activeVisualization.getWarningMessages(visualizationState, framePublicAPI);
return (
<>
<div>
<EuiFlexGroup
alignItems="center"
gutterSize="m"
direction="row"
responsive={false}
wrap={true}
className="lnsWorkspacePanelWrapper__toolbar"
justifyContent="spaceBetween"
>
<EuiFlexItem grow={false}>
<ChartSwitch
data-test-subj="lnsChartSwitcher"
visualizationMap={visualizationMap}
visualizationId={visualizationId}
visualizationState={visualizationState}
datasourceMap={datasourceMap}
datasourceStates={datasourceStates}
dispatch={dispatch}
framePublicAPI={framePublicAPI}
/>
<EuiFlexGroup
gutterSize="m"
direction="row"
responsive={false}
wrap={true}
className="lnsWorkspacePanelWrapper__toolbar"
>
<EuiFlexItem grow={false}>
<ChartSwitch
data-test-subj="lnsChartSwitcher"
visualizationMap={visualizationMap}
visualizationId={visualizationId}
visualizationState={visualizationState}
datasourceMap={datasourceMap}
datasourceStates={datasourceStates}
dispatch={dispatch}
framePublicAPI={framePublicAPI}
/>
</EuiFlexItem>
{activeVisualization && activeVisualization.renderToolbar && (
<EuiFlexItem grow={false}>
<NativeRenderer
render={activeVisualization.renderToolbar}
nativeProps={{
frame: framePublicAPI,
state: visualizationState,
setState: setVisualizationState,
}}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem grow={false}>
{warningMessages && warningMessages.length ? (
<WarningsPopover>{warningMessages}</WarningsPopover>
) : null}
</EuiFlexItem>
{activeVisualization && activeVisualization.renderToolbar && (
<EuiFlexItem grow={false}>
<NativeRenderer
render={activeVisualization.renderToolbar}
nativeProps={{
frame: framePublicAPI,
state: visualizationState,
setState: setVisualizationState,
}}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
</div>
<EuiPageContent className="lnsWorkspacePanelWrapper">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
column-gap: $euiSizeXL;
}

.lnsIndexPatternDimensionEditor__operation .euiListGroupItem__label {
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
width: 100%;
}

.lnsIndexPatternDimensionEditor__operation > button {
padding-top: 0;
padding-bottom: 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
EuiSpacer,
EuiListGroupItemProps,
EuiFormLabel,
EuiToolTip,
} from '@elastic/eui';
import { IndexPatternDimensionEditorProps } from './dimension_panel';
import { OperationSupportMatrix } from './operation_support';
Expand Down Expand Up @@ -118,20 +119,19 @@ export function DimensionEditor(props: DimensionEditorProps) {
definition.input === 'field' &&
fieldByOperation[operationType]?.has(selectedColumn.sourceField)) ||
(selectedColumn && !hasField(selectedColumn) && definition.input !== 'field'),
disabledStatus:
definition.getDisabledStatus &&
definition.getDisabledStatus(state.indexPatterns[state.currentIndexPatternId]),
};
});

const selectedColumnSourceField =
selectedColumn && 'sourceField' in selectedColumn ? selectedColumn.sourceField : undefined;

const currentFieldIsInvalid = useMemo(
() =>
fieldIsInvalid(selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern),
[selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern]
);
const currentFieldIsInvalid = useMemo(() => fieldIsInvalid(selectedColumn, currentIndexPattern), [
selectedColumn,
currentIndexPattern,
]);

const sideNavItems: EuiListGroupItemProps[] = operationsWithCompatibility.map(
({ operationType, compatibleWithCurrentField }) => {
({ operationType, compatibleWithCurrentField, disabledStatus }) => {
const isActive = Boolean(
incompatibleSelectedOperationType === operationType ||
(!incompatibleSelectedOperationType &&
Expand All @@ -147,7 +147,13 @@ export function DimensionEditor(props: DimensionEditorProps) {
}

let label: EuiListGroupItemProps['label'] = operationPanels[operationType].displayName;
if (isActive) {
if (disabledStatus) {
label = (
<EuiToolTip position="top" content={disabledStatus}>
<span>{operationPanels[operationType].displayName}</span>
</EuiToolTip>
mbondyra marked this conversation as resolved.
Show resolved Hide resolved
);
} else if (isActive) {
label = <strong>{operationPanels[operationType].displayName}</strong>;
}

Expand All @@ -157,6 +163,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
color,
isActive,
size: 's',
isDisabled: !!disabledStatus,
className: 'lnsIndexPatternDimensionEditor__operation',
'data-test-subj': `lns-indexPatternDimension-${operationType}${
compatibleWithCurrentField ? '' : ' incompatible'
Expand Down Expand Up @@ -216,7 +223,6 @@ export function DimensionEditor(props: DimensionEditorProps) {
? currentIndexPattern.getFieldByName(selectedColumn.sourceField)
: undefined,
});

setState(mergeLayer({ state, layerId, newLayer }));
},
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -975,12 +975,12 @@ describe('IndexPatternDimensionEditorPanel', () => {
expect(items.map(({ label }: { label: React.ReactNode }) => label)).toEqual([
'Average',
'Count',
'Last value',
'Maximum',
'Median',
'Minimum',
'Sum',
'Unique count',
'\u00a0',
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
import { DatasourceDimensionTriggerProps, DatasourceDimensionEditorProps } from '../../types';
import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public';
import { IndexPatternColumn } from '../indexpattern';
import { fieldIsInvalid } from '../utils';
import { isColumnInvalid } from '../utils';
import { IndexPatternPrivateState } from '../types';
import { DimensionEditor } from './dimension_editor';
import { DateRange } from '../../../common';
Expand Down Expand Up @@ -45,24 +45,22 @@ export const IndexPatternDimensionTriggerComponent = function IndexPatternDimens
) {
const layerId = props.layerId;
const layer = props.state.layers[layerId];
const selectedColumn: IndexPatternColumn | null = layer.columns[props.columnId] || null;
const currentIndexPattern = props.state.indexPatterns[layer.indexPatternId];
const { columnId, uniqueLabel } = props;

const selectedColumnSourceField =
selectedColumn && 'sourceField' in selectedColumn ? selectedColumn.sourceField : undefined;
const currentFieldIsInvalid = useMemo(
() =>
fieldIsInvalid(selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern),
[selectedColumnSourceField, selectedColumn?.operationType, currentIndexPattern]
const currentColumnHasErrors = useMemo(
() => isColumnInvalid(layer, columnId, currentIndexPattern),
[layer, columnId, currentIndexPattern]
);

const { columnId, uniqueLabel } = props;
const selectedColumn: IndexPatternColumn | null = layer.columns[props.columnId] || null;

if (!selectedColumn) {
return null;
}
const formattedLabel = wrapOnDot(uniqueLabel);

if (currentFieldIsInvalid) {
if (currentColumnHasErrors) {
return (
<EuiToolTip
content={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import { OperationMetadata } from '../../types';
import { IndexPatternColumn } from '../operations';
import { getFieldByNameFactory } from '../pure_helpers';

jest.mock('../operations');

const fields = [
{
name: 'timestamp',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ describe('IndexPattern Data Source', () => {
expect(messages).toHaveLength(1);
expect(messages![0]).toEqual({
shortMessage: 'Invalid reference.',
longMessage: 'Field "bytes" has an invalid reference.',
longMessage: '"Foo" has an invalid reference.',
});
});

Expand All @@ -764,7 +764,7 @@ describe('IndexPattern Data Source', () => {
col2: {
dataType: 'number',
isBucketed: false,
label: 'Foo',
label: 'Foo2',
operationType: 'count', // <= invalid
sourceField: 'memory',
},
Expand All @@ -777,7 +777,7 @@ describe('IndexPattern Data Source', () => {
expect(messages).toHaveLength(1);
expect(messages![0]).toEqual({
shortMessage: 'Invalid references.',
longMessage: 'Fields "bytes", "memory" have invalid reference.',
longMessage: '"Foo", "Foo2" have invalid reference.',
});
});

Expand All @@ -802,7 +802,7 @@ describe('IndexPattern Data Source', () => {
col2: {
dataType: 'number',
isBucketed: false,
label: 'Foo',
label: 'Foo2',
operationType: 'count', // <= invalid
sourceField: 'memory',
},
Expand All @@ -829,11 +829,11 @@ describe('IndexPattern Data Source', () => {
expect(messages).toEqual([
{
shortMessage: 'Invalid references on Layer 1.',
longMessage: 'Layer 1 has invalid references in fields "bytes", "memory".',
longMessage: 'Layer 1 has invalid references in "Foo", "Foo2".',
},
{
shortMessage: 'Invalid reference on Layer 2.',
longMessage: 'Layer 2 has an invalid reference in field "source".',
longMessage: 'Layer 2 has an invalid reference in "Foo".',
},
]);
});
Expand Down
Loading