Skip to content

Commit

Permalink
[Lens] Revisit flyout fields style from column to row compressed (ela…
Browse files Browse the repository at this point in the history
…stic#120103)

* 💄 Revisit form style + spacers

* ✅ Fix tests

* ✨ Replace percentile input with range

* 💄 Fix mobile layout

* 💄 Make searchbar go below flyout

* 🔥 Remove unused jsx

* [Lens] Refactor Flyout Design Updates (#14)

* allow flyout to go full width on small viewports

* remove hiding of layer panel when flyout open

Co-authored-by: Kibana Machine <[email protected]>
Co-authored-by: Michael Marcialis <[email protected]>
  • Loading branch information
3 people authored and TinLe committed Dec 22, 2021
1 parent ceb2958 commit 5f8062d
Showing 16 changed files with 149 additions and 103 deletions.
Original file line number Diff line number Diff line change
@@ -5,17 +5,15 @@
// Use the EuiFlyout style
@include euiFlyout;
// But with custom positioning to keep it within the sidebar contents
position: absolute;
left: 0;
animation: euiFlyout $euiAnimSpeedNormal $euiAnimSlightResistance;
left: 0;
max-width: none !important;
z-index: $euiZContentMenu;

@include euiBreakpoint('l', 'xl') {
top: 0 !important;
height: 100% !important;
}

@include euiBreakpoint('xs', 's', 'm') {
@include euiFlyout;
position: absolute;
top: 0 !important;
}

.lnsFrameLayout__sidebar-isFullscreen & {
Original file line number Diff line number Diff line change
@@ -317,12 +317,7 @@ export function LayerPanel(

return (
<>
<section
tabIndex={-1}
ref={registerLayerRef}
className="lnsLayerPanel"
style={{ visibility: isDimensionPanelOpen ? 'hidden' : 'visible' }}
>
<section tabIndex={-1} ref={registerLayerRef} className="lnsLayerPanel">
<EuiPanel data-test-subj={`lns-layerPanel-${layerIndex}`} paddingSize="none">
<header className="lnsLayerPanel__layerHeader">
<EuiFlexGroup gutterSize="s" responsive={false} alignItems="center">
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@ export function AdvancedOptions(props: {
<>
{popoverOptions.length > 0 && (
<EuiText textAlign="right">
<EuiSpacer size="s" />
<EuiPopover
ownFocus
button={
@@ -69,17 +68,12 @@ export function AdvancedOptions(props: {
</EuiPopover>
</EuiText>
)}
{inlineOptions.length > 0 && (
<>
{inlineOptions.map((option) => (
<React.Fragment key={option.dataTestSubj}>
<EuiSpacer size="s" />
{inlineOptions.map((option, index) => (
<React.Fragment key={option.dataTestSubj}>
{option.inlineElement}
{index !== inlineOptions.length - 1 && <EuiSpacer size="s" />}
</React.Fragment>
))}
</>
)}
{option.inlineElement}
</React.Fragment>
))}
</>
);
}
Original file line number Diff line number Diff line change
@@ -414,7 +414,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
<div className="lnsIndexPatternDimensionEditor__section lnsIndexPatternDimensionEditor__section--padded lnsIndexPatternDimensionEditor__section--shaded">
<EuiFormLabel>
{i18n.translate('xpack.lens.indexPattern.functionsLabel', {
defaultMessage: 'Select a function',
defaultMessage: 'Functions',
})}
</EuiFormLabel>
<EuiSpacer size="s" />
@@ -474,7 +474,7 @@ export function DimensionEditor(props: DimensionEditorProps) {
/>
);
})}
<EuiSpacer size="s" />
{selectedOperationDefinition.selectionStyle !== 'field' ? <EuiSpacer size="s" /> : null}
</>
) : null}

Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ export function Filtering({

return (
<EuiFormRow
display="columnCompressed"
display="rowCompressed"
label={filterByLabel}
fullWidth
isInvalid={!isInputFilterValid}
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@ export function TimeScaling({

return (
<EuiFormRow
display="columnCompressed"
display="rowCompressed"
fullWidth
label={
<EuiToolTip
Original file line number Diff line number Diff line change
@@ -127,7 +127,7 @@ export function TimeShift({
}}
>
<EuiFormRow
display="columnCompressed"
display="rowCompressed"
fullWidth
data-test-subj="indexPattern-dimension-time-shift-row"
label={i18n.translate('xpack.lens.indexPattern.timeShift.label', {
Original file line number Diff line number Diff line change
@@ -193,7 +193,7 @@ function MovingAverageParamEditor({
label={i18n.translate('xpack.lens.indexPattern.movingAverage.window', {
defaultMessage: 'Window size',
})}
display="columnCompressed"
display="rowCompressed"
fullWidth
isInvalid={!isValidNumber(inputValue)}
>
Original file line number Diff line number Diff line change
@@ -223,7 +223,7 @@ export const lastValueOperation: OperationDefinition<LastValueIndexPatternColumn
label={i18n.translate('xpack.lens.indexPattern.lastValue.sortField', {
defaultMessage: 'Sort by date field',
})}
display="columnCompressed"
display="rowCompressed"
fullWidth
error={i18n.translate('xpack.lens.indexPattern.sortField.invalid', {
defaultMessage: 'Invalid field. Check your data view or pick another field.',
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
* 2.0.
*/

import React from 'react';
import React, { ChangeEvent } from 'react';
import { shallow, mount } from 'enzyme';
import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public';
import { IStorageWrapper } from 'src/plugins/kibana_utils/public';
@@ -14,11 +14,20 @@ import { createMockedIndexPattern } from '../../mocks';
import { percentileOperation } from './index';
import { IndexPattern, IndexPatternLayer } from '../../types';
import { PercentileIndexPatternColumn } from './percentile';
import { EuiFieldNumber } from '@elastic/eui';
import { EuiRange } from '@elastic/eui';
import { act } from 'react-dom/test-utils';
import { EuiFormRow } from '@elastic/eui';
import { TermsIndexPatternColumn } from './terms';

jest.mock('lodash', () => {
const original = jest.requireActual('lodash');

return {
...original,
debounce: (fn: unknown) => fn,
};
});

const uiSettingsMock = {} as IUiSettingsClient;

const defaultProps = {
@@ -272,8 +281,7 @@ describe('percentile', () => {
expect(input.prop('value')).toEqual('23');
});

it('should update state on change', async () => {
jest.useFakeTimers();
it('should update state on change', () => {
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
@@ -285,20 +293,19 @@ describe('percentile', () => {
/>
);

jest.runAllTimers();

const input = instance.find(
'[data-test-subj="lns-indexPattern-percentile-input"] input[type="number"]'
);
const input = instance
.find('[data-test-subj="lns-indexPattern-percentile-input"]')
.find(EuiRange);

await act(async () => {
input.simulate('change', { target: { value: '27' } });
act(() => {
input.prop('onChange')!(
{ currentTarget: { value: '27' } } as ChangeEvent<HTMLInputElement>,
true
);
});

instance.update();

jest.runAllTimers();

expect(updateLayerSpy).toHaveBeenCalledWith({
...layer,
columns: {
@@ -314,7 +321,7 @@ describe('percentile', () => {
});
});

it('should not update on invalid input, but show invalid value locally', async () => {
it('should not update on invalid input, but show invalid value locally', () => {
const updateLayerSpy = jest.fn();
const instance = mount(
<InlineOptions
@@ -326,20 +333,19 @@ describe('percentile', () => {
/>
);

jest.runAllTimers();

const input = instance.find(
'[data-test-subj="lns-indexPattern-percentile-input"] input[type="number"]'
);
const input = instance
.find('[data-test-subj="lns-indexPattern-percentile-input"]')
.find(EuiRange);

await act(async () => {
input.simulate('change', { target: { value: '12.12' } });
act(() => {
input.prop('onChange')!(
{ currentTarget: { value: '12.12' } } as ChangeEvent<HTMLInputElement>,
true
);
});

instance.update();

jest.runAllTimers();

expect(updateLayerSpy).not.toHaveBeenCalled();

expect(
@@ -351,7 +357,7 @@ describe('percentile', () => {
expect(
instance
.find('[data-test-subj="lns-indexPattern-percentile-input"]')
.find(EuiFieldNumber)
.find(EuiRange)
.prop('value')
).toEqual('12.12');
});
Original file line number Diff line number Diff line change
@@ -5,8 +5,8 @@
* 2.0.
*/

import { EuiFieldNumber, EuiFormRow } from '@elastic/eui';
import React, { useCallback, useState } from 'react';
import { EuiFormRow, EuiRange, EuiRangeProps } from '@elastic/eui';
import React, { useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { AggFunctionsMapping } from 'src/plugins/data/public';
import { buildExpressionFunction } from '../../../../../../../src/plugins/expressions/public';
@@ -21,7 +21,7 @@ import {
} from './helpers';
import { FieldBasedIndexPatternColumn } from './column_types';
import { adjustTimeScaleLabelSuffix } from '../time_scale_utils';
import { useDebounceWithOptions } from '../../../shared_components';
import { useDebouncedValue } from '../../../shared_components';

export interface PercentileIndexPatternColumn extends FieldBasedIndexPatternColumn {
operationType: 'percentile';
@@ -150,16 +150,14 @@ export const percentileOperation: OperationDefinition<
columnId,
indexPattern,
}) {
const [inputValue, setInputValue] = useState(String(currentColumn.params.percentile));

const inputValueAsNumber = Number(inputValue);
// an input is value if it's not an empty string, parses to a valid number, is between 0 and 100 (exclusive)
// and is an integer
const inputValueIsValid = isValidNumber(inputValue, true, 99, 1);

useDebounceWithOptions(
() => {
if (!inputValueIsValid) return;
const onChange = useCallback(
(value) => {
if (
!isValidNumber(value, true, 99, 1) ||
Number(value) === currentColumn.params.percentile
) {
return;
}
updateLayer({
...layer,
columns: {
@@ -171,33 +169,39 @@ export const percentileOperation: OperationDefinition<
: ofName(
indexPattern.getFieldByName(currentColumn.sourceField)?.displayName ||
currentColumn.sourceField,
inputValueAsNumber,
Number(value),
currentColumn.timeShift
),
params: {
...currentColumn.params,
percentile: inputValueAsNumber,
percentile: Number(value),
},
} as PercentileIndexPatternColumn,
},
});
},
{ skipFirstRender: true },
256,
[inputValue]
[updateLayer, layer, columnId, currentColumn, indexPattern]
);
const { inputValue, handleInputChange: handleInputChangeWithoutValidation } = useDebouncedValue<
string | undefined
>({
onChange,
value: String(currentColumn.params.percentile),
});
const inputValueIsValid = isValidNumber(inputValue, true, 99, 1);

const handleInputChange: EuiRangeProps['onChange'] = useCallback(
(e) => handleInputChangeWithoutValidation(String(e.currentTarget.value)),
[handleInputChangeWithoutValidation]
);

const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const val = String(e.target.value);
setInputValue(val);
}, []);
return (
<EuiFormRow
label={i18n.translate('xpack.lens.indexPattern.percentile.percentileValue', {
defaultMessage: 'Percentile',
})}
data-test-subj="lns-indexPattern-percentile-form"
display="columnCompressed"
display="rowCompressed"
fullWidth
isInvalid={!inputValueIsValid}
error={
@@ -207,14 +211,18 @@ export const percentileOperation: OperationDefinition<
})
}
>
<EuiFieldNumber
<EuiRange
data-test-subj="lns-indexPattern-percentile-input"
compressed
value={inputValue}
value={inputValue ?? ''}
min={1}
max={99}
step={1}
onChange={handleInputChange}
showInput
aria-label={i18n.translate('xpack.lens.indexPattern.percentile.percentileValue', {
defaultMessage: 'Percentile',
})}
/>
</EuiFormRow>
);
Loading

0 comments on commit 5f8062d

Please sign in to comment.