Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiDescriptionList] Add new columnWidths prop #7146

Merged
merged 8 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
2 changes: 1 addition & 1 deletion src-docs/src/views/datagrid/_snippets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ inMemory={{ level: 'sorting' }}`,
'renderFooterCellValue={({ rowIndex, columnId }) => {}}',
renderCustomGridBody: `// Optional; advanced usage only. This render function is an escape hatch for consumers who need to opt out of virtualization or otherwise need total custom control over how data grid cells are rendered.

renderCustomDataGridBody={({ visibleColumns, visibleRowData, Cell }) => (
renderCustomGridBody={({ visibleColumns, visibleRowData, Cell }) => (
<Cell colIndex={mappedFromVisibleColumns} visibleRowIndex={mappedFromVisibleRowData} />
)}`,
pagination: `pagination={{
Expand Down
28 changes: 28 additions & 0 deletions src-docs/src/views/description_list/column_widths.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';

import { EuiDescriptionList } from '../../../../src/components';

const listItems = [
{
title: '25% width',
description: '75% width',
},
{
title: 'TIE Fighter',
description:
'The sequel to XWING, join the dark side and fly for the Emperor.',
},
{
title: 'Quake 2',
description: 'The game that made me drop out of college.',
},
];

export default () => (
<EuiDescriptionList
listItems={listItems}
type="column"
columnWidths={[1, 3]} // Same as [25, 75]
style={{ maxInlineSize: '400px' }}
/>
);
72 changes: 57 additions & 15 deletions src-docs/src/views/description_list/description_list_example.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { Fragment } from 'react';
import React from 'react';

import { GuideSectionTypes } from '../../components';

import {
EuiCode,
EuiLink,
EuiDescriptionList,
EuiDescriptionListTitle,
EuiDescriptionListDescription,
Expand Down Expand Up @@ -59,6 +60,13 @@ const descriptionListResponsiveColumnSnippet = `<EuiDescriptionList
listItems={favoriteVideoGames}
/>`;

import DescriptionListColumnWidths from './column_widths';
const descriptionListColumnWidthsSource = require('!!raw-loader!./column_widths');
const descriptionListColumnWidthsSnippet = `<EuiDescriptionList
type="column"
columnWidths={[1, 3]}
/>`;

import DescriptionListStyling from './description_list_styling';
const descriptionListStylingSource = require('!!raw-loader!./description_list_styling');
const descriptionListStylingSnippet = [
Expand Down Expand Up @@ -181,13 +189,11 @@ export const DescriptionListExample = {
},
],
text: (
<Fragment>
<p>
Using the prop <EuiCode>type</EuiCode> set to{' '}
<EuiCode>column</EuiCode> description lists can be presented in an
inline, column format.
</p>
</Fragment>
<p>
Using the prop <EuiCode>type</EuiCode> set to{' '}
<EuiCode>column</EuiCode> description lists can be presented in an
inline, column format.
</p>
),
snippet: descriptionListColumnSnippet,
demo: <DescriptionListColumn />,
Expand All @@ -200,17 +206,53 @@ export const DescriptionListExample = {
},
],
text: (
<Fragment>
<p>
To return to the typical row format on smaller screens set{' '}
<EuiCode>type</EuiCode> to <EuiCode>responsiveColumn</EuiCode>. The
following list will only show the column format on larger screens.
</p>
</Fragment>
<p>
To return to the typical row format on smaller screens set{' '}
<EuiCode>type</EuiCode> to <EuiCode>responsiveColumn</EuiCode>. The
following list will only show the column format on larger screens.
</p>
),
snippet: descriptionListResponsiveColumnSnippet,
demo: <DescriptionListResponsiveColumn />,
},
{
source: [
{
type: GuideSectionTypes.TSX,
code: descriptionListColumnWidthsSource,
},
],
text: (
<>
<p>
The optional <EuiCode>columnWidths</EuiCode> prop allows customizing
specific column widths (e.g.{' '}
<EuiCode>{"['100px', '200px']"}</EuiCode>). The first array value
applies to the title column, and the second applies to the
description column.
</p>
<p>
Passing numbers instead of CSS width strings will use a ratio of
widths. For example, <EuiCode>[1, 3]</EuiCode> will render a
description column 3x the width of the title column. In other words,
descriptions will have a width of 75% and titles will have a width
of 25%.
</p>
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
<p>
For advanced usage, column width strings also accept{' '}
<EuiLink
href="https://css-tricks.com/snippets/css/complete-guide-grid/#aa-special-units-functions"
target="_blank"
>
CSS grid special units, sizing, keywords, and sizing functions
</EuiLink>
.
</p>
</>
),
snippet: descriptionListColumnWidthsSnippet,
demo: <DescriptionListColumnWidths />,
},
{
title: 'Inline',
source: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ exports[`useDataGridKeyboardShortcuts returns a popover containing a list of key
aria-labelledby="generated-id"
class="euiDescriptionList emotion-euiDescriptionList-column-center-s-s"
data-type="column"
style="grid-template-columns: 1fr 3fr;"
>
<dt
class="euiDescriptionList__title emotion-euiDescriptionList__title-column-compressed-right"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
overflow-block: auto;

.euiDescriptionList {
.euiDescriptionList__title {
width: 25%;
}

.euiDescriptionList__description {
width: 75%;
}
row-gap: 0; // Row spacing handled by default EuiText dd/dt styles
}
}
1 change: 1 addition & 0 deletions src/components/datagrid/controls/keyboard_shortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const useDataGridKeyboardShortcuts = (): {
<EuiDescriptionList
aria-labelledby={titleId}
type="column"
columnWidths={[1, 3]}
align="center"
compressed
listItems={[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ exports[`EuiDescriptionList props align left is rendered 1`] = `
/>
`;

exports[`EuiDescriptionList props column gap m is rendered 1`] = `
exports[`EuiDescriptionList props columnGutterSize m is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-s-m"
data-type="column"
/>
`;

exports[`EuiDescriptionList props column gap s is rendered 1`] = `
exports[`EuiDescriptionList props columnGutterSize s is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-s-s"
data-type="column"
Expand All @@ -88,20 +88,6 @@ exports[`EuiDescriptionList props compressed is rendered 1`] = `
/>
`;

exports[`EuiDescriptionList props gutter m is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-m-s"
data-type="column"
/>
`;

exports[`EuiDescriptionList props gutter s is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-s-s"
data-type="column"
/>
`;

exports[`EuiDescriptionList props listItems descriptionProps is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-row-left"
Expand Down Expand Up @@ -198,6 +184,20 @@ exports[`EuiDescriptionList props listItems titleProps is rendered 1`] = `
</dl>
`;

exports[`EuiDescriptionList props rowGutterSize m is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-m-s"
data-type="column"
/>
`;

exports[`EuiDescriptionList props rowGutterSize s is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-s-s"
data-type="column"
/>
`;

exports[`EuiDescriptionList props type column is rendered 1`] = `
<dl
class="euiDescriptionList emotion-euiDescriptionList-column-left-s-s"
Expand Down
54 changes: 52 additions & 2 deletions src/components/description_list/description_list.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('EuiDescriptionList', () => {
description: 'Description 3',
},
];

describe('props', () => {
describe('listItems', () => {
const { container } = render(
Expand Down Expand Up @@ -145,7 +146,7 @@ describe('EuiDescriptionList', () => {
});
});

describe('gutter', () => {
describe('rowGutterSize', () => {
ROW_GUTTER_SIZES.forEach((gutter) => {
test(`${gutter} is rendered`, () => {
const { container } = render(
Expand All @@ -157,7 +158,7 @@ describe('EuiDescriptionList', () => {
});
});

describe('column gap', () => {
describe('columnGutterSize', () => {
COLUMN_GUTTER_SIZES.forEach((columnGutterSize) => {
test(`${columnGutterSize} is rendered`, () => {
const { container } = render(
Expand All @@ -170,6 +171,55 @@ describe('EuiDescriptionList', () => {
expect(container.firstChild).toMatchSnapshot();
});
});

describe('columnWidths', () => {
it('renders the passed values as an inline css grid style', () => {
const { container } = render(
<EuiDescriptionList
type="column"
columnWidths={['100px', 'minmax(200px, auto)']}
/>
);
expect(container.firstChild).toHaveStyle(
'grid-template-columns: 100px minmax(200px, auto)'
);
});

it('converts numbers into fr grid units', () => {
const { container } = render(
<EuiDescriptionList type="column" columnWidths={[1, 2]} />
);
expect(container.firstChild).toHaveStyle(
'grid-template-columns: 1fr 2fr'
);
});

it('respects custom styles', () => {
const { container } = render(
<EuiDescriptionList
type="column"
columnWidths={[3, 4]}
style={{ color: 'red' }}
/>
);
expect(container.firstChild).toHaveStyle('color: red');
});

it('correctly removes inline styles when responsive columns collapse to rows', () => {
const { container, rerender } = render(
<EuiDescriptionList type="responsiveColumn" columnWidths={[3, 4]} />
);
expect(container.firstChild).toHaveStyle(
'grid-template-columns: 3fr 4fr'
);

mockUseIsWithinBreakpoints.mockReturnValue(true);
rerender(
<EuiDescriptionList type="responsiveColumn" columnWidths={[3, 4]} />
);
expect(container.firstChild).toHaveAttribute('style', '');
});
});
});
});
});
26 changes: 25 additions & 1 deletion src/components/description_list/description_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const EuiDescriptionList: FunctionComponent<
align = 'left',
children,
className,
style,
compressed = false,
descriptionProps,
listItems,
Expand All @@ -32,6 +33,7 @@ export const EuiDescriptionList: FunctionComponent<
type: _type = 'row',
rowGutterSize = 's',
columnGutterSize = 's',
columnWidths,
...rest
}) => {
const showResponsiveColumns = useIsWithinBreakpoints(['xs', 's']);
Expand All @@ -54,6 +56,22 @@ export const EuiDescriptionList: FunctionComponent<
type === 'column' && styles.columnGap[columnGutterSize],
];

const inlineStyles = useMemo(() => {
if (type === 'column' && columnWidths) {
// Leave string values as is - e.g. if a consumer passes in a specific '200px' or 'minmax()'
const convertNumbersToFr = (value: number | string) =>
typeof value === 'number' ? `${value}fr` : value;

const titleWidth = convertNumbersToFr(columnWidths[0]);
const descriptionWidth = convertNumbersToFr(columnWidths[1]);
return {
gridTemplateColumns: `${titleWidth} ${descriptionWidth}`,
...style,
};
}
return style;
}, [style, type, columnWidths]);

const classes = classNames('euiDescriptionList', className);

let childrenOrListItems = null;
Expand Down Expand Up @@ -81,7 +99,13 @@ export const EuiDescriptionList: FunctionComponent<
<EuiDescriptionListContext.Provider
value={{ type, compressed, textStyle, align, rowGutterSize }}
>
<dl className={classes} css={cssStyles} {...rest} data-type={_type}>
<dl
className={classes}
css={cssStyles}
style={inlineStyles}
{...rest}
data-type={_type}
>
{childrenOrListItems}
</dl>
</EuiDescriptionListContext.Provider>
Expand Down
14 changes: 14 additions & 0 deletions src/components/description_list/description_list_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,20 @@ export interface EuiDescriptionListProps {
* Only applies to `column` and `responsiveColumn` types.
*/
columnGutterSize?: EuiDescriptionListColumnGapSizes;
/**
* Allows customizing specific column widths (e.g. `['100px', '200px']`). The first
* array value applies to the title column, and the second applies to the description column.
*
* Passing numbers instead of CSS width strings will use a ratio of widths.
* For example, [1, 3] will render a description column 3x the width of the title column.
* In other words, descriptions will have a width of `75%` and titles will have a width of `25%`.
*
* Only applies to `column` and `responsiveColumn` types.
*
* _Advanced usage note:_ column width strings also accept [CSS grid special units,
* sizing, keywords, and sizing functions](https://css-tricks.com/snippets/css/complete-guide-grid/#aa-special-units-functions).
*/
columnWidths?: [number | string, number | string];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking for feedback on whether folks think the [x, y] array syntax is the most intuitive for devs, or whether we want to use object notation for more specific keys, e.g.

columnWidths={{
  titles: '100px',
  descriptions: '300px',
}}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My 2¢ is the array is the more intuitive syntax. When I think of columns, I'm already thinking in an array-like structure. Being able to set an array and pass a string or number in two places, without having to know the correct key names, is huge. Admittedly this is less of an issue with TypeScript suggestions, but I still lean toward array.

That's an opinion loosely held, so I have no objection if there's a strong argument to move to an object.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My 2¢ is the array is the more intuitive syntax. When I think of columns, I'm already thinking in an array-like structure.

I really like this reasoning - thanks Trevor! I will note one possible reason to use an object instead is if a consumer wants to specify the width of the title column but let the description column remain auto - an obj allows that, the array (currently) doesn't.

That being said, I don't see that as a use case right now, so let's move forward with the array syntax for now. We're looking to move fast due to this affecting the current Kibana upgrade, and if absolutely needed, we can come back in later and add other types with an |.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an excellent plan!

}

export const CHILD_TYPES = ['row', 'inline', 'column'] as const;
Expand Down
6 changes: 6 additions & 0 deletions upcoming_changelogs/7146.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- Updated `EuiDescriptionList` with new `columnWidths` prop

**Bug fixes**

- Fixed `EuiDataGrid`'s keyboard shortcuts popover display

Loading