Enable deletion on table views (#113)

* Enable deletion on table views

* Add tests

* Enable deletion on table views for companies too
This commit is contained in:
Charles Bochet
2023-05-08 23:26:37 +02:00
committed by GitHub
parent 94ea9835a9
commit 2212900663
12 changed files with 291 additions and 57 deletions

View File

@ -2,7 +2,6 @@ import * as React from 'react';
import {
ColumnDef,
RowSelectionState,
flexRender,
getCoreRowModel,
useReactTable,
@ -16,6 +15,12 @@ import {
SortType,
} from './table-header/interface';
declare module 'react' {
function forwardRef<T, P = object>(
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null,
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}
type OwnProps<TData, SortField, FilterProperties> = {
data: Array<TData>;
columns: Array<ColumnDef<TData, any>>;
@ -35,7 +40,7 @@ type OwnProps<TData, SortField, FilterProperties> = {
filter: FilterType<FilterProperties> | null,
searchValue: string,
) => void;
onRowSelectionChange?: (rowSelection: RowSelectionState) => void;
onRowSelectionChange?: (rowSelection: string[]) => void;
};
const StyledTable = styled.table`
@ -89,34 +94,48 @@ const StyledTableScrollableContainer = styled.div`
flex: 1;
`;
function Table<TData extends { id: string }, SortField, FilterProperies>({
data,
columns,
viewName,
viewIcon,
availableSorts,
availableFilters,
filterSearchResults,
onSortsUpdate,
onFiltersUpdate,
onFilterSearch,
onRowSelectionChange,
}: OwnProps<TData, SortField, FilterProperies>) {
const [rowSelection, setRowSelection] = React.useState({});
React.useEffect(() => {
onRowSelectionChange && onRowSelectionChange(rowSelection);
}, [rowSelection, onRowSelectionChange]);
const Table = <TData extends { id: string }, SortField, FilterProperies>(
{
data,
columns,
viewName,
viewIcon,
availableSorts,
availableFilters,
filterSearchResults,
onSortsUpdate,
onFiltersUpdate,
onFilterSearch,
onRowSelectionChange,
}: OwnProps<TData, SortField, FilterProperies>,
ref: React.ForwardedRef<{ resetRowSelection: () => void } | undefined>,
) => {
const [internalRowSelection, setInternalRowSelection] = React.useState({});
const table = useReactTable<TData>({
data,
columns,
state: {
rowSelection,
rowSelection: internalRowSelection,
},
getCoreRowModel: getCoreRowModel(),
enableRowSelection: true, //enable row selection for all rows
onRowSelectionChange: setRowSelection,
enableRowSelection: true,
onRowSelectionChange: setInternalRowSelection,
});
const selectedRows = table.getSelectedRowModel().rows;
React.useEffect(() => {
const selectedRowIds = selectedRows.map((row) => row.original.id);
onRowSelectionChange && onRowSelectionChange(selectedRowIds);
}, [onRowSelectionChange, selectedRows]);
React.useImperativeHandle(ref, () => {
return {
resetRowSelection: () => {
table.resetRowSelection();
},
};
});
return (
@ -169,6 +188,6 @@ function Table<TData extends { id: string }, SortField, FilterProperies>({
</StyledTableScrollableContainer>
</StyledTableWithHeader>
);
}
};
export default Table;
export default React.forwardRef(Table);

View File

@ -0,0 +1,38 @@
import styled from '@emotion/styled';
import ActionBarButton from './ActionBarButton';
import { FaTrash } from 'react-icons/fa';
type OwnProps = {
onDeleteClick: () => void;
};
const StyledContainer = styled.div`
display: flex;
position: absolute;
z-index: 1;
height: 48px;
bottom: 38px;
background: ${(props) => props.theme.secondaryBackground};
align-items: center;
padding-left: ${(props) => props.theme.spacing(4)};
padding-right: ${(props) => props.theme.spacing(4)};
color: ${(props) => props.theme.red};
left: 50%;
border-radius: 8px;
border: 1px solid ${(props) => props.theme.primaryBorder};
`;
function ActionBar({ onDeleteClick }: OwnProps) {
return (
<StyledContainer>
<ActionBarButton
label="Delete"
icon={<FaTrash />}
onClick={onDeleteClick}
/>
</StyledContainer>
);
}
export default ActionBar;

View File

@ -0,0 +1,37 @@
import styled from '@emotion/styled';
import { ReactNode } from 'react';
type OwnProps = {
icon: ReactNode;
label: string;
onClick: () => void;
};
const StyledButton = styled.div`
display: flex;
cursor: pointer;
justify-content: center;
padding: ${(props) => props.theme.spacing(2)};
border-radius: 4px;
&:hover {
background: ${(props) => props.theme.tertiaryBackground};
}
`;
const StyledButtonabel = styled.div`
margin-left: ${(props) => props.theme.spacing(2)};
`;
function ActionBarButton({ label, icon, onClick }: OwnProps) {
return (
<StyledButton onClick={onClick}>
{icon}
<StyledButtonabel>{label}</StyledButtonabel>
</StyledButton>
);
}
export default ActionBarButton;

View File

@ -0,0 +1,30 @@
import ActionBar from '../ActionBar';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../../layout/styles/themes';
import { StoryFn } from '@storybook/react';
const component = {
title: 'ActionBar',
component: ActionBar,
};
type OwnProps = {
onDeleteClick: () => void;
};
export default component;
const Template: StoryFn<typeof ActionBar> = (args: OwnProps) => {
return (
<ThemeProvider theme={lightTheme}>
<ActionBar {...args} />
</ThemeProvider>
);
};
export const ActionBarStory = Template.bind({});
ActionBarStory.args = {
onDeleteClick: () => {
console.log('deleted');
},
};

View File

@ -0,0 +1,17 @@
import { fireEvent, render } from '@testing-library/react';
import { ActionBarStory } from '../__stories__/ActionBar.stories';
import { act } from 'react-dom/test-utils';
it('Checks the ActionBar editing event bubbles up', async () => {
const deleteFunc = jest.fn(() => null);
const { getByText } = render(<ActionBarStory onDeleteClick={deleteFunc} />);
expect(getByText('Delete')).toBeInTheDocument();
act(() => {
fireEvent.click(getByText('Delete'));
});
expect(deleteFunc).toHaveBeenCalled();
});