diff --git a/front/src/components/table/Table.tsx b/front/src/components/table/Table.tsx index 6652c2ad0..2926b159f 100644 --- a/front/src/components/table/Table.tsx +++ b/front/src/components/table/Table.tsx @@ -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; diff --git a/front/src/components/table/table-header/DropdownButton.tsx b/front/src/components/table/table-header/DropdownButton.tsx index 6aac67046..2c1b85d52 100644 --- a/front/src/components/table/table-header/DropdownButton.tsx +++ b/front/src/components/table/table-header/DropdownButton.tsx @@ -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; + 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 && ( {options.map((option, index) => ( - + { + setIsUnfolded(false); + if (onSortSelect) { + onSortSelect(option.id); + } + }} + > - + {option.icon && } {option.label} diff --git a/front/src/components/table/table-header/SortAndFilterBar.tsx b/front/src/components/table/table-header/SortAndFilterBar.tsx new file mode 100644 index 000000000..87c7abfc3 --- /dev/null +++ b/front/src/components/table/table-header/SortAndFilterBar.tsx @@ -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; + 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 ( + + {sorts.map((sort) => { + return ( + onRemoveSort(sort.id)} + /> + ); + })} + + ); +} + +export default SortAndFilterBar; diff --git a/front/src/components/table/table-header/SortOrFilterChip.tsx b/front/src/components/table/table-header/SortOrFilterChip.tsx new file mode 100644 index 000000000..8bdef75c3 --- /dev/null +++ b/front/src/components/table/table-header/SortOrFilterChip.tsx @@ -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 ( + + + + + {label} + + + + + ); +} + +export default SortOrFilterChip; diff --git a/front/src/components/table/table-header/TableHeader.tsx b/front/src/components/table/table-header/TableHeader.tsx index ac1f3c5f6..a3c861ce1 100644 --- a/front/src/components/table/table-header/TableHeader.tsx +++ b/front/src/components/table/table-header/TableHeader.tsx @@ -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); + const onSortItemSelect = (sortId: string) => { + setSorts([ + { + label: 'Created at', + order: 'asc', + id: sortId, + }, + ]); + }; + + const onSortItemUnSelect = (sortId: string) => { + setSorts([]); + }; + + const sortsAvailable: Array = [ + { + id: 'created_at', + label: 'Created at', + order: 'asc', + icon: faCalendar, + }, + ]; + return ( - - - - {viewIcon && } - - {viewName} - - - - - - - + + + + + {viewIcon && } + + {viewName} + + + + + + + + {sorts.length > 0 && ( + + )} + ); } diff --git a/front/src/components/table/table-header/__stories__/SortAndFilterBar.stories.tsx b/front/src/components/table/table-header/__stories__/SortAndFilterBar.stories.tsx new file mode 100644 index 000000000..a8e84829e --- /dev/null +++ b/front/src/components/table/table-header/__stories__/SortAndFilterBar.stories.tsx @@ -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 ( + + + + ); +}; diff --git a/front/src/components/table/table-header/__stories__/SortOrFilterChip.stories.tsx b/front/src/components/table/table-header/__stories__/SortOrFilterChip.stories.tsx new file mode 100644 index 000000000..4f1ec8002 --- /dev/null +++ b/front/src/components/table/table-header/__stories__/SortOrFilterChip.stories.tsx @@ -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 ( + + + + ); +}; diff --git a/front/src/components/table/table-header/__tests__/SortAndFilterBar.test.tsx b/front/src/components/table/table-header/__tests__/SortAndFilterBar.test.tsx new file mode 100644 index 000000000..89b34679f --- /dev/null +++ b/front/src/components/table/table-header/__tests__/SortAndFilterBar.test.tsx @@ -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( + , + ); + expect(getByText('Test sort')).toBeDefined(); + + const removeIcon = getByTestId('remove-icon-test_sort'); + fireEvent.click(removeIcon); + + expect(removeFunction).toHaveBeenCalled(); +}); diff --git a/front/src/components/table/table-header/__tests__/SortOrFilterChip.test.tsx b/front/src/components/table/table-header/__tests__/SortOrFilterChip.test.tsx new file mode 100644 index 000000000..18fc1bd03 --- /dev/null +++ b/front/src/components/table/table-header/__tests__/SortOrFilterChip.test.tsx @@ -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( + , + ); + expect(getByText('Test sort')).toBeDefined(); + + const removeIcon = getByTestId('remove-icon-test_sort'); + fireEvent.click(removeIcon); + + expect(removeFunction).toHaveBeenCalled(); +}); diff --git a/front/src/components/table/table-header/__tests__/TableHeader.test.tsx b/front/src/components/table/table-header/__tests__/TableHeader.test.tsx index d3d7fe83b..448dce4fa 100644 --- a/front/src/components/table/table-header/__tests__/TableHeader.test.tsx +++ b/front/src/components/table/table-header/__tests__/TableHeader.test.tsx @@ -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(); - expect(getByText('Test')).toBeDefined(); + const sortDropdownButton = getByText('Sort'); + fireEvent.click(sortDropdownButton); + + const sortByCreatedAt = getByText('Created at'); + fireEvent.click(sortByCreatedAt); + + expect(getByText('Created at')).toBeDefined(); }); diff --git a/front/src/hooks/useOutsideAlerter.ts b/front/src/hooks/useOutsideAlerter.ts index 746c44afb..742b12337 100644 --- a/front/src/hooks/useOutsideAlerter.ts +++ b/front/src/hooks/useOutsideAlerter.ts @@ -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(); diff --git a/front/src/layout/styles/themes.ts b/front/src/layout/styles/themes.ts index ed6a692a1..1f3af15be 100644 --- a/front/src/layout/styles/themes.ts +++ b/front/src/layout/styles/themes.ts @@ -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) => diff --git a/front/src/pages/people/People.tsx b/front/src/pages/people/People.tsx index 5b6e793c4..368da4812 100644 --- a/front/src/pages/people/People.tsx +++ b/front/src/pages/people/People.tsx @@ -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 {