mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* [Logs UI] Make column configurations reorderable * Improve typing aand memoize callback * Guard against index bounds and rename reorderLogColumns * Fix useCallback memoization * Add functional test for reordering log columns * Use browser.keys instead of Key in functional test
This commit is contained in:
parent
8148b2dd26
commit
2f31e58ee8
8 changed files with 165 additions and 50 deletions
|
@ -205,6 +205,18 @@ export class WebElementWrapper {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses this element.
|
||||
*
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async focus() {
|
||||
await this.retryCall(async function focus(wrapper) {
|
||||
await wrapper.scrollIntoViewIfNecessary();
|
||||
await wrapper.driver.executeScript(`arguments[0].focus()`, wrapper._webElement);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the value of this element. This command has no effect if the underlying DOM element
|
||||
* is neither a text INPUT element nor a TEXTAREA element.
|
||||
|
|
17
x-pack/legacy/common/eui_draggable/index.d.ts
vendored
Normal file
17
x-pack/legacy/common/eui_draggable/index.d.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { EuiDraggable, EuiDragDropContext } from '@elastic/eui';
|
||||
|
||||
type PropsOf<T> = T extends React.ComponentType<infer ComponentProps> ? ComponentProps : never;
|
||||
type FirstArgumentOf<Func> = Func extends ((arg1: infer FirstArgument, ...rest: any[]) => any)
|
||||
? FirstArgument
|
||||
: never;
|
||||
export type DragHandleProps = FirstArgumentOf<
|
||||
Exclude<PropsOf<typeof EuiDraggable>['children'], React.ReactElement>
|
||||
>['dragHandleProps'];
|
||||
export type DropResult = FirstArgumentOf<FirstArgumentOf<typeof EuiDragDropContext>['onDragEnd']>;
|
|
@ -106,6 +106,20 @@ export const useLogColumnsConfigurationFormState = ({
|
|||
[formState.logColumns]
|
||||
);
|
||||
|
||||
const moveLogColumn = useCallback(
|
||||
(sourceIndex, destinationIndex) => {
|
||||
if (destinationIndex >= 0 && sourceIndex < formState.logColumns.length - 1) {
|
||||
const newLogColumns = [...formState.logColumns];
|
||||
newLogColumns.splice(destinationIndex, 0, newLogColumns.splice(sourceIndex, 1)[0]);
|
||||
setFormStateChanges(changes => ({
|
||||
...changes,
|
||||
logColumns: newLogColumns,
|
||||
}));
|
||||
}
|
||||
},
|
||||
[formState.logColumns]
|
||||
);
|
||||
|
||||
const errors = useMemo(
|
||||
() =>
|
||||
logColumnConfigurationProps.length <= 0
|
||||
|
@ -125,6 +139,7 @@ export const useLogColumnsConfigurationFormState = ({
|
|||
|
||||
return {
|
||||
addLogColumn,
|
||||
moveLogColumn,
|
||||
errors,
|
||||
logColumnConfigurationProps,
|
||||
formState,
|
||||
|
|
|
@ -14,9 +14,14 @@ import {
|
|||
EuiTitle,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiDragDropContext,
|
||||
EuiDraggable,
|
||||
EuiDroppable,
|
||||
EuiIcon,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { DragHandleProps, DropResult } from '../../../../../common/eui_draggable';
|
||||
|
||||
import { AddLogColumnButtonAndPopover } from './add_log_column_popover';
|
||||
import {
|
||||
|
@ -30,70 +35,95 @@ interface LogColumnsConfigurationPanelProps {
|
|||
isLoading: boolean;
|
||||
logColumnConfiguration: LogColumnConfigurationProps[];
|
||||
addLogColumn: (logColumn: LogColumnConfiguration) => void;
|
||||
moveLogColumn: (sourceIndex: number, destinationIndex: number) => void;
|
||||
}
|
||||
|
||||
export const LogColumnsConfigurationPanel: React.FunctionComponent<
|
||||
LogColumnsConfigurationPanelProps
|
||||
> = ({ addLogColumn, availableFields, isLoading, logColumnConfiguration }) => (
|
||||
<EuiForm>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="s" data-test-subj="sourceConfigurationLogColumnsSectionTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logColumnsSectionTitle"
|
||||
defaultMessage="Columns"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<AddLogColumnButtonAndPopover
|
||||
addLogColumn={addLogColumn}
|
||||
availableFields={availableFields}
|
||||
isDisabled={isLoading}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{logColumnConfiguration.length > 0 ? (
|
||||
logColumnConfiguration.map((column, index) => (
|
||||
<LogColumnConfigurationPanel
|
||||
logColumnConfigurationProps={column}
|
||||
key={`logColumnConfigurationPanel-${index}`}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<LogColumnConfigurationEmptyPrompt />
|
||||
)}
|
||||
</EuiForm>
|
||||
);
|
||||
> = ({ addLogColumn, moveLogColumn, availableFields, isLoading, logColumnConfiguration }) => {
|
||||
const onDragEnd = useCallback(
|
||||
({ source, destination }: DropResult) =>
|
||||
destination && moveLogColumn(source.index, destination.index),
|
||||
[moveLogColumn]
|
||||
);
|
||||
|
||||
return (
|
||||
<EuiForm>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="s" data-test-subj="sourceConfigurationLogColumnsSectionTitle">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.logColumnsSectionTitle"
|
||||
defaultMessage="Columns"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<AddLogColumnButtonAndPopover
|
||||
addLogColumn={addLogColumn}
|
||||
availableFields={availableFields}
|
||||
isDisabled={isLoading}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
{logColumnConfiguration.length > 0 ? (
|
||||
<EuiDragDropContext onDragEnd={onDragEnd}>
|
||||
<EuiDroppable droppableId="COLUMN_CONFIG_DROPPABLE_AREA">
|
||||
<>
|
||||
{/* Fragment here necessary for typechecking */}
|
||||
{logColumnConfiguration.map((column, index) => (
|
||||
<EuiDraggable
|
||||
key={`logColumnConfigurationPanel-${column.logColumnConfiguration.id}`}
|
||||
index={index}
|
||||
draggableId={column.logColumnConfiguration.id}
|
||||
customDragHandle
|
||||
>
|
||||
{provided => (
|
||||
<LogColumnConfigurationPanel
|
||||
dragHandleProps={provided.dragHandleProps}
|
||||
logColumnConfigurationProps={column}
|
||||
/>
|
||||
)}
|
||||
</EuiDraggable>
|
||||
))}
|
||||
</>
|
||||
</EuiDroppable>
|
||||
</EuiDragDropContext>
|
||||
) : (
|
||||
<LogColumnConfigurationEmptyPrompt />
|
||||
)}
|
||||
</EuiForm>
|
||||
);
|
||||
};
|
||||
|
||||
interface LogColumnConfigurationPanelProps {
|
||||
logColumnConfigurationProps: LogColumnConfigurationProps;
|
||||
dragHandleProps: DragHandleProps;
|
||||
}
|
||||
|
||||
const LogColumnConfigurationPanel: React.FunctionComponent<LogColumnConfigurationPanelProps> = ({
|
||||
logColumnConfigurationProps,
|
||||
}) => (
|
||||
const LogColumnConfigurationPanel: React.FunctionComponent<
|
||||
LogColumnConfigurationPanelProps
|
||||
> = props => (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
{logColumnConfigurationProps.type === 'timestamp' ? (
|
||||
<TimestampLogColumnConfigurationPanel
|
||||
logColumnConfigurationProps={logColumnConfigurationProps}
|
||||
/>
|
||||
) : logColumnConfigurationProps.type === 'message' ? (
|
||||
<MessageLogColumnConfigurationPanel
|
||||
logColumnConfigurationProps={logColumnConfigurationProps}
|
||||
/>
|
||||
{props.logColumnConfigurationProps.type === 'timestamp' ? (
|
||||
<TimestampLogColumnConfigurationPanel {...props} />
|
||||
) : props.logColumnConfigurationProps.type === 'message' ? (
|
||||
<MessageLogColumnConfigurationPanel {...props} />
|
||||
) : (
|
||||
<FieldLogColumnConfigurationPanel logColumnConfigurationProps={logColumnConfigurationProps} />
|
||||
<FieldLogColumnConfigurationPanel
|
||||
logColumnConfigurationProps={props.logColumnConfigurationProps}
|
||||
dragHandleProps={props.dragHandleProps}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
const TimestampLogColumnConfigurationPanel: React.FunctionComponent<
|
||||
LogColumnConfigurationPanelProps
|
||||
> = ({ logColumnConfigurationProps }) => (
|
||||
> = ({ logColumnConfigurationProps, dragHandleProps }) => (
|
||||
<ExplainedLogColumnConfigurationPanel
|
||||
fieldName="Timestamp"
|
||||
helpText={
|
||||
|
@ -107,12 +137,13 @@ const TimestampLogColumnConfigurationPanel: React.FunctionComponent<
|
|||
/>
|
||||
}
|
||||
removeColumn={logColumnConfigurationProps.remove}
|
||||
dragHandleProps={dragHandleProps}
|
||||
/>
|
||||
);
|
||||
|
||||
const MessageLogColumnConfigurationPanel: React.FunctionComponent<
|
||||
LogColumnConfigurationPanelProps
|
||||
> = ({ logColumnConfigurationProps }) => (
|
||||
> = ({ logColumnConfigurationProps, dragHandleProps }) => (
|
||||
<ExplainedLogColumnConfigurationPanel
|
||||
fieldName="Message"
|
||||
helpText={
|
||||
|
@ -123,19 +154,27 @@ const MessageLogColumnConfigurationPanel: React.FunctionComponent<
|
|||
/>
|
||||
}
|
||||
removeColumn={logColumnConfigurationProps.remove}
|
||||
dragHandleProps={dragHandleProps}
|
||||
/>
|
||||
);
|
||||
|
||||
const FieldLogColumnConfigurationPanel: React.FunctionComponent<{
|
||||
logColumnConfigurationProps: FieldLogColumnConfigurationProps;
|
||||
dragHandleProps: DragHandleProps;
|
||||
}> = ({
|
||||
logColumnConfigurationProps: {
|
||||
logColumnConfiguration: { field },
|
||||
remove,
|
||||
},
|
||||
dragHandleProps,
|
||||
}) => (
|
||||
<EuiPanel data-test-subj={`logColumnPanel fieldLogColumnPanel fieldLogColumnPanel:${field}`}>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<div data-test-subj="moveLogColumnHandle" {...dragHandleProps}>
|
||||
<EuiIcon type="grab" />
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={1}>
|
||||
<FormattedMessage
|
||||
id="xpack.infra.sourceConfiguration.fieldLogColumnTitle"
|
||||
|
@ -156,11 +195,17 @@ const ExplainedLogColumnConfigurationPanel: React.FunctionComponent<{
|
|||
fieldName: React.ReactNode;
|
||||
helpText: React.ReactNode;
|
||||
removeColumn: () => void;
|
||||
}> = ({ fieldName, helpText, removeColumn }) => (
|
||||
dragHandleProps: DragHandleProps;
|
||||
}> = ({ fieldName, helpText, removeColumn, dragHandleProps }) => (
|
||||
<EuiPanel
|
||||
data-test-subj={`logColumnPanel systemLogColumnPanel systemLogColumnPanel:${fieldName}`}
|
||||
>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<div data-test-subj="moveLogColumnHandle" {...dragHandleProps}>
|
||||
<EuiIcon type="grab" />
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={1}>{fieldName}</EuiFlexItem>
|
||||
<EuiFlexItem grow={3}>
|
||||
<EuiText size="s" color="subdued">
|
||||
|
|
|
@ -57,6 +57,7 @@ export const SourceConfigurationFlyout = injectI18n(
|
|||
|
||||
const {
|
||||
addLogColumn,
|
||||
moveLogColumn,
|
||||
indicesConfigurationProps,
|
||||
logColumnConfigurationProps,
|
||||
errors,
|
||||
|
@ -137,6 +138,7 @@ export const SourceConfigurationFlyout = injectI18n(
|
|||
<EuiSpacer />
|
||||
<LogColumnsConfigurationPanel
|
||||
addLogColumn={addLogColumn}
|
||||
moveLogColumn={moveLogColumn}
|
||||
availableFields={availableFields}
|
||||
isLoading={isLoading}
|
||||
logColumnConfiguration={logColumnConfigurationProps}
|
||||
|
@ -148,6 +150,7 @@ export const SourceConfigurationFlyout = injectI18n(
|
|||
: [],
|
||||
[
|
||||
addLogColumn,
|
||||
moveLogColumn,
|
||||
availableFields,
|
||||
indicesConfigurationProps,
|
||||
intl.formatMessage,
|
||||
|
|
|
@ -105,6 +105,7 @@ export const useSourceConfigurationFormState = (configuration?: SourceConfigurat
|
|||
|
||||
return {
|
||||
addLogColumn: logColumnsConfigurationFormState.addLogColumn,
|
||||
moveLogColumn: logColumnsConfigurationFormState.moveLogColumn,
|
||||
errors,
|
||||
formState,
|
||||
formStateChanges,
|
||||
|
|
|
@ -98,6 +98,8 @@ export default ({ getPageObjects, getService }: KibanaFunctionalTestDefaultProvi
|
|||
await infraSourceConfigurationFlyout.addTimestampLogColumn();
|
||||
await infraSourceConfigurationFlyout.addFieldLogColumn('host.name');
|
||||
|
||||
await infraSourceConfigurationFlyout.moveLogColumn(0, 1);
|
||||
|
||||
await infraSourceConfigurationFlyout.saveConfiguration();
|
||||
await infraSourceConfigurationFlyout.closeFlyout();
|
||||
});
|
||||
|
@ -105,7 +107,7 @@ export default ({ getPageObjects, getService }: KibanaFunctionalTestDefaultProvi
|
|||
it('renders the changed log columns with their headers', async () => {
|
||||
const columnHeaderLabels = await infraLogStream.getColumnHeaderLabels();
|
||||
|
||||
expect(columnHeaderLabels).to.eql(['Timestamp', 'host.name', '']);
|
||||
expect(columnHeaderLabels).to.eql(['host.name', 'Timestamp', '']);
|
||||
|
||||
const logStreamEntries = await infraLogStream.getStreamEntries();
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ export function InfraSourceConfigurationFlyoutProvider({
|
|||
const find = getService('find');
|
||||
const retry = getService('retry');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const browser = getService('browser');
|
||||
|
||||
return {
|
||||
/**
|
||||
|
@ -81,6 +82,25 @@ export function InfraSourceConfigurationFlyoutProvider({
|
|||
await this.removeLogColumn(0);
|
||||
}
|
||||
},
|
||||
async moveLogColumn(sourceIndex: number, destinationIndex: number) {
|
||||
const logColumnPanel = (await this.getLogColumnPanels())[sourceIndex];
|
||||
const moveLogColumnHandle = await testSubjects.findDescendant(
|
||||
'moveLogColumnHandle',
|
||||
logColumnPanel
|
||||
);
|
||||
await moveLogColumnHandle.focus();
|
||||
const movementDifference = destinationIndex - sourceIndex;
|
||||
await moveLogColumnHandle.pressKeys(browser.keys.SPACE);
|
||||
for (let i = 0; i < Math.abs(movementDifference); i++) {
|
||||
await new Promise(res => setTimeout(res, 100));
|
||||
if (movementDifference > 0) {
|
||||
await moveLogColumnHandle.pressKeys(browser.keys.ARROW_DOWN);
|
||||
} else {
|
||||
await moveLogColumnHandle.pressKeys(browser.keys.ARROW_UP);
|
||||
}
|
||||
}
|
||||
await moveLogColumnHandle.pressKeys(browser.keys.SPACE);
|
||||
},
|
||||
|
||||
/**
|
||||
* Form and flyout
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue