Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { i18n } from '@kbn/i18n';
import type { ExecutionContext } from '@kbn/expressions-plugin/common';
import type { DataGridDensity } from '@kbn/unified-data-table';
import type { FormatFactory, RowHeightMode } from '../../types';
import type { DatatableColumnResult } from './datatable_column';
import type { DatatableExpressionFunction } from './types';
Expand All @@ -32,6 +33,7 @@ export interface DatatableArgs {
headerRowHeight?: RowHeightMode;
headerRowHeightLines?: number;
pageSize?: PagingState['size'];
density?: DataGridDensity;
}

export const getDatatable = (
Expand Down Expand Up @@ -87,6 +89,10 @@ export const getDatatable = (
types: ['number'],
help: '',
},
density: {
types: ['string'],
help: '',
},
},
async fn(...args) {
/** Build optimization: prevent adding extra code into initial bundle **/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { DataGridDensity } from '@kbn/unified-data-table';
import { DensitySettings, type DensitySettingsProps } from './density_settings';

describe('DensitySettings', () => {
const defaultProps: DensitySettingsProps = {
dataGridDensity: DataGridDensity.NORMAL,
onChange: jest.fn(),
};

beforeEach(() => {
jest.clearAllMocks();
});

const renderDensitySettingsComponent = (propsOverrides: Partial<DensitySettingsProps> = {}) => {
const rtlRender = render(<DensitySettings {...defaultProps} {...propsOverrides} />);
return {
...rtlRender,
rerender: (newProps: Partial<DensitySettingsProps>) =>
rtlRender.rerender(<DensitySettings {...defaultProps} {...newProps} />),
};
};

it('renders the density settings component with label', () => {
renderDensitySettingsComponent();

expect(screen.getByLabelText('Density')).toBeInTheDocument();
expect(screen.getByTestId('lnsDensitySettings')).toBeInTheDocument();
});

it('displays all three density options and selects the provided option', () => {
renderDensitySettingsComponent();

expect(screen.getByRole('button', { name: 'Compact', pressed: false })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Normal', pressed: true })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Expanded', pressed: false })).toBeInTheDocument();
});

it('calls onChange with compact density when compact option is clicked', () => {
renderDensitySettingsComponent();

fireEvent.click(screen.getByRole('button', { name: 'Compact' }));

expect(defaultProps.onChange).toHaveBeenCalledTimes(1);
expect(defaultProps.onChange).toHaveBeenCalledWith(DataGridDensity.COMPACT);
});

it('calls onChange with expanded density when expanded option is clicked', () => {
renderDensitySettingsComponent();

fireEvent.click(screen.getByRole('button', { name: 'Expanded' }));

expect(defaultProps.onChange).toHaveBeenCalledTimes(1);
expect(defaultProps.onChange).toHaveBeenCalledWith(DataGridDensity.EXPANDED);
});

it('falls back to NORMAL density when an invalid density is provided', () => {
renderDensitySettingsComponent({
dataGridDensity: 'invalid' as DataGridDensity,
});

// The component should still render without errors
expect(screen.getByLabelText('Density')).toBeInTheDocument();

// The Normal button should be pressed
expect(screen.getByRole('button', { name: 'Normal', pressed: true })).toBeInTheDocument();
});

it('updates selection when props change', () => {
const { rerender } = renderDensitySettingsComponent();

// Initial render should have Normal selected
expect(screen.getByRole('button', { name: 'Normal', pressed: true }));

// Update props to Compact
rerender({ dataGridDensity: DataGridDensity.COMPACT });

// Now Compact should be pressed
expect(screen.getByRole('button', { name: 'Compact', pressed: true })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Normal', pressed: false })).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React, { useCallback } from 'react';
import { EuiButtonGroup, EuiFormRow } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DataGridDensity } from '@kbn/unified-data-table';

export interface DensitySettingsProps {
dataGridDensity: DataGridDensity;
onChange: (density: DataGridDensity) => void;
}

const densityValues = Object.values(DataGridDensity);

const getValidDensity = (density: string) => {
const isValidDensity = densityValues.includes(density as DataGridDensity);
return isValidDensity ? (density as DataGridDensity) : DataGridDensity.NORMAL;
};

const densityLabel = i18n.translate('xpack.lens.table.densityLabel', {
defaultMessage: 'Density',
});

const densityOptions = [
{
id: DataGridDensity.COMPACT,
label: i18n.translate('xpack.lens.table.labelCompact', {
defaultMessage: 'Compact',
}),
},
{
id: DataGridDensity.NORMAL,
label: i18n.translate('xpack.lens.table.labelNormal', {
defaultMessage: 'Normal',
}),
},
{
id: DataGridDensity.EXPANDED,
label: i18n.translate('xpack.lens.table.labelExpanded', {
defaultMessage: 'Expanded',
}),
},
];

export const DensitySettings: React.FC<DensitySettingsProps> = ({ dataGridDensity, onChange }) => {
// Falls back to NORMAL density when an invalid density is provided
const validDensity = getValidDensity(dataGridDensity);

const setDensity = useCallback(
(density: string) => {
onChange(getValidDensity(density));
},
[onChange]
);

return (
<EuiFormRow
aria-label={densityLabel}
display="columnCompressed"
data-test-subj="lnsDensitySettings"
>
<EuiButtonGroup
legend={densityLabel}
buttonSize="compressed"
isFullWidth
options={densityOptions}
onChange={setDensity}
idSelected={validDensity}
/>
</EuiFormRow>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { PaletteOutput } from '@kbn/coloring';
import { getTransposeId } from '@kbn/transpose-utils';
import { CustomPaletteState } from '@kbn/charts-plugin/common';
import { getCellColorFn } from '../../../shared_components/coloring/get_cell_color_fn';
import { DataGridDensity } from '@kbn/unified-data-table';

jest.mock('../../../shared_components/coloring/get_cell_color_fn', () => {
const mod = jest.requireActual('../../../shared_components/coloring/get_cell_color_fn');
Expand Down Expand Up @@ -762,4 +763,59 @@ describe('DatatableComponent', () => {
});
});
});

describe('gridStyle', () => {
it('should apply default grid style when density is not provided', () => {
renderDatatableComponent();
const table = screen.getByTestId('lnsDataTable');
expect(table).toHaveClass(/cellPadding-m-fontSize-m/);
});
it('should apply normal grid style when density is normal', () => {
renderDatatableComponent({
args: {
...args,
density: DataGridDensity.NORMAL,
},
});
const table = screen.getByTestId('lnsDataTable');
expect(table).toHaveClass(/cellPadding-m-fontSize-m/);
});
it('should apply compact grid style when density is compact', () => {
renderDatatableComponent({
args: {
...args,
density: DataGridDensity.COMPACT,
},
});
const table = screen.getByTestId('lnsDataTable');
expect(table).toHaveClass(/cellPadding-s-fontSize-s/);
});
it('should apply expanded grid style when density is expanded', () => {
renderDatatableComponent({
args: {
...args,
density: DataGridDensity.EXPANDED,
},
});
const table = screen.getByTestId('lnsDataTable');
expect(table).toHaveClass(/cellPadding-l-fontSize-l/);
});
it('should update grid style when density changes', () => {
const { rerender } = renderDatatableComponent({
args: {
...args,
density: DataGridDensity.NORMAL,
},
});
const table = screen.getByTestId('lnsDataTable');
expect(table).toHaveClass(/cellPadding-m-fontSize-m/);
rerender({
args: {
...args,
density: DataGridDensity.COMPACT,
},
});
expect(table).toHaveClass(/cellPadding-s-fontSize-s/);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { getColorCategories } from '@kbn/chart-expressions-common';
import { getOriginalId } from '@kbn/transpose-utils';
import { css } from '@emotion/react';
import { CoreTheme } from '@kbn/core/public';
import { DATA_GRID_DENSITY_STYLE_MAP } from '@kbn/unified-data-table/src/hooks/use_data_grid_density';
import { DATA_GRID_STYLE_NORMAL } from '@kbn/unified-data-table/src/constants';
import { getKbnPalettes } from '@kbn/palettes';
import type { LensTableRowContextMenuEvent } from '../../../types';
import type { FormatFactory } from '../../../../common/types';
Expand Down Expand Up @@ -69,7 +71,7 @@ import { getColumnAlignment } from '../utils';

export const DataContext = React.createContext<DataContextType>({});

const gridStyle: EuiDataGridStyle = {
const DATA_GRID_STYLE_DEFAULT: EuiDataGridStyle = {
border: 'horizontal',
header: 'shade',
footer: 'shade',
Expand Down Expand Up @@ -501,6 +503,16 @@ export const DatatableComponent = (props: DatatableRenderProps) => {
}
}, [columnConfig.columns, alignments, props.data, columns]);

const gridStyle = useMemo<EuiDataGridStyle>(
() => ({
...DATA_GRID_STYLE_DEFAULT,
...(props.args.density
? DATA_GRID_DENSITY_STYLE_MAP[props.args.density]
: DATA_GRID_STYLE_NORMAL),
}),
[props.args.density]
);

if (isEmpty) {
return (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { FramePublicAPI, VisualizationToolbarProps } from '../../../types';
import { PagingState } from '../../../../common/expressions';
import { fireEvent, render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { DataGridDensity } from '@kbn/unified-data-table';

// mocking random id generator function
jest.mock('@elastic/eui', () => {
Expand Down Expand Up @@ -45,6 +46,7 @@ describe('datatable toolbar', () => {
const renderAndOpenToolbar = async (overrides = {}) => {
const ROW_HEIGHT_SETTINGS_TEST_ID = 'lnsRowHeightSettings';
const HEADER_HEIGHT_SETTINGS_TEST_ID = 'lnsHeaderHeightSettings';
const DENSITY_SETTINGS_TEST_ID = 'lnsDensitySettings';

const rtlRender = render(<DataTableToolbar {...defaultProps} {...overrides} />);

Expand Down Expand Up @@ -78,6 +80,9 @@ describe('datatable toolbar', () => {
selectHeaderHeightOption: selectOptionFromButtonGroup(HEADER_HEIGHT_SETTINGS_TEST_ID),
getPaginationSwitch,
clickPaginationSwitch,
// Density
getDensityValue: getSelectedButtonInGroup(DENSITY_SETTINGS_TEST_ID),
selectDensityOption: selectOptionFromButtonGroup(DENSITY_SETTINGS_TEST_ID),
};
};

Expand All @@ -87,12 +92,14 @@ describe('datatable toolbar', () => {
getRowHeightValue,
getHeaderHeightValue,
getPaginationSwitch,
getDensityValue,
} = await renderAndOpenToolbar();

expect(getRowHeightValue()).toHaveTextContent(/custom/i);
expect(getHeaderHeightValue()).toHaveTextContent(/custom/i);
expect(getHeaderHeightCustomValue()).toHaveValue(3);
expect(getPaginationSwitch()).not.toBeChecked();
expect(getDensityValue()).toHaveTextContent(/normal/i);
});

it('should reflect passed state in the UI', async () => {
Expand All @@ -102,6 +109,7 @@ describe('datatable toolbar', () => {
getPaginationSwitch,
getHeaderHeightCustomValue,
getRowHeightCustomValue,
getDensityValue,
} = await renderAndOpenToolbar({
state: {
...defaultProps.state,
Expand All @@ -110,6 +118,7 @@ describe('datatable toolbar', () => {
headerRowHeight: 'custom',
headerRowHeightLines: 4,
paging: { size: 10, enabled: true },
density: DataGridDensity.COMPACT,
},
});

Expand All @@ -118,6 +127,7 @@ describe('datatable toolbar', () => {
expect(getHeaderHeightValue()).toHaveTextContent(/custom/i);
expect(getHeaderHeightCustomValue()).toHaveValue(4);
expect(getPaginationSwitch()).toBeChecked();
expect(getDensityValue()).toHaveTextContent(/compact/i);
});

it('should change row height to "Auto" mode when selected', async () => {
Expand Down Expand Up @@ -190,4 +200,22 @@ describe('datatable toolbar', () => {
paging: { ...defaultPagingState, enabled: false },
});
});

it('should change density to "Compact" when selected', async () => {
const { selectDensityOption } = await renderAndOpenToolbar();
selectDensityOption(/compact/i);
expect(defaultProps.setState).toHaveBeenCalledTimes(1);
expect(defaultProps.setState).toHaveBeenCalledWith({
density: DataGridDensity.COMPACT,
});
});

it('should change density to "Expanded" when selected', async () => {
const { selectDensityOption } = await renderAndOpenToolbar();
selectDensityOption(/expanded/i);
expect(defaultProps.setState).toHaveBeenCalledTimes(1);
expect(defaultProps.setState).toHaveBeenCalledWith({
density: DataGridDensity.EXPANDED,
});
});
});
Loading