Skip to content

Commit

Permalink
improvement: column minimum width
Browse files Browse the repository at this point in the history
When resizing a column, the minimum width causes the columns not to
become smaller than the specified width. The minimum width however is
based on the initial width of the column. Developers should be able to
specify the width and minWidth separately to be able to default to a
comfortable width while allowing to decrease the width.

Added minWidth property to allow specifying minimum width separately
from initial width.
  • Loading branch information
Gido Manders committed Aug 6, 2024
1 parent f576583 commit 6951e5c
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 58 deletions.
72 changes: 44 additions & 28 deletions src/table/EpicTable/EpicTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import moment from 'moment';
import { MoreOrLess } from '../../core/MoreOrLess/MoreOrLess';
import { pageOf } from '../../utilities/page/page';
import { Tag } from '../../core/Tag/Tag';
import { ContentState, ContentStateMode } from '../../core/ContentState/ContentState';
import {
ContentState,
ContentStateMode
} from '../../core/ContentState/ContentState';
import { DateTimeInput } from '../../form/DateTimeInput/DateTimeInput';
import { Button } from '../../core/Button/Button';
import { Pagination } from '../../core/Pagination/Pagination';
Expand All @@ -33,16 +36,25 @@ storiesOf('table/EpicTable', module)
.addDecorator((Story) => (
<>
<Alert color="warning" className="mb-4">
<p className="mb-0">To be able to use EpicTable, you have to add lodash, overlayscrollbars and overlayscrollbars-react to your dependencies:</p>
<code>npm install --save lodash overlayscrollbars overlayscrollbars-react</code>
<p className="mb-0 mt-2">You also have to add the stylesheet to your project</p>
<code>@import &apos;overlayscrollbars/overlayscrollbars.css&apos;;</code>
<p className="mb-0">
To be able to use EpicTable, you have to add lodash, overlayscrollbars
and overlayscrollbars-react to your dependencies:
</p>
<code>
npm install --save lodash overlayscrollbars overlayscrollbars-react
</code>
<p className="mb-0 mt-2">
You also have to add the stylesheet to your project
</p>
<code>
@import &apos;overlayscrollbars/overlayscrollbars.css&apos;;
</code>
</Alert>
<Story />
</>
))
.add('full example', () => {
const [ widths, setWidths ] = useState(() => ({
const [widths, setWidths] = useState(() => ({
firstName: 300,
lastName: 200,
age: 200,
Expand All @@ -61,7 +73,7 @@ storiesOf('table/EpicTable', module)
setWidths((widths) => ({ ...widths, [name]: width }));
}

const [ filters, setFilters ] = useState(() => ({
const [filters, setFilters] = useState(() => ({
firstName: '',
lastName: '',
age: '',
Expand Down Expand Up @@ -91,7 +103,7 @@ storiesOf('table/EpicTable', module)
});
});

const [ sort, setSort ] = useState<{
const [sort, setSort] = useState<{
direction: EpicTableSortDirection;
column: string;
}>({ direction: 'NONE', column: 'firstName' });
Expand All @@ -111,16 +123,16 @@ storiesOf('table/EpicTable', module)

filteredPersons.sort(sortFn);

const [ page, setPage ] = useState(1);
const [page, setPage] = useState(1);

const pageOfPersons = pageOf(filteredPersons, page, 20);

const [ selected, setSelected ] = useState<Person[]>([]);
const [selected, setSelected] = useState<Person[]>([]);

function onSelect(person: Person, checked: boolean) {
if (checked) {
selected.push(person);
setSelected([ ...selected ]);
setSelected([...selected]);
} else {
const nextSelected = selected.filter((p) => p.id !== person.id);

Expand All @@ -139,16 +151,16 @@ storiesOf('table/EpicTable', module)
selected.push(p);
}

setSelected([ ...selected ]);
setSelected([...selected]);
});
} else {
setSelected([]);
}
}

const [ detail, setDetail ] = useState(-1);
const [detail, setDetail] = useState(-1);

const [ loading, setLoading ] = useState(false);
const [loading, setLoading] = useState(false);

useEffect(() => {
setLoading(true);
Expand Down Expand Up @@ -500,7 +512,11 @@ storiesOf('table/EpicTable', module)
{person.sex}
</EpicCell>
<EpicCell width={widths.actions} height={44}>
<Button icon="delete" color="danger" onClick={action('delete')} />
<Button
icon="delete"
color="danger"
onClick={action('delete')}
/>
<Button icon="edit" onClick={action('edit')} />
</EpicCell>
</EpicRow>
Expand Down Expand Up @@ -1014,8 +1030,8 @@ storiesOf('table/EpicTable', module)
);
})
.add('with overlay', () => {
const [ mode, setMode ] = useState<ContentStateMode>('loading');
const [ detail, setDetail ] = useState(false);
const [mode, setMode] = useState<ContentStateMode>('loading');
const [detail, setDetail] = useState(false);

return (
<Card body>
Expand Down Expand Up @@ -1106,7 +1122,7 @@ storiesOf('table/EpicTable', module)
);
})
.add('with sort', () => {
const [ direction, setDirection ] = useState<EpicTableSortDirection>('NONE');
const [direction, setDirection] = useState<EpicTableSortDirection>('NONE');

const sortFn =
direction === 'ASC'
Expand Down Expand Up @@ -1206,7 +1222,7 @@ storiesOf('table/EpicTable', module)
);
})
.add('with resizable columns', () => {
const [ widths, setWidths ] = useState(() => ({
const [widths, setWidths] = useState(() => ({
firstName: 300,
lastName: 100,
age: 100,
Expand All @@ -1222,6 +1238,7 @@ storiesOf('table/EpicTable', module)
<EpicTable>
<EpicRow header>
<EpicHeader
minWidth={100}
width={widths.firstName}
height={44}
onResize={(width) => changeSize('firstName', width)}
Expand Down Expand Up @@ -1408,7 +1425,7 @@ storiesOf('table/EpicTable', module)
);
})
.add('with pagination', () => {
const [ page, setPage ] = useState(1);
const [page, setPage] = useState(1);

const pageOfPersons = pageOf(persons, page, 20);

Expand Down Expand Up @@ -1508,7 +1525,7 @@ storiesOf('table/EpicTable', module)
);
})
.add('with expander', () => {
const [ expanded, setExpanded ] = useState(-1);
const [expanded, setExpanded] = useState(-1);

return (
<Card body>
Expand Down Expand Up @@ -1617,7 +1634,7 @@ storiesOf('table/EpicTable', module)
);
})
.add('with detail', () => {
const [ detail, setDetail ] = useState(-1);
const [detail, setDetail] = useState(-1);

return (
<Card body>
Expand Down Expand Up @@ -1809,7 +1826,7 @@ storiesOf('table/EpicTable', module)
);
})
.add('with detail but small', () => {
const [ detail, setDetail ] = useState(-1);
const [detail, setDetail] = useState(-1);

return (
<Card body>
Expand Down Expand Up @@ -1890,16 +1907,16 @@ storiesOf('table/EpicTable', module)
);
})
.add('with selection', () => {
const [ page, setPage ] = useState(1);
const [page, setPage] = useState(1);

const pageOfPersons = pageOf(persons, page, 20);

const [ selected, setSelected ] = useState<Person[]>([]);
const [selected, setSelected] = useState<Person[]>([]);

function onSelect(person: Person, checked: boolean) {
if (checked) {
selected.push(person);
setSelected([ ...selected ]);
setSelected([...selected]);
} else {
const nextSelected = selected.filter((p) => p.id !== person.id);

Expand All @@ -1918,7 +1935,7 @@ storiesOf('table/EpicTable', module)
selected.push(p);
}

setSelected([ ...selected ]);
setSelected([...selected]);
});
} else {
setSelected([]);
Expand Down Expand Up @@ -2045,4 +2062,3 @@ storiesOf('table/EpicTable', module)
</Card>
);
});

2 changes: 1 addition & 1 deletion src/table/EpicTable/cells/EpicHeader/EpicHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('Component: EpicHeader', () => {

test('with resize', () => {
render(
<EpicHeader width={300} height={44} onResize={jest.fn()}>
<EpicHeader minWidth={200} width={300} height={44} onResize={jest.fn()}>
epic header
</EpicHeader>
);
Expand Down
30 changes: 26 additions & 4 deletions src/table/EpicTable/cells/EpicHeader/EpicHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef } from 'react';

import { EpicResize } from './EpicResize/EpicResize';

Expand All @@ -8,6 +8,12 @@ export type Props = {
*/
children: React.ReactNode;

/**
* The minimum width of the cell in pixels.
* Defaults to the value of width.
*/
minWidth?: number;

/**
* The width of the cell in pixels.
*/
Expand Down Expand Up @@ -38,19 +44,35 @@ export type Props = {
*
* It is resizable whenever the `onResize` callback is defined.
*/
export function EpicHeader({ children, width, height = 44, onResize }: Props) {
export function EpicHeader({
children,
width,
minWidth,
height = 44,
onResize
}: Props) {
// Store the original width of when the EpicHeader was first rendered
// when minWidth is not specified to prevent resizing only allowing
// columns to grow.
const minimumWidth = useRef(minWidth ?? width);
return (
<div
className="epic-table-header d-flex align-items-center justify-content-between p-1"
style={{
minWidth: width,
minWidth: minimumWidth.current,
width,
height
}}
>
{children}

{onResize ? <EpicResize width={width} onResize={onResize} /> : null}
{onResize ? (
<EpicResize
minWidth={minimumWidth.current}
width={width}
onResize={onResize}
/>
) : null}
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EpicResize } from './EpicResize';
describe('Component: EpicResize', () => {
test('ui', () => {
const { container } = render(
<EpicResize width={300} onResize={jest.fn()} />
<EpicResize minWidth={100} width={300} onResize={jest.fn()} />
);

expect(container).toMatchSnapshot();
Expand All @@ -16,7 +16,7 @@ describe('Component: EpicResize', () => {
it('should resize with a throttle', () => {
const onResizeSpy = jest.fn();

render(<EpicResize width={300} onResize={onResizeSpy} />);
render(<EpicResize minWidth={100} width={300} onResize={onResizeSpy} />);

fireEvent.mouseDown(screen.getByTestId('epic-table-header-resize'), {
clientX: 0
Expand Down Expand Up @@ -48,7 +48,7 @@ describe('Component: EpicResize', () => {
const oldResizeSpy = jest.fn();

const { rerender } = render(
<EpicResize width={300} onResize={oldResizeSpy} />
<EpicResize minWidth={100} width={300} onResize={oldResizeSpy} />
);

fireEvent.mouseDown(screen.getByTestId('epic-table-header-resize'), {
Expand All @@ -62,7 +62,9 @@ describe('Component: EpicResize', () => {

const newResizeSpy = jest.fn();

rerender(<EpicResize width={300} onResize={newResizeSpy} />);
rerender(
<EpicResize minWidth={100} width={300} onResize={newResizeSpy} />
);

fireEvent.mouseMove(screen.getByTestId('epic-table-header-resize'), {
clientX: 42
Expand All @@ -78,7 +80,7 @@ describe('Component: EpicResize', () => {
it('should not allow resizing to below the initial width', () => {
const onResizeSpy = jest.fn();

render(<EpicResize width={300} onResize={onResizeSpy} />);
render(<EpicResize minWidth={100} width={300} onResize={onResizeSpy} />);

fireEvent.mouseDown(screen.getByTestId('epic-table-header-resize'), {
clientX: 300
Expand All @@ -88,11 +90,11 @@ describe('Component: EpicResize', () => {
expect(document.body.classList.contains('user-select-none')).toBe(true);

fireEvent.mouseMove(screen.getByTestId('epic-table-header-resize'), {
clientX: 280
clientX: 80
});

expect(onResizeSpy).toHaveBeenCalledTimes(1);
expect(onResizeSpy).toHaveBeenCalledWith(300);
expect(onResizeSpy).toHaveBeenCalledWith(100);
});
});
});
Loading

0 comments on commit 6951e5c

Please sign in to comment.