Filter ongoing

This commit is contained in:
Charles Bochet
2023-04-19 15:21:40 +02:00
parent ff90a3c67b
commit 5a1baf9121
13 changed files with 278 additions and 31 deletions

View File

@ -18,10 +18,12 @@ type OwnProps = {
};
const StyledTable = styled.table`
min-width: 100%;
min-width: calc(100% - ${(props) => props.theme.spacing(4)});
border-radius: 4px;
border-spacing: 0;
border-collapse: collapse;
margin-left: ${(props) => props.theme.spacing(2)};
margin-right: ${(props) => props.theme.spacing(2)};
th {
border-collapse: collapse;

View File

@ -1,13 +1,14 @@
import styled from '@emotion/styled';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useState, useRef } from 'react';
import { useOutsideAlerter } from '../../../hooks/useOutsideAlerter';
import { modalBackground } from '../../../layout/styles/themes';
import { SortType } from './SortAndFilterBar';
type OwnProps = {
label: string;
options: Array<{ label: string; icon: IconProp }>;
options: Array<SortType>;
onSortSelect?: (id: string) => void;
};
const StyledDropdownButtonContainer = styled.div`
@ -68,7 +69,7 @@ const StyledIcon = styled.div`
margin-right: ${(props) => props.theme.spacing(1)};
`;
function DropdownButton({ label, options }: OwnProps) {
function DropdownButton({ label, options, onSortSelect }: OwnProps) {
const [isUnfolded, setIsUnfolded] = useState(false);
const onButtonClick = () => {
@ -90,9 +91,17 @@ function DropdownButton({ label, options }: OwnProps) {
{isUnfolded && options.length > 0 && (
<StyledDropdown ref={dropdownRef}>
{options.map((option, index) => (
<StyledDropdownItem key={index}>
<StyledDropdownItem
key={index}
onClick={() => {
setIsUnfolded(false);
if (onSortSelect) {
onSortSelect(option.id);
}
}}
>
<StyledIcon>
<FontAwesomeIcon icon={option.icon} />
{option.icon && <FontAwesomeIcon icon={option.icon} />}
</StyledIcon>
{option.label}
</StyledDropdownItem>

View File

@ -0,0 +1,45 @@
import styled from '@emotion/styled';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import SortOrFilterChip from './SortOrFilterChip';
import { faArrowDown, faArrowUp } from '@fortawesome/pro-regular-svg-icons';
type OwnProps = {
sorts: Array<SortType>;
onRemoveSort: (sortId: string) => void;
};
export type SortType = {
label: string;
order: string;
id: string;
icon?: IconProp;
};
const StyledBar = styled.div`
display: flex;
flex-direction: row;
border-top: 1px solid ${(props) => props.theme.primaryBorder};
align-items: center;
justify-content: space-between;
height: 40px;
`;
function SortAndFilterBar({ sorts, onRemoveSort }: OwnProps) {
return (
<StyledBar>
{sorts.map((sort) => {
return (
<SortOrFilterChip
key={sort.id}
label={sort.label}
id={sort.id}
icon={sort.order === 'asc' ? faArrowDown : faArrowUp}
onRemove={() => onRemoveSort(sort.id)}
/>
);
})}
</StyledBar>
);
}
export default SortAndFilterBar;

View File

@ -0,0 +1,48 @@
import styled from '@emotion/styled';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { faTimes } from '@fortawesome/pro-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
type OwnProps = {
id: string;
label: string;
icon: IconProp;
onRemove: () => void;
};
const StyledChip = styled.div`
border-radius: 50px;
display: flex;
flex-direction: row;
background-color: ${(props) => props.theme.blueHighTransparency};
border: 1px solid ${(props) => props.theme.blueLowTransparency};
color: ${(props) => props.theme.blue};
padding: ${(props) => props.theme.spacing(1)}
${(props) => props.theme.spacing(2)};
margin-left: ${(props) => props.theme.spacing(2)};
fontsize: ${(props) => props.theme.fontSizeSmall};
`;
const StyledIcon = styled.div`
margin-right: ${(props) => props.theme.spacing(1)};
`;
const StyledDelete = styled.div`
margin-left: ${(props) => props.theme.spacing(2)};
cursor: pointer;
`;
function SortOrFilterChip({ id, label, icon, onRemove }: OwnProps) {
return (
<StyledChip>
<StyledIcon>
<FontAwesomeIcon icon={icon} />
</StyledIcon>
{label}
<StyledDelete onClick={onRemove} data-testid={'remove-icon-' + id}>
<FontAwesomeIcon icon={faTimes} />
</StyledDelete>
</StyledChip>
);
}
export default SortOrFilterChip;

View File

@ -3,13 +3,20 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import DropdownButton from './DropdownButton';
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { faCalendar } from '@fortawesome/pro-regular-svg-icons';
import SortAndFilterBar, { SortType } from './SortAndFilterBar';
import { useState } from 'react';
type OwnProps = {
viewName: string;
viewIcon?: IconProp;
};
const StyledTitle = styled.div`
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
`;
const StyledTableHeader = styled.div`
display: flex;
flex-direction: row;
align-items: center;
@ -17,7 +24,8 @@ const StyledTitle = styled.div`
height: 40px;
color: ${(props) => props.theme.text60};
font-weight: 500;
padding-left: ${(props) => props.theme.spacing(2)};
padding-left: ${(props) => props.theme.spacing(3)};
padding-right: ${(props) => props.theme.spacing(1)};
`;
const StyledIcon = styled.div`
@ -36,23 +44,53 @@ const StyledFilters = styled.div`
`;
function TableHeader({ viewName, viewIcon }: OwnProps) {
const [sorts, setSorts] = useState([] as Array<SortType>);
const onSortItemSelect = (sortId: string) => {
setSorts([
{
label: 'Created at',
order: 'asc',
id: sortId,
},
]);
};
const onSortItemUnSelect = (sortId: string) => {
setSorts([]);
};
const sortsAvailable: Array<SortType> = [
{
id: 'created_at',
label: 'Created at',
order: 'asc',
icon: faCalendar,
},
];
return (
<StyledTitle>
<StyledViewSection>
<StyledIcon>
{viewIcon && <FontAwesomeIcon icon={viewIcon} size="lg" />}
</StyledIcon>
{viewName}
</StyledViewSection>
<StyledFilters>
<DropdownButton label="Filter" options={[]} />
<DropdownButton
label="Sort"
options={[{ label: 'Created at', icon: faCalendar }]}
/>
<DropdownButton label="Settings" options={[]} />
</StyledFilters>
</StyledTitle>
<StyledContainer>
<StyledTableHeader>
<StyledViewSection>
<StyledIcon>
{viewIcon && <FontAwesomeIcon icon={viewIcon} size="lg" />}
</StyledIcon>
{viewName}
</StyledViewSection>
<StyledFilters>
<DropdownButton label="Filter" options={[]} />
<DropdownButton
label="Sort"
options={sortsAvailable}
onSortSelect={onSortItemSelect}
/>
<DropdownButton label="Settings" options={[]} />
</StyledFilters>
</StyledTableHeader>
{sorts.length > 0 && (
<SortAndFilterBar sorts={sorts} onRemoveSort={onSortItemUnSelect} />
)}
</StyledContainer>
);
}

View File

@ -0,0 +1,37 @@
import SortAndFilterBar from '../SortAndFilterBar';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../../layout/styles/themes';
import { faArrowDown } from '@fortawesome/pro-regular-svg-icons';
export default {
title: 'SortAndFilterBar',
component: SortAndFilterBar,
};
type OwnProps = {
removeFunction: () => void;
};
export const RegularSortAndFilterBar = ({ removeFunction }: OwnProps) => {
return (
<ThemeProvider theme={lightTheme}>
<SortAndFilterBar
sorts={[
{
label: 'Test sort',
order: 'asc',
id: 'test_sort',
icon: faArrowDown,
},
{
label: 'Test sort 2',
order: 'desc',
id: 'test_sort_2',
icon: faArrowDown,
},
]}
onRemoveSort={removeFunction}
/>
</ThemeProvider>
);
};

View File

@ -0,0 +1,26 @@
import SortOrFilterChip from '../SortOrFilterChip';
import { ThemeProvider } from '@emotion/react';
import { lightTheme } from '../../../../layout/styles/themes';
import { faArrowDown } from '@fortawesome/pro-regular-svg-icons';
export default {
title: 'SortOrFilterChip',
component: SortOrFilterChip,
};
type OwnProps = {
removeFunction: () => void;
};
export const RegularSortOrFilterChip = ({ removeFunction }: OwnProps) => {
return (
<ThemeProvider theme={lightTheme}>
<SortOrFilterChip
id="test_sort"
icon={faArrowDown}
label="Test sort"
onRemove={removeFunction}
/>
</ThemeProvider>
);
};

View File

@ -0,0 +1,17 @@
import { fireEvent, render } from '@testing-library/react';
import { RegularSortAndFilterBar } from '../__stories__/SortAndFilterBar.stories';
const removeFunction = jest.fn();
it('Checks the SortAndFilterBar renders', async () => {
const { getByText, getByTestId } = render(
<RegularSortAndFilterBar removeFunction={removeFunction} />,
);
expect(getByText('Test sort')).toBeDefined();
const removeIcon = getByTestId('remove-icon-test_sort');
fireEvent.click(removeIcon);
expect(removeFunction).toHaveBeenCalled();
});

View File

@ -0,0 +1,17 @@
import { fireEvent, render } from '@testing-library/react';
import { RegularSortOrFilterChip } from '../__stories__/SortOrFilterChip.stories';
const removeFunction = jest.fn();
it('Checks the RegularSortOrFilterChip renders', async () => {
const { getByText, getByTestId } = render(
<RegularSortOrFilterChip removeFunction={removeFunction} />,
);
expect(getByText('Test sort')).toBeDefined();
const removeIcon = getByTestId('remove-icon-test_sort');
fireEvent.click(removeIcon);
expect(removeFunction).toHaveBeenCalled();
});

View File

@ -1,9 +1,15 @@
import { render } from '@testing-library/react';
import { fireEvent, render } from '@testing-library/react';
import { RegularTableHeader } from '../__stories__/TableHeader.stories';
it('Checks the TableHeader renders', () => {
it('Checks the TableHeader renders', async () => {
const { getByText } = render(<RegularTableHeader />);
expect(getByText('Test')).toBeDefined();
const sortDropdownButton = getByText('Sort');
fireEvent.click(sortDropdownButton);
const sortByCreatedAt = getByText('Created at');
fireEvent.click(sortByCreatedAt);
expect(getByText('Created at')).toBeDefined();
});

View File

@ -8,8 +8,6 @@ export function useOutsideAlerter(
) {
useEffect(() => {
function handleClickOutside(event: Event) {
console.log('test3');
const target = event.target as HTMLButtonElement;
if (ref.current && !ref.current.contains(target)) {
callback();

View File

@ -40,6 +40,9 @@ const lightThemeSpecific = {
green: '#1e7e50',
purple: '#1111b7',
yellow: '#cc660a',
blueHighTransparency: 'rgba(25, 97, 237, 0.03)',
blueLowTransparency: 'rgba(25, 97, 237, 0.32)',
};
const darkThemeSpecific: typeof lightThemeSpecific = {
@ -70,6 +73,9 @@ const darkThemeSpecific: typeof lightThemeSpecific = {
green: '#e6fff2',
purple: '#e0e0ff',
yellow: '#fff2e7',
blueHighTransparency: 'rgba(104, 149, 236, 0.03)',
blueLowTransparency: 'rgba(104, 149, 236, 0.32)',
};
export const modalBackground = (props: any) =>

View File

@ -36,8 +36,6 @@ type Person = {
const StyledPeopleContainer = styled.div`
display: flex;
padding-left: ${(props) => props.theme.spacing(2)};
padding-right: ${(props) => props.theme.spacing(2)};
width: 100%;
a {