Skip to content

feat(ui): add fitMaxContent prop to grideditable #93704

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

Merged
merged 4 commits into from
Jun 18, 2025
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
57 changes: 57 additions & 0 deletions static/app/components/gridEditable/index.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,61 @@ export default Storybook.story('GridEditable', story => {
</Fragment>
);
});

story('Enforcing Cell to fit Content', () => {
Copy link
Member

Choose a reason for hiding this comment

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

Thank you for adding docs for this 🏅

const newData = [
...data,
{
name: 'Something very long',
category:
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent fringilla ultricies turpis, quis lobortis leo varius ut. Maecenas venenatis purus a sodales facilisis.',
},
] as ExampleDataItem[];
return (
<Fragment>
<p>
Passing
<Storybook.JSXProperty name="fit" value={'max-content'} /> will set the width of
the grid to fit around the content.
</p>
<p>
<Storybook.JSXNode name="GridEditable" /> will by default resize the columns to
fit within it's container. So columns of long width may take up multiple lines
or be cut off, which might not be desired (ex. when the table has many columns
or is placed into a small container). One way to control column width this is to
provide
<Storybook.JSXProperty name="minColumnWidth" value={'number'} />, which applies
the same width to all columns. However, this does not account for varying widths
between columns, unlike this prop does.
</p>
<Storybook.SideBySide>
<div style={{width: 400}}>
<div>Without fit content is forced in multiple lines or cut off</div>
<GridEditable
data={newData}
columnOrder={columns}
columnSortBy={[]}
grid={{
renderHeadCell,
renderBodyCell,
}}
/>
</div>
<div style={{width: 400}}>
<div>With fit the content forces the table to expand (scroll)</div>
<GridEditable
data={newData}
columnOrder={columns}
columnSortBy={[]}
grid={{
renderHeadCell,
renderBodyCell,
}}
fit="max-content"
/>
</div>
</Storybook.SideBySide>
</Fragment>
);
});
});
12 changes: 8 additions & 4 deletions static/app/components/gridEditable/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {ReactNode} from 'react';
import type {CSSProperties, ReactNode} from 'react';
import {Component, createRef, Fragment} from 'react';

import EmptyStateWarning from 'sentry/components/emptyStateWarning';
Expand Down Expand Up @@ -98,23 +98,24 @@ type GridEditableProps<DataRow, ColumnKey> = {
bodyStyle?: React.CSSProperties;
emptyMessage?: React.ReactNode;
error?: unknown | null;

fit?: 'max-content';
/**
* Inject a set of buttons into the top of the grid table.
* The controlling component is responsible for handling any actions
* in these buttons and updating props to the GridEditable instance.
*/
headerButtons?: () => React.ReactNode;
height?: CSSProperties['height'];

height?: string | number;
highlightedRowKey?: number;

isLoading?: boolean;

minimumColWidth?: number;

onRowMouseOut?: (row: DataRow, key: number, event: React.MouseEvent) => void;
onRowMouseOver?: (row: DataRow, key: number, event: React.MouseEvent) => void;

onRowMouseOver?: (row: DataRow, key: number, event: React.MouseEvent) => void;
/**
* Whether columns in the grid can be resized.
*
Expand All @@ -123,6 +124,7 @@ type GridEditableProps<DataRow, ColumnKey> = {
resizable?: boolean;
scrollable?: boolean;
stickyHeader?: boolean;

/**
* GridEditable (mostly) do not maintain any internal state and relies on the
* parent component to tell it how/what to render and will mutate the view
Expand Down Expand Up @@ -457,6 +459,7 @@ class GridEditable<
'aria-label': ariaLabel,
bodyStyle,
stickyHeader,
fit,
} = this.props;
const showHeader = title || headerButtons;
return (
Expand All @@ -477,6 +480,7 @@ class GridEditable<
scrollable={scrollable}
height={height}
ref={this.refGrid}
fit={fit}
>
<GridHead sticky={stickyHeader}>{this.renderGridHead()}</GridHead>
<GridBody>{this.renderGridBody()}</GridBody>
Expand Down
15 changes: 13 additions & 2 deletions static/app/components/gridEditable/styles.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {CSSProperties} from 'react';
import {css} from '@emotion/react';
import styled from '@emotion/styled';

Expand Down Expand Up @@ -57,7 +58,7 @@ export const Body = styled(
showVerticalScrollbar?: boolean;
}) => (
<Panel {...props}>
<PanelBody>{children}</PanelBody>
<StyledPanelBody>{children}</StyledPanelBody>
</Panel>
)
)`
Expand All @@ -79,7 +80,11 @@ export const Body = styled(
* <thead>, <tbody>, <tr> are ignored by CSS Grid.
* The entire layout is determined by the usage of <th> and <td>.
*/
export const Grid = styled('table')<{height?: string | number; scrollable?: boolean}>`
export const Grid = styled('table')<{
fit?: 'max-content';
height?: CSSProperties['height'];
scrollable?: boolean;
}>`
position: inherit;
display: grid;

Expand All @@ -103,6 +108,8 @@ export const Grid = styled('table')<{height?: string | number; scrollable?: bool
max-height: ${typeof p.height === 'number' ? p.height + 'px' : p.height};
`
: ''}

width: ${p => p.fit}
`;

/**
Expand Down Expand Up @@ -332,3 +339,7 @@ export const GridResizer = styled('div')<{dataRows: number}>`
opacity: 0.4;
}
`;

const StyledPanelBody = styled(PanelBody)`
height: 100%;
`;
Loading