mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
## Summary For accesibility reasons we need to label all the headers. At this point in time the actions from the unified data table have an empty header, for that we are grouping them in a column and labeling them as "Actions". | Scenario | Before | After (only icon) | |----------|--------|-----------------| | 1 icon | <img width="1035" alt="image" src="https://github.com/user-attachments/assets/2884904b-42ce-432f-9cf7-90140c349ca2" /> | <img width="1036" alt="image" src="https://github.com/user-attachments/assets/84947522-0426-4317-a642-1fb6cab27306" /> | | Multiple icons | <img width="558" alt="image" src="https://github.com/user-attachments/assets/fe4be017-116d-4586-888d-0e81b0b0395e" /> | <img width="557" alt="image" src="https://github.com/user-attachments/assets/58ea3261-703e-4d17-906c-29ee5a40569f" /> | ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --------- Co-authored-by: Matthias Wilhelm <matthias.wilhelm@elastic.co>
This commit is contained in:
parent
26891242ee
commit
b4b49acb68
27 changed files with 730 additions and 181 deletions
|
@ -30,8 +30,7 @@ interface DegradedDocsControlProps extends Partial<RowControlProps> {
|
|||
*/
|
||||
export const createDegradedDocsControl = (props?: DegradedDocsControlProps): RowControlColumn => ({
|
||||
id: 'connectedDegradedDocs',
|
||||
headerAriaLabel: actionsHeaderAriaLabelDegradedAction,
|
||||
renderControl: (Control, rowProps) => {
|
||||
render: (Control, rowProps) => {
|
||||
return <DegradedDocs Control={Control} rowProps={rowProps} {...props} />;
|
||||
},
|
||||
});
|
||||
|
|
|
@ -24,17 +24,11 @@ import { getStacktraceFields } from '../../utils/get_stack_trace_fields';
|
|||
*/
|
||||
export const createStacktraceControl = (props?: Partial<RowControlProps>): RowControlColumn => ({
|
||||
id: 'connectedStacktraceDocs',
|
||||
headerAriaLabel: actionsHeaderAriaLabelStacktraceAction,
|
||||
renderControl: (Control, rowProps) => {
|
||||
render: (Control, rowProps) => {
|
||||
return <Stacktrace Control={Control} rowProps={rowProps} {...props} />;
|
||||
},
|
||||
});
|
||||
|
||||
const actionsHeaderAriaLabelStacktraceAction = i18n.translate(
|
||||
'discover.customControl.stacktraceArialLabel',
|
||||
{ defaultMessage: 'Access to available stacktraces' }
|
||||
);
|
||||
|
||||
const stacktraceAvailableControlButton = i18n.translate(
|
||||
'discover.customControl.stacktrace.available',
|
||||
{ defaultMessage: 'Stacktraces available' }
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { EuiButtonIconProps, EuiDataGridControlColumn, IconType } from '@elastic/eui';
|
||||
import { EuiButtonIconProps, IconType } from '@elastic/eui';
|
||||
import type { Interpolation, Theme } from '@emotion/react';
|
||||
import React, { FC, ReactElement } from 'react';
|
||||
import { DataTableRecord } from '../../types';
|
||||
|
@ -32,7 +32,5 @@ export type RowControlComponent = FC<RowControlProps>;
|
|||
|
||||
export interface RowControlColumn {
|
||||
id: string;
|
||||
headerAriaLabel: string;
|
||||
headerCellRender?: EuiDataGridControlColumn['headerCellRender'];
|
||||
renderControl: (Control: RowControlComponent, props: RowControlRowProps) => ReactElement;
|
||||
render: (Control: RowControlComponent, props: RowControlRowProps) => ReactElement;
|
||||
}
|
||||
|
|
|
@ -120,10 +120,9 @@ export const testLeadingControlColumn: EuiDataGridControlColumn = {
|
|||
};
|
||||
|
||||
export const mockRowAdditionalLeadingControls = ['visBarVerticalStacked', 'heart', 'inspect'].map(
|
||||
(iconType, index): RowControlColumn => ({
|
||||
id: `exampleControl_${iconType}`,
|
||||
headerAriaLabel: `Example Row Control ${iconType}`,
|
||||
renderControl: (Control, rowProps) => {
|
||||
(iconType): RowControlColumn => ({
|
||||
id: `exampleRowControl-${iconType}`,
|
||||
render: (Control, rowProps) => {
|
||||
return (
|
||||
<Control
|
||||
data-test-subj={`exampleRowControl-${iconType}`}
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { getActionsColumn } from './actions_column';
|
||||
import { RowControlColumn } from '@kbn/discover-utils';
|
||||
import { render, within, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import * as actionsHeader from './actions_header';
|
||||
import { UnifiedDataTableContext } from '../../../table_context';
|
||||
import { dataTableContextComplexMock } from '../../../../__mocks__/table_context';
|
||||
|
||||
describe('getActionsColumn', () => {
|
||||
describe('given no columns', () => {
|
||||
it('returns null', () => {
|
||||
const result = getActionsColumn({
|
||||
baseColumns: [],
|
||||
externalControlColumns: undefined,
|
||||
rowAdditionalLeadingControls: undefined,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe.each([
|
||||
{
|
||||
baseColumns: [() => <div>Base column</div>],
|
||||
rowAdditionalLeadingControls: [],
|
||||
externalControlColumns: [],
|
||||
expectedWidth: 24,
|
||||
expectedTexts: ['Base column'],
|
||||
description: '1 base column',
|
||||
},
|
||||
{
|
||||
baseColumns: [() => <div>Base column 1</div>, () => <div>Base column 2</div>],
|
||||
rowAdditionalLeadingControls: [],
|
||||
externalControlColumns: [],
|
||||
expectedWidth: 48,
|
||||
expectedTexts: ['Base column 1', 'Base column 2'],
|
||||
description: '2 base columns',
|
||||
},
|
||||
{
|
||||
baseColumns: [],
|
||||
rowAdditionalLeadingControls: [
|
||||
{
|
||||
id: 'row-control-column',
|
||||
render: () => <div>Row control column</div>,
|
||||
},
|
||||
],
|
||||
externalControlColumns: [],
|
||||
expectedWidth: 24,
|
||||
expectedTexts: ['Row control column'],
|
||||
description: '1 row additional leading control column',
|
||||
},
|
||||
{
|
||||
baseColumns: [],
|
||||
rowAdditionalLeadingControls: [
|
||||
{
|
||||
id: 'row-control-column-1',
|
||||
render: () => <div>Row control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-2',
|
||||
render: () => <div>Row control column 2</div>,
|
||||
},
|
||||
],
|
||||
externalControlColumns: [],
|
||||
expectedWidth: 48,
|
||||
expectedTexts: ['Row control column 1', 'Row control column 2'],
|
||||
description: '2 row additional leading control columns',
|
||||
},
|
||||
{
|
||||
baseColumns: [],
|
||||
rowAdditionalLeadingControls: [],
|
||||
externalControlColumns: [
|
||||
{
|
||||
id: 'external-1',
|
||||
width: 80,
|
||||
rowCellRender: () => <div>External control column</div>,
|
||||
headerCellRender: () => <div>External control column</div>,
|
||||
},
|
||||
],
|
||||
expectedWidth: 80,
|
||||
expectedTexts: ['External control column'],
|
||||
description: '1 external control column',
|
||||
},
|
||||
{
|
||||
baseColumns: [],
|
||||
rowAdditionalLeadingControls: [],
|
||||
externalControlColumns: [
|
||||
{
|
||||
id: 'external-1',
|
||||
width: 80,
|
||||
rowCellRender: () => <div>External control column 1</div>,
|
||||
headerCellRender: () => <div>External control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'external-2',
|
||||
width: 90,
|
||||
rowCellRender: () => <div>External control column 2</div>,
|
||||
headerCellRender: () => <div>External control column 2</div>,
|
||||
},
|
||||
],
|
||||
expectedWidth: 170,
|
||||
expectedTexts: ['External control column 1', 'External control column 2'],
|
||||
description: '2 external control columns',
|
||||
},
|
||||
{
|
||||
baseColumns: [() => <div>Base column 1</div>, () => <div>Base column 2</div>],
|
||||
rowAdditionalLeadingControls: [
|
||||
{
|
||||
id: 'row-control-column-1',
|
||||
render: () => <div>Row control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-2',
|
||||
render: () => <div>Row control column 2</div>,
|
||||
},
|
||||
],
|
||||
externalControlColumns: [],
|
||||
expectedWidth: 96,
|
||||
expectedTexts: [
|
||||
'Base column 1',
|
||||
'Base column 2',
|
||||
'Row control column 1',
|
||||
'Row control column 2',
|
||||
],
|
||||
description: '2 base columns and 2 row additional leading control columns',
|
||||
},
|
||||
{
|
||||
baseColumns: [() => <div>Base column 1</div>, () => <div>Base column 2</div>],
|
||||
rowAdditionalLeadingControls: [],
|
||||
externalControlColumns: [
|
||||
{
|
||||
id: 'external-1',
|
||||
width: 80,
|
||||
rowCellRender: () => <div>External control column 1</div>,
|
||||
headerCellRender: () => <div>External control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'external-2',
|
||||
width: 90,
|
||||
rowCellRender: () => <div>External control column 2</div>,
|
||||
headerCellRender: () => <div>External control column 2</div>,
|
||||
},
|
||||
],
|
||||
expectedWidth: 222,
|
||||
expectedTexts: [
|
||||
'Base column 1',
|
||||
'Base column 2',
|
||||
'External control column 1',
|
||||
'External control column 2',
|
||||
],
|
||||
description: '2 base columns and 2 external control columns',
|
||||
},
|
||||
{
|
||||
baseColumns: [],
|
||||
rowAdditionalLeadingControls: [
|
||||
{
|
||||
id: 'row-control-column-1',
|
||||
render: () => <div>Row control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-2',
|
||||
render: () => <div>Row control column 2</div>,
|
||||
},
|
||||
],
|
||||
externalControlColumns: [
|
||||
{
|
||||
id: 'external-1',
|
||||
width: 80,
|
||||
rowCellRender: () => <div>External control column 1</div>,
|
||||
headerCellRender: () => <div>External control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'external-2',
|
||||
width: 90,
|
||||
rowCellRender: () => <div>External control column 2</div>,
|
||||
headerCellRender: () => <div>External control column 2</div>,
|
||||
},
|
||||
],
|
||||
expectedWidth: 218,
|
||||
expectedTexts: [
|
||||
'Row control column 1',
|
||||
'Row control column 2',
|
||||
'External control column 1',
|
||||
'External control column 2',
|
||||
],
|
||||
description: '2 row additional leading columns and 2 external control columns',
|
||||
},
|
||||
{
|
||||
baseColumns: [() => <div>Base column 1</div>, () => <div>Base column 2</div>],
|
||||
rowAdditionalLeadingControls: [
|
||||
{
|
||||
id: 'row-control-column-1',
|
||||
render: () => <div>Row control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-2',
|
||||
render: () => <div>Row control column 2</div>,
|
||||
},
|
||||
],
|
||||
externalControlColumns: [
|
||||
{
|
||||
id: 'external-1',
|
||||
width: 80,
|
||||
rowCellRender: () => <div>External control column 1</div>,
|
||||
headerCellRender: () => <div>External control column 1</div>,
|
||||
},
|
||||
{
|
||||
id: 'external-2',
|
||||
width: 90,
|
||||
rowCellRender: () => <div>External control column 2</div>,
|
||||
headerCellRender: () => <div>External control column 2</div>,
|
||||
},
|
||||
],
|
||||
expectedWidth: 270,
|
||||
expectedTexts: [
|
||||
'Base column 1',
|
||||
'Base column 2',
|
||||
'Row control column 1',
|
||||
'Row control column 2',
|
||||
'External control column 1',
|
||||
'External control column 2',
|
||||
],
|
||||
description: '2 of each column type',
|
||||
},
|
||||
])(
|
||||
'given $description',
|
||||
({
|
||||
expectedWidth,
|
||||
expectedTexts,
|
||||
baseColumns,
|
||||
rowAdditionalLeadingControls,
|
||||
externalControlColumns,
|
||||
}) => {
|
||||
it('returns a column with the correct width', () => {
|
||||
const result = getActionsColumn({
|
||||
baseColumns,
|
||||
rowAdditionalLeadingControls:
|
||||
rowAdditionalLeadingControls as unknown as RowControlColumn[],
|
||||
externalControlColumns,
|
||||
});
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
width: expectedWidth,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the header cell render function', () => {
|
||||
// Given
|
||||
const actionsHeaderSpy = jest.spyOn(actionsHeader, 'ActionsHeader');
|
||||
|
||||
// When
|
||||
const result = getActionsColumn({
|
||||
baseColumns,
|
||||
rowAdditionalLeadingControls:
|
||||
rowAdditionalLeadingControls as unknown as RowControlColumn[],
|
||||
externalControlColumns,
|
||||
});
|
||||
expect(result?.headerCellRender).toBeInstanceOf(Function);
|
||||
|
||||
// Then
|
||||
render(result?.headerCellRender());
|
||||
expect(actionsHeaderSpy).toHaveBeenCalledWith(
|
||||
{
|
||||
maxWidth: expectedWidth,
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
|
||||
it('should return the row cell render function', () => {
|
||||
// Given
|
||||
const result = getActionsColumn({
|
||||
baseColumns,
|
||||
rowAdditionalLeadingControls,
|
||||
externalControlColumns,
|
||||
});
|
||||
expect(result?.rowCellRender).toBeInstanceOf(Function);
|
||||
|
||||
// When
|
||||
render(
|
||||
<UnifiedDataTableContext.Provider value={dataTableContextComplexMock}>
|
||||
{result?.rowCellRender({
|
||||
setCellProps: jest.fn(),
|
||||
rowIndex: 0,
|
||||
colIndex: 0,
|
||||
columnId: 'actions',
|
||||
isExpandable: false,
|
||||
isExpanded: false,
|
||||
isDetails: false,
|
||||
})}
|
||||
</UnifiedDataTableContext.Provider>
|
||||
);
|
||||
|
||||
// Then
|
||||
expectedTexts.forEach((text) => {
|
||||
within(screen.getByTestId('unifiedDataTable_actionsColumnCell')).getByText(text);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe('given 4 row additional leading control columns', () => {
|
||||
const rowAdditionalLeadingControls: RowControlColumn[] = [
|
||||
{
|
||||
id: 'row-control-column-1',
|
||||
render: (Control) => (
|
||||
<Control iconType="empty" label="Row control column 1" onClick={jest.fn()} />
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-2',
|
||||
render: (Control) => (
|
||||
<Control iconType="empty" label="Row control column 2" onClick={jest.fn()} />
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-3',
|
||||
render: (Control) => (
|
||||
<Control iconType="empty" label="Row control column 3" onClick={jest.fn()} />
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'row-control-column-4',
|
||||
render: (Control) => (
|
||||
<Control iconType="empty" label="Row control column 4" onClick={jest.fn()} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
it('should return a menu control column', async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
|
||||
// When
|
||||
const result = getActionsColumn({
|
||||
baseColumns: [],
|
||||
rowAdditionalLeadingControls,
|
||||
externalControlColumns: [],
|
||||
});
|
||||
|
||||
render(
|
||||
<UnifiedDataTableContext.Provider value={dataTableContextComplexMock}>
|
||||
{result?.rowCellRender({
|
||||
setCellProps: jest.fn(),
|
||||
rowIndex: 0,
|
||||
colIndex: 0,
|
||||
columnId: 'actions',
|
||||
isExpandable: false,
|
||||
isExpanded: false,
|
||||
isDetails: false,
|
||||
})}
|
||||
</UnifiedDataTableContext.Provider>
|
||||
);
|
||||
|
||||
// Then
|
||||
|
||||
// The first item appears by itself
|
||||
expect(
|
||||
screen.getByTestId(`unifiedDataTable_rowControl_${rowAdditionalLeadingControls[0].id}`)
|
||||
);
|
||||
|
||||
// The rest of the items are in a menu
|
||||
expect(screen.getByLabelText('Additional actions')).toBeVisible();
|
||||
await user.click(screen.getByLabelText('Additional actions'));
|
||||
|
||||
rowAdditionalLeadingControls.slice(1).forEach((control) => {
|
||||
expect(screen.getByTestId(`unifiedDataTable_rowMenu_${control.id}`)).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import {
|
||||
EuiDataGridCellValueElementProps,
|
||||
EuiDataGridControlColumn,
|
||||
EuiFlexGroup,
|
||||
RenderCellValue,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import type { RowControlColumn } from '@kbn/discover-utils';
|
||||
import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants';
|
||||
import { getAdditionalRowControlColumns } from '../additional_row_control';
|
||||
import { ActionsHeader } from './actions_header';
|
||||
|
||||
const COLUMN_ID = 'actions';
|
||||
const EXTERNAL_CONTROL_COLUMNS_SPACING = 4;
|
||||
|
||||
const HorizontalSpacer = () => <div css={{ paddingLeft: EXTERNAL_CONTROL_COLUMNS_SPACING }} />;
|
||||
|
||||
export const getActionsColumn = ({
|
||||
baseColumns,
|
||||
externalControlColumns,
|
||||
rowAdditionalLeadingControls,
|
||||
}: {
|
||||
baseColumns: RenderCellValue[];
|
||||
rowAdditionalLeadingControls?: RowControlColumn[];
|
||||
externalControlColumns?: EuiDataGridControlColumn[];
|
||||
}) => {
|
||||
if (
|
||||
!baseColumns.length &&
|
||||
!externalControlColumns?.length &&
|
||||
!rowAdditionalLeadingControls?.length
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let columnWidth = baseColumns.length * DEFAULT_CONTROL_COLUMN_WIDTH;
|
||||
const actions = [...baseColumns];
|
||||
if (externalControlColumns?.length) {
|
||||
if (actions.length > 0) {
|
||||
actions.push(HorizontalSpacer);
|
||||
columnWidth += EXTERNAL_CONTROL_COLUMNS_SPACING;
|
||||
}
|
||||
|
||||
actions.push(...externalControlColumns.map((column) => column.rowCellRender));
|
||||
columnWidth += externalControlColumns.reduce((acc, column) => acc + column.width, 0);
|
||||
}
|
||||
if (rowAdditionalLeadingControls?.length) {
|
||||
const additionalRowControColumns = getAdditionalRowControlColumns(rowAdditionalLeadingControls);
|
||||
actions.push(...additionalRowControColumns);
|
||||
columnWidth += DEFAULT_CONTROL_COLUMN_WIDTH * additionalRowControColumns.length;
|
||||
}
|
||||
|
||||
return {
|
||||
id: COLUMN_ID,
|
||||
width: columnWidth,
|
||||
rowCellRender: (props: EuiDataGridCellValueElementProps) => (
|
||||
<EuiFlexGroup
|
||||
data-test-subj="unifiedDataTable_actionsColumnCell"
|
||||
responsive={false}
|
||||
alignItems="center"
|
||||
gutterSize="none"
|
||||
>
|
||||
{actions.map((Action, idx) => (
|
||||
<Action key={idx} {...props} />
|
||||
))}
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
headerCellRender: () => <ActionsHeader maxWidth={columnWidth} />,
|
||||
};
|
||||
};
|
|
@ -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
|
||||
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ActionsHeader } from './actions_header';
|
||||
|
||||
const setup = (props: React.ComponentProps<typeof ActionsHeader>) => {
|
||||
render(<ActionsHeader {...props} />);
|
||||
};
|
||||
|
||||
describe('<ActionsHeader />', () => {
|
||||
describe('when the maxWidth is greater than the text width', () => {
|
||||
it('should show the text', () => {
|
||||
const maxWidth = 500;
|
||||
setup({ maxWidth });
|
||||
|
||||
expect(screen.getByTestId('unifiedDataTable_actionsColumnHeaderText')).toBeVisible();
|
||||
});
|
||||
|
||||
it('should NOT show the icon', () => {
|
||||
const maxWidth = 500;
|
||||
setup({ maxWidth });
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('unifiedDataTable_actionsColumnHeaderIcon')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the maxWidth is less than the text width', () => {
|
||||
it('should NOT show the text', () => {
|
||||
const maxWidth = 0;
|
||||
setup({ maxWidth });
|
||||
|
||||
expect(
|
||||
screen.queryByTestId('unifiedDataTable_actionsColumnHeaderText')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show the icon', () => {
|
||||
const maxWidth = 0;
|
||||
setup({ maxWidth });
|
||||
|
||||
expect(screen.getByTestId('unifiedDataTable_actionsColumnHeaderIcon')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import React, { useLayoutEffect, useRef, useState } from 'react';
|
||||
import { EuiIconTip, EuiScreenReaderOnly, useEuiTheme } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import ColumnHeaderTruncateContainer from '../../column_header_truncate_container';
|
||||
|
||||
export const ActionsHeader = ({ maxWidth }: { maxWidth: number }) => {
|
||||
const textRef = useRef<HTMLSpanElement>(null);
|
||||
const [showText, setShowText] = useState(false);
|
||||
const { euiTheme } = useEuiTheme();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!textRef.current) return;
|
||||
const textWidth = textRef.current.getBoundingClientRect().width;
|
||||
setShowText(textWidth < maxWidth);
|
||||
}, [textRef, maxWidth, setShowText]);
|
||||
|
||||
const actionsText = i18n.translate('unifiedDataTable.controlColumnsActionHeader', {
|
||||
defaultMessage: 'Actions',
|
||||
});
|
||||
|
||||
return (
|
||||
<div css={{ padding: euiTheme.size.xs }}>
|
||||
<ColumnHeaderTruncateContainer>
|
||||
<EuiScreenReaderOnly>
|
||||
<span>
|
||||
{i18n.translate('unifiedDataTable.actionsColumnHeader', {
|
||||
defaultMessage: 'Actions column',
|
||||
})}
|
||||
</span>
|
||||
</EuiScreenReaderOnly>
|
||||
{showText ? (
|
||||
<span data-test-subj="unifiedDataTable_actionsColumnHeaderText">{actionsText}</span>
|
||||
) : (
|
||||
<EuiIconTip
|
||||
iconProps={{
|
||||
'data-test-subj': 'unifiedDataTable_actionsColumnHeaderIcon',
|
||||
}}
|
||||
type="iInCircle"
|
||||
content={actionsText}
|
||||
/>
|
||||
)}
|
||||
{/* Hidden measurement span */}
|
||||
<span
|
||||
ref={textRef}
|
||||
css={css`
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
`}
|
||||
>
|
||||
{actionsText}
|
||||
</span>
|
||||
</ColumnHeaderTruncateContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
|
||||
* Public License v 1"; you may not use this file except in compliance with, at
|
||||
* your election, the "Elastic License 2.0", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export { getActionsColumn } from './actions_column';
|
|
@ -9,6 +9,33 @@
|
|||
|
||||
import { getAdditionalRowControlColumns } from './get_additional_row_control_columns';
|
||||
import { mockRowAdditionalLeadingControls } from '../../../../__mocks__/external_control_columns';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { UnifiedDataTableContext } from '../../../table_context';
|
||||
import { dataTableContextComplexMock } from '../../../../__mocks__/table_context';
|
||||
import { userEvent } from '@testing-library/user-event';
|
||||
import { RowControlColumn } from '@kbn/discover-utils';
|
||||
|
||||
const setup = (rowControlColumns: RowControlColumn[]) => {
|
||||
const columns = getAdditionalRowControlColumns(rowControlColumns);
|
||||
|
||||
render(
|
||||
<UnifiedDataTableContext.Provider value={dataTableContextComplexMock}>
|
||||
{columns.map((Column, idx) => (
|
||||
<Column
|
||||
key={idx}
|
||||
setCellProps={jest.fn()}
|
||||
rowIndex={0}
|
||||
colIndex={0}
|
||||
columnId="actions"
|
||||
isExpandable
|
||||
isExpanded
|
||||
isDetails
|
||||
/>
|
||||
))}
|
||||
</UnifiedDataTableContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('getAdditionalRowControlColumns', () => {
|
||||
it('should work correctly for 0 controls', () => {
|
||||
|
@ -18,35 +45,46 @@ describe('getAdditionalRowControlColumns', () => {
|
|||
});
|
||||
|
||||
it('should work correctly for 1 control', () => {
|
||||
const columns = getAdditionalRowControlColumns([mockRowAdditionalLeadingControls[0]]);
|
||||
// Given
|
||||
const rowControlColumnMock = mockRowAdditionalLeadingControls[0];
|
||||
|
||||
expect(columns.map((column) => column.id)).toEqual([
|
||||
`additionalRowControl_${mockRowAdditionalLeadingControls[0].id}`,
|
||||
]);
|
||||
// When
|
||||
setup([rowControlColumnMock]);
|
||||
|
||||
// Then
|
||||
expect(screen.getByTestId(rowControlColumnMock.id)).toBeVisible();
|
||||
});
|
||||
|
||||
it('should work correctly for 2 controls', () => {
|
||||
const columns = getAdditionalRowControlColumns([
|
||||
mockRowAdditionalLeadingControls[0],
|
||||
mockRowAdditionalLeadingControls[1],
|
||||
]);
|
||||
// Given
|
||||
const mocks = [mockRowAdditionalLeadingControls[0], mockRowAdditionalLeadingControls[1]];
|
||||
|
||||
expect(columns.map((column) => column.id)).toEqual([
|
||||
`additionalRowControl_${mockRowAdditionalLeadingControls[0].id}`,
|
||||
`additionalRowControl_${mockRowAdditionalLeadingControls[1].id}`,
|
||||
]);
|
||||
// When
|
||||
setup(mocks);
|
||||
|
||||
// Then
|
||||
expect(screen.getByTestId(mocks[0].id)).toBeVisible();
|
||||
expect(screen.getByTestId(mocks[1].id)).toBeVisible();
|
||||
});
|
||||
|
||||
it('should work correctly for 3 and more controls', () => {
|
||||
const columns = getAdditionalRowControlColumns([
|
||||
it('should work correctly for 3 and more controls', async () => {
|
||||
// Given
|
||||
const user = userEvent.setup();
|
||||
const mocks = [
|
||||
mockRowAdditionalLeadingControls[0],
|
||||
mockRowAdditionalLeadingControls[1],
|
||||
mockRowAdditionalLeadingControls[2],
|
||||
]);
|
||||
];
|
||||
|
||||
expect(columns.map((column) => column.id)).toEqual([
|
||||
`additionalRowControl_${mockRowAdditionalLeadingControls[0].id}`,
|
||||
`additionalRowControl_menuControl`,
|
||||
]);
|
||||
// When
|
||||
setup(mocks);
|
||||
|
||||
// Then
|
||||
expect(screen.getByTestId(mocks[0].id)).toBeVisible();
|
||||
|
||||
// The other elements are hidden under the menu button
|
||||
await user.click(screen.getByTestId('unifiedDataTable_additionalRowControl_actionsMenu'));
|
||||
expect(screen.getByTestId(mocks[1].id)).toBeVisible();
|
||||
expect(screen.getByTestId(mocks[2].id)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,14 +7,11 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { EuiDataGridControlColumn } from '@elastic/eui';
|
||||
import { RowControlColumn } from '@kbn/discover-utils';
|
||||
import { getRowControlColumn } from './row_control_column';
|
||||
import { getRowMenuControlColumn } from './row_menu_control_column';
|
||||
|
||||
export const getAdditionalRowControlColumns = (
|
||||
rowControlColumns: RowControlColumn[]
|
||||
): EuiDataGridControlColumn[] => {
|
||||
export const getAdditionalRowControlColumns = (rowControlColumns: RowControlColumn[]) => {
|
||||
if (rowControlColumns.length <= 2) {
|
||||
return rowControlColumns.map(getRowControlColumn);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { getRowControlColumn } from './row_control_column';
|
||||
|
@ -24,14 +23,11 @@ describe('getRowControlColumn', () => {
|
|||
const mockClick = jest.fn();
|
||||
const props = {
|
||||
id: 'test_row_control',
|
||||
headerAriaLabel: 'row control',
|
||||
renderControl: jest.fn((Control, rowProps) => (
|
||||
render: jest.fn((Control, rowProps) => (
|
||||
<Control label={`test-${rowProps.rowIndex}`} iconType="heart" onClick={mockClick} />
|
||||
)),
|
||||
};
|
||||
const rowControlColumn = getRowControlColumn(props);
|
||||
const RowControlColumn =
|
||||
rowControlColumn.rowCellRender as React.FC<EuiDataGridCellValueElementProps>;
|
||||
const RowControlColumn = getRowControlColumn(props);
|
||||
render(
|
||||
<UnifiedDataTableContext.Provider value={contextMock}>
|
||||
<RowControlColumn
|
||||
|
@ -56,8 +52,7 @@ describe('getRowControlColumn', () => {
|
|||
it('should wrap the Control button with a tooltip when tooltipContent is passed', async () => {
|
||||
const props = {
|
||||
id: 'test_row_control',
|
||||
headerAriaLabel: 'row control',
|
||||
renderControl: jest.fn((Control, rowProps) => (
|
||||
render: jest.fn((Control, rowProps) => (
|
||||
<Control
|
||||
label={`test-${rowProps.rowIndex}`}
|
||||
tooltipContent="Control tooltip text!"
|
||||
|
@ -66,9 +61,7 @@ describe('getRowControlColumn', () => {
|
|||
/>
|
||||
)),
|
||||
};
|
||||
const rowControlColumn = getRowControlColumn(props);
|
||||
const RowControlColumn =
|
||||
rowControlColumn.rowCellRender as React.FC<EuiDataGridCellValueElementProps>;
|
||||
const RowControlColumn = getRowControlColumn(props);
|
||||
render(
|
||||
<UnifiedDataTableContext.Provider value={contextMock}>
|
||||
<RowControlColumn
|
||||
|
|
|
@ -8,22 +8,15 @@
|
|||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
EuiButtonIcon,
|
||||
EuiDataGridCellValueElementProps,
|
||||
EuiDataGridControlColumn,
|
||||
EuiScreenReaderOnly,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { EuiButtonIcon, EuiDataGridCellValueElementProps, EuiToolTip } from '@elastic/eui';
|
||||
import { RowControlColumn, RowControlProps } from '@kbn/discover-utils';
|
||||
import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants';
|
||||
import { useControlColumn } from '../../../hooks/use_control_column';
|
||||
|
||||
export const RowControlCell = ({
|
||||
renderControl,
|
||||
rowControlColumn,
|
||||
...props
|
||||
}: EuiDataGridCellValueElementProps & {
|
||||
renderControl: RowControlColumn['renderControl'];
|
||||
rowControlColumn: RowControlColumn;
|
||||
}) => {
|
||||
const { record, rowIndex } = useControlColumn(props);
|
||||
|
||||
|
@ -47,7 +40,7 @@ export const RowControlCell = ({
|
|||
<EuiButtonIcon
|
||||
aria-label={label}
|
||||
color={color ?? 'text'}
|
||||
data-test-subj={dataTestSubj ?? `unifiedDataTable_rowControl_${props.columnId}`}
|
||||
data-test-subj={dataTestSubj ?? `unifiedDataTable_rowControl_${rowControlColumn.id}`}
|
||||
disabled={disabled}
|
||||
iconSize="s"
|
||||
iconType={iconType}
|
||||
|
@ -75,29 +68,14 @@ export const RowControlCell = ({
|
|||
|
||||
return control;
|
||||
},
|
||||
[props.columnId, record, rowIndex]
|
||||
[rowControlColumn.id, record, rowIndex]
|
||||
);
|
||||
|
||||
return record ? renderControl(Control, { record, rowIndex }) : null;
|
||||
return record ? rowControlColumn.render(Control, { record, rowIndex }) : null;
|
||||
};
|
||||
|
||||
export const getRowControlColumn = (
|
||||
rowControlColumn: RowControlColumn
|
||||
): EuiDataGridControlColumn => {
|
||||
const { id, headerAriaLabel, headerCellRender, renderControl } = rowControlColumn;
|
||||
|
||||
return {
|
||||
id: `additionalRowControl_${id}`,
|
||||
width: DEFAULT_CONTROL_COLUMN_WIDTH,
|
||||
headerCellRender:
|
||||
headerCellRender ??
|
||||
(() => (
|
||||
<EuiScreenReaderOnly>
|
||||
<span>{headerAriaLabel}</span>
|
||||
</EuiScreenReaderOnly>
|
||||
)),
|
||||
rowCellRender: (props) => {
|
||||
return <RowControlCell {...props} renderControl={renderControl} />;
|
||||
},
|
||||
export const getRowControlColumn = (rowControlColumn: RowControlColumn) => {
|
||||
return (props: EuiDataGridCellValueElementProps) => {
|
||||
return <RowControlCell {...props} rowControlColumn={rowControlColumn} />;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { EuiDataGridCellValueElementProps } from '@elastic/eui';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { getRowMenuControlColumn } from './row_menu_control_column';
|
||||
import { dataTableContextMock } from '../../../../__mocks__/table_context';
|
||||
|
@ -25,8 +24,7 @@ describe('getRowMenuControlColumn', () => {
|
|||
const mockClick = jest.fn();
|
||||
const props = {
|
||||
id: 'test_row_menu_control',
|
||||
headerAriaLabel: 'row control',
|
||||
renderControl: jest.fn((Control, rowProps) => (
|
||||
render: jest.fn((Control, rowProps) => (
|
||||
<Control
|
||||
label={`test-${rowProps.rowIndex}`}
|
||||
tooltipContent={`test-${rowProps.rowIndex}`}
|
||||
|
@ -35,13 +33,11 @@ describe('getRowMenuControlColumn', () => {
|
|||
/>
|
||||
)),
|
||||
};
|
||||
const rowMenuControlColumn = getRowMenuControlColumn([
|
||||
const RowMenuControlColumn = getRowMenuControlColumn([
|
||||
props,
|
||||
mockRowAdditionalLeadingControls[0],
|
||||
mockRowAdditionalLeadingControls[1],
|
||||
]);
|
||||
const RowMenuControlColumn =
|
||||
rowMenuControlColumn.rowCellRender as React.FC<EuiDataGridCellValueElementProps>;
|
||||
render(
|
||||
<UnifiedDataTableContext.Provider value={contextMock}>
|
||||
<RowMenuControlColumn
|
||||
|
@ -55,7 +51,9 @@ describe('getRowMenuControlColumn', () => {
|
|||
/>
|
||||
</UnifiedDataTableContext.Provider>
|
||||
);
|
||||
const menuButton = screen.getByTestId('unifiedDataTable_test_row_menu_control');
|
||||
const menuButton = screen.getByTestId(
|
||||
'unifiedDataTable_additionalRowControl_test_row_menu_controlMenu'
|
||||
);
|
||||
expect(menuButton).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(menuButton);
|
||||
|
|
|
@ -13,14 +13,11 @@ import {
|
|||
EuiContextMenuItem,
|
||||
EuiContextMenuPanel,
|
||||
EuiDataGridCellValueElementProps,
|
||||
EuiDataGridControlColumn,
|
||||
EuiPopover,
|
||||
EuiScreenReaderOnly,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { RowControlColumn, RowControlProps } from '@kbn/discover-utils';
|
||||
import { DEFAULT_CONTROL_COLUMN_WIDTH } from '../../../constants';
|
||||
import { useControlColumn } from '../../../hooks/use_control_column';
|
||||
|
||||
/**
|
||||
|
@ -68,7 +65,7 @@ export const RowMenuControlCell = ({
|
|||
const Control = getControlComponent(rowControlColumn.id);
|
||||
return (
|
||||
<Fragment key={rowControlColumn.id}>
|
||||
{record ? rowControlColumn.renderControl(Control, { record, rowIndex }) : null}
|
||||
{record ? rowControlColumn.render(Control, { record, rowIndex }) : null}
|
||||
</Fragment>
|
||||
);
|
||||
}),
|
||||
|
@ -82,7 +79,7 @@ export const RowMenuControlCell = ({
|
|||
button={
|
||||
<EuiToolTip content={buttonLabel} delay="long">
|
||||
<EuiButtonIcon
|
||||
data-test-subj={`unifiedDataTable_${props.columnId}`}
|
||||
data-test-subj={`unifiedDataTable_additionalRowControl_${props.columnId}Menu`}
|
||||
iconSize="s"
|
||||
iconType="boxesVertical"
|
||||
color="text"
|
||||
|
@ -103,23 +100,8 @@ export const RowMenuControlCell = ({
|
|||
);
|
||||
};
|
||||
|
||||
export const getRowMenuControlColumn = (
|
||||
rowControlColumns: RowControlColumn[]
|
||||
): EuiDataGridControlColumn => {
|
||||
return {
|
||||
id: 'additionalRowControl_menuControl',
|
||||
width: DEFAULT_CONTROL_COLUMN_WIDTH,
|
||||
headerCellRender: () => (
|
||||
<EuiScreenReaderOnly>
|
||||
<span>
|
||||
{i18n.translate('unifiedDataTable.additionalActionsColumnHeader', {
|
||||
defaultMessage: 'Additional actions column',
|
||||
})}
|
||||
</span>
|
||||
</EuiScreenReaderOnly>
|
||||
),
|
||||
rowCellRender: (props) => {
|
||||
return <RowMenuControlCell {...props} rowControlColumns={rowControlColumns} />;
|
||||
},
|
||||
export const getRowMenuControlColumn = (rowControlColumns: RowControlColumn[]) => {
|
||||
return (props: EuiDataGridCellValueElementProps) => {
|
||||
return <RowMenuControlCell {...props} rowControlColumns={rowControlColumns} />;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -13,3 +13,5 @@ export {
|
|||
} from './color_indicator';
|
||||
|
||||
export { getAdditionalRowControlColumns } from './additional_row_control';
|
||||
|
||||
export { getActionsColumn } from './actions_column';
|
||||
|
|
|
@ -797,9 +797,13 @@ describe('UnifiedDataTable', () => {
|
|||
expect(
|
||||
findTestSubject(component, 'exampleRowControl-visBarVerticalStacked').exists()
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
findTestSubject(component, 'unifiedDataTable_additionalRowControl_menuControl').exists()
|
||||
).toBeTruthy();
|
||||
|
||||
// The other actions are within the popover
|
||||
findTestSubject(component, 'unifiedDataTable_additionalRowControl_actionsMenu')
|
||||
.first()
|
||||
.simulate('click');
|
||||
expect(findTestSubject(component, 'exampleRowControl-heart').exists()).toBeTruthy();
|
||||
expect(findTestSubject(component, 'exampleRowControl-inspect').exists()).toBeTruthy();
|
||||
},
|
||||
EXTENDED_JEST_TIMEOUT
|
||||
);
|
||||
|
|
|
@ -91,8 +91,8 @@ import { getCustomCellPopoverRenderer } from '../utils/get_render_cell_popover';
|
|||
import { useSelectedDocs } from '../hooks/use_selected_docs';
|
||||
import {
|
||||
getColorIndicatorControlColumn,
|
||||
getActionsColumn,
|
||||
type ColorIndicatorControlColumnParams,
|
||||
getAdditionalRowControlColumns,
|
||||
} from './custom_control_columns';
|
||||
import { useSorting } from '../hooks/use_sorting';
|
||||
|
||||
|
@ -949,34 +949,32 @@ export const UnifiedDataTable = ({
|
|||
const canSetExpandedDoc = Boolean(setExpandedDoc && !!renderDocumentView);
|
||||
|
||||
const leadingControlColumns: EuiDataGridControlColumn[] = useMemo(() => {
|
||||
const defaultControlColumns = getLeadControlColumns({ rows: displayedRows, canSetExpandedDoc });
|
||||
const internalControlColumns = controlColumnIds
|
||||
? // reorder the default controls as per controlColumnIds
|
||||
controlColumnIds.reduce((acc, id) => {
|
||||
const controlColumn = defaultControlColumns.find((col) => col.id === id);
|
||||
if (controlColumn) {
|
||||
acc.push(controlColumn);
|
||||
}
|
||||
return acc;
|
||||
}, [] as EuiDataGridControlColumn[])
|
||||
: defaultControlColumns;
|
||||
const { leadColumns, leadColumnsExtraContent } = getLeadControlColumns({
|
||||
rows: displayedRows,
|
||||
canSetExpandedDoc,
|
||||
});
|
||||
|
||||
const leadingColumns: EuiDataGridControlColumn[] = externalControlColumns
|
||||
? [...internalControlColumns, ...externalControlColumns]
|
||||
: internalControlColumns;
|
||||
const filteredLeadColumns = leadColumns.filter((column) =>
|
||||
controlColumnIds.includes(column.id)
|
||||
);
|
||||
|
||||
if (getRowIndicator) {
|
||||
const colorIndicatorControlColumn = getColorIndicatorControlColumn({
|
||||
getRowIndicator,
|
||||
});
|
||||
leadingColumns.unshift(colorIndicatorControlColumn);
|
||||
filteredLeadColumns.unshift(colorIndicatorControlColumn);
|
||||
}
|
||||
|
||||
if (rowAdditionalLeadingControls?.length) {
|
||||
leadingColumns.push(...getAdditionalRowControlColumns(rowAdditionalLeadingControls));
|
||||
const actionsColumn = getActionsColumn({
|
||||
baseColumns: leadColumnsExtraContent,
|
||||
rowAdditionalLeadingControls,
|
||||
externalControlColumns,
|
||||
});
|
||||
if (actionsColumn) {
|
||||
filteredLeadColumns.push(actionsColumn);
|
||||
}
|
||||
|
||||
return leadingColumns;
|
||||
return filteredLeadColumns;
|
||||
}, [
|
||||
canSetExpandedDoc,
|
||||
controlColumnIds,
|
||||
|
|
|
@ -12,9 +12,10 @@ import { i18n } from '@kbn/i18n';
|
|||
import {
|
||||
type EuiDataGridColumn,
|
||||
type EuiDataGridColumnCellAction,
|
||||
EuiScreenReaderOnly,
|
||||
type EuiDataGridControlColumn,
|
||||
EuiListGroupItemProps,
|
||||
type EuiDataGridColumnSortingConfig,
|
||||
RenderCellValue,
|
||||
} from '@elastic/eui';
|
||||
import type { DataView } from '@kbn/data-views-plugin/public';
|
||||
import { getDataViewFieldOrCreateFromColumnMeta } from '@kbn/data-view-utils';
|
||||
|
@ -67,21 +68,6 @@ const DataTableScoreColumnHeaderMemoized = React.memo(DataTableScoreColumnHeader
|
|||
export const OPEN_DETAILS = 'openDetails';
|
||||
export const SELECT_ROW = 'select';
|
||||
|
||||
const openDetails = {
|
||||
id: OPEN_DETAILS,
|
||||
width: DEFAULT_CONTROL_COLUMN_WIDTH,
|
||||
headerCellRender: () => (
|
||||
<EuiScreenReaderOnly>
|
||||
<span>
|
||||
{i18n.translate('unifiedDataTable.controlColumnHeader', {
|
||||
defaultMessage: 'Control column',
|
||||
})}
|
||||
</span>
|
||||
</EuiScreenReaderOnly>
|
||||
),
|
||||
rowCellRender: ExpandButton,
|
||||
};
|
||||
|
||||
const getSelect = (rows: DataTableRecord[]) => ({
|
||||
id: SELECT_ROW,
|
||||
width: DEFAULT_CONTROL_COLUMN_WIDTH,
|
||||
|
@ -95,11 +81,14 @@ export function getLeadControlColumns({
|
|||
}: {
|
||||
rows: DataTableRecord[];
|
||||
canSetExpandedDoc: boolean;
|
||||
}) {
|
||||
if (!canSetExpandedDoc) {
|
||||
return [getSelect(rows)];
|
||||
}
|
||||
return [openDetails, getSelect(rows)];
|
||||
}): {
|
||||
leadColumns: EuiDataGridControlColumn[];
|
||||
leadColumnsExtraContent: RenderCellValue[];
|
||||
} {
|
||||
return {
|
||||
leadColumns: [getSelect(rows)],
|
||||
leadColumnsExtraContent: canSetExpandedDoc ? [ExpandButton] : [],
|
||||
};
|
||||
}
|
||||
|
||||
function buildEuiGridColumn({
|
||||
|
|
|
@ -197,10 +197,9 @@ export const createExampleDataSourceProfileProvider = (): DataSourceProfileProvi
|
|||
return [
|
||||
...additionalControls,
|
||||
...['visBarVerticalStacked', 'heart', 'inspect'].map(
|
||||
(iconType, index): RowControlColumn => ({
|
||||
(iconType): RowControlColumn => ({
|
||||
id: `exampleControl_${iconType}`,
|
||||
headerAriaLabel: `Example Row Control ${iconType}`,
|
||||
renderControl: (Control, rowProps) => {
|
||||
render: (Control, rowProps) => {
|
||||
return (
|
||||
<Control
|
||||
data-test-subj={`exampleLogsControl_${iconType}`}
|
||||
|
|
|
@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
await discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
|
||||
it('should not render logs controls for non-logs data source', async () => {
|
||||
|
@ -42,7 +42,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
await discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -52,7 +52,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await dataViews.switchTo('my-example-logs');
|
||||
await discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
|
||||
// check Surrounding docs page
|
||||
await dataGrid.clickRowToggle();
|
||||
|
@ -63,7 +63,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await header.waitUntilLoadingHasFinished();
|
||||
|
||||
await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
|
||||
it('should not render logs controls for non-logs data source', async () => {
|
||||
|
@ -71,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await dataViews.switchTo('my-example-metrics');
|
||||
await discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
|
||||
// check Surrounding docs page
|
||||
await dataGrid.clickRowToggle();
|
||||
|
@ -82,7 +82,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await header.waitUntilLoadingHasFinished();
|
||||
|
||||
await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2683,7 +2683,6 @@
|
|||
"discover.customControl.degradedDocPresent": "Ce document n'a pas pu être analysé correctement. Tous les champs n'ont pas été remplis correctement.",
|
||||
"discover.customControl.stacktrace.available": "Traces d'appel disponibles",
|
||||
"discover.customControl.stacktrace.notAvailable": "Traces d'appel indisponibles",
|
||||
"discover.customControl.stacktraceArialLabel": "Accès aux traces d'appel disponibles",
|
||||
"discover.discoverBreadcrumbTitle": "Discover",
|
||||
"discover.discoverDefaultSearchSessionName": "Discover",
|
||||
"discover.discoverDescription": "Explorez vos données de manière interactive en interrogeant et en filtrant des documents bruts.",
|
||||
|
@ -8256,7 +8255,6 @@
|
|||
"uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.urlTemplateVariablesHelpLinkText": "Aide",
|
||||
"uiActionsEnhanced.drilldowns.urlDrilldownValidation.urlCompileErrorMessage": "Le modèle d'URL n'est pas valide dans le contexte donné. {message}.",
|
||||
"uiActionsEnhanced.drilldowns.urlDrilldownValidation.urlFormatGeneralErrorMessage": "Format d’URL non valide.",
|
||||
"unifiedDataTable.additionalActionsColumnHeader": "Colonne d'actions supplémentaires",
|
||||
"unifiedDataTable.advancedDiffModesTooltip": "Les modes avancés offrent des capacités de diffraction améliorées, mais ils fonctionnent sur des documents bruts et ne prennent donc pas en charge le formatage des champs.",
|
||||
"unifiedDataTable.clearSelection": "Effacer la sélection",
|
||||
"unifiedDataTable.compareSelectedRowsButtonDisabledTooltip": "La comparaison est limitée à {limit} lignes",
|
||||
|
@ -8270,7 +8268,6 @@
|
|||
"unifiedDataTable.comparisonColumnTooltip": "Document de comparaison : {documentId}",
|
||||
"unifiedDataTable.comparisonMaxFieldsCallout": "La comparaison est limitée aux {comparisonFields} de {totalFields} champs.",
|
||||
"unifiedDataTable.comparisonSettings": "Paramètres de comparaison",
|
||||
"unifiedDataTable.controlColumnHeader": "Colonne de commande",
|
||||
"unifiedDataTable.copyColumnNameToClipboard.toastTitle": "Copié dans le presse-papiers",
|
||||
"unifiedDataTable.copyColumnValuesToClipboard.toastTitle": "Valeurs de la colonne \"{column}\" copiées dans le presse-papiers",
|
||||
"unifiedDataTable.copyDocsToClipboardJSON": "Copier des documents au format JSON",
|
||||
|
|
|
@ -2682,7 +2682,6 @@
|
|||
"discover.customControl.degradedDocPresent": "このドキュメントを正しく解析できませんでした。一部のフィールドが正しく入力されていません。",
|
||||
"discover.customControl.stacktrace.available": "スタックトレースがあります",
|
||||
"discover.customControl.stacktrace.notAvailable": "スタックトレースがありません",
|
||||
"discover.customControl.stacktraceArialLabel": "使用可能なスタックトレースにアクセス",
|
||||
"discover.discoverBreadcrumbTitle": "Discover",
|
||||
"discover.discoverDefaultSearchSessionName": "Discover",
|
||||
"discover.discoverDescription": "ドキュメントにクエリーをかけたりフィルターを適用することで、データをインタラクティブに閲覧できます。",
|
||||
|
@ -8247,7 +8246,6 @@
|
|||
"uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.urlTemplateVariablesHelpLinkText": "ヘルプ",
|
||||
"uiActionsEnhanced.drilldowns.urlDrilldownValidation.urlCompileErrorMessage": "このURLテンプレートは指定されたコンテキストで無効です。{message}。",
|
||||
"uiActionsEnhanced.drilldowns.urlDrilldownValidation.urlFormatGeneralErrorMessage": "無効なURL形式です。",
|
||||
"unifiedDataTable.additionalActionsColumnHeader": "追加のアクション列",
|
||||
"unifiedDataTable.advancedDiffModesTooltip": "高度なモードでは、拡張差異機能を利用できますが、未加工ドキュメントで動作するため、フィールド書式設定はサポートされません。",
|
||||
"unifiedDataTable.clearSelection": "選択した項目をクリア",
|
||||
"unifiedDataTable.compareSelectedRowsButtonDisabledTooltip": "比較は{limit}行に制限されます",
|
||||
|
@ -8261,7 +8259,6 @@
|
|||
"unifiedDataTable.comparisonColumnTooltip": "比較ドキュメント:{documentId}",
|
||||
"unifiedDataTable.comparisonMaxFieldsCallout": "比較は、{totalFields}フィールド中{comparisonFields}フィールドに制限されます。",
|
||||
"unifiedDataTable.comparisonSettings": "比較設定",
|
||||
"unifiedDataTable.controlColumnHeader": "列の制御",
|
||||
"unifiedDataTable.copyColumnNameToClipboard.toastTitle": "クリップボードにコピーされました",
|
||||
"unifiedDataTable.copyColumnValuesToClipboard.toastTitle": "\"{column}\"列の値がクリップボードにコピーされました",
|
||||
"unifiedDataTable.copyDocsToClipboardJSON": "ドキュメントをJSONとしてコピー",
|
||||
|
|
|
@ -2683,7 +2683,6 @@
|
|||
"discover.customControl.degradedDocPresent": "无法正确解析此文档。并非所有字段都进行了正确填充。",
|
||||
"discover.customControl.stacktrace.available": "堆栈跟踪可用",
|
||||
"discover.customControl.stacktrace.notAvailable": "堆栈跟踪不可用",
|
||||
"discover.customControl.stacktraceArialLabel": "访问可用堆栈跟踪",
|
||||
"discover.discoverBreadcrumbTitle": "Discover",
|
||||
"discover.discoverDefaultSearchSessionName": "发现",
|
||||
"discover.discoverDescription": "通过查询和筛选原始文档来以交互方式浏览您的数据。",
|
||||
|
@ -8262,7 +8261,6 @@
|
|||
"uiActionsEnhanced.drilldowns.urlDrilldownCollectConfig.urlTemplateVariablesHelpLinkText": "帮助",
|
||||
"uiActionsEnhanced.drilldowns.urlDrilldownValidation.urlCompileErrorMessage": "URL 模板在给定上下文中无效。{message}。",
|
||||
"uiActionsEnhanced.drilldowns.urlDrilldownValidation.urlFormatGeneralErrorMessage": "URL 格式无效。",
|
||||
"unifiedDataTable.additionalActionsColumnHeader": "其他操作列",
|
||||
"unifiedDataTable.advancedDiffModesTooltip": "高级模式提供了增强型差异功能,但在原始文档上运行,因此不支持字段格式化。",
|
||||
"unifiedDataTable.clearSelection": "清除所选内容",
|
||||
"unifiedDataTable.compareSelectedRowsButtonDisabledTooltip": "对比限定为 {limit} 行",
|
||||
|
@ -8276,7 +8274,6 @@
|
|||
"unifiedDataTable.comparisonColumnTooltip": "比较文档:{documentId}",
|
||||
"unifiedDataTable.comparisonMaxFieldsCallout": "比较仅限于 {comparisonFields} 个(共 {totalFields} 个)字段。",
|
||||
"unifiedDataTable.comparisonSettings": "对比设置",
|
||||
"unifiedDataTable.controlColumnHeader": "控制列",
|
||||
"unifiedDataTable.copyColumnNameToClipboard.toastTitle": "已复制到剪贴板",
|
||||
"unifiedDataTable.copyColumnValuesToClipboard.toastTitle": "“{column}”列的值已复制到剪贴板",
|
||||
"unifiedDataTable.copyDocsToClipboardJSON": "将文档复制为 JSON",
|
||||
|
|
|
@ -559,7 +559,7 @@ describe('query tab with unified timeline', () => {
|
|||
const messageColumnIndex =
|
||||
customColumnOrder.findIndex((header) => header.id === 'message') +
|
||||
// offset for additional leading columns on left
|
||||
4;
|
||||
3;
|
||||
|
||||
expect(container.querySelector('[data-gridcell-column-id="message"]')).toHaveAttribute(
|
||||
'data-gridcell-column-index',
|
||||
|
|
|
@ -104,7 +104,7 @@ export const StyledTimelineUnifiedDataTable = styled.div.attrs(({ className = ''
|
|||
|
||||
/* auto row height */
|
||||
.euiDataGridRowCell__content--autoHeight {
|
||||
margin-top: 6px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
/* single row height */
|
||||
|
|
|
@ -30,7 +30,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
|
||||
it('should not render logs controls for non-logs data source', async () => {
|
||||
|
@ -43,7 +43,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
});
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -53,7 +53,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await dataViews.switchTo('my-example-logs');
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
|
||||
// check Surrounding docs page
|
||||
await dataGrid.clickRowToggle();
|
||||
|
@ -64,7 +64,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await testSubjects.existOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.existOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
|
||||
it('should not render logs controls for non-logs data source', async () => {
|
||||
|
@ -72,7 +72,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await dataViews.switchTo('my-example-metrics');
|
||||
await PageObjects.discover.waitUntilSearchingHasFinished();
|
||||
await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
|
||||
// check Surrounding docs page
|
||||
await dataGrid.clickRowToggle();
|
||||
|
@ -83,7 +83,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
|
||||
await testSubjects.missingOrFail('exampleLogsControl_visBarVerticalStacked');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_menuControl');
|
||||
await testSubjects.missingOrFail('unifiedDataTable_additionalRowControl_actionsMenu');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue