From c073908027bdeff9704e1d76ade774ea5fb738f4 Mon Sep 17 00:00:00 2001 From: Fredrik Strand Oseberg Date: Wed, 25 May 2022 11:12:53 +0200 Subject: [PATCH] feat: new tag types table (#1014) * feat: new tag types table * fix: update loader text * fix: update header * fix: regenerator runtime * fix: update snapshot --- .../AddTagTypeButton/AddTagTypeButton.tsx | 36 + .../tags/TagTypeList/TagTypeList.module.scss | 50 - .../tags/TagTypeList/TagTypeList.tsx | 307 +++-- .../__snapshots__/TagTypeList.test.tsx.snap | 1012 ++++++++++++++++- frontend/src/setupTests.ts | 1 + 5 files changed, 1235 insertions(+), 171 deletions(-) create mode 100644 frontend/src/component/tags/TagTypeList/AddTagTypeButton/AddTagTypeButton.tsx delete mode 100644 frontend/src/component/tags/TagTypeList/TagTypeList.module.scss diff --git a/frontend/src/component/tags/TagTypeList/AddTagTypeButton/AddTagTypeButton.tsx b/frontend/src/component/tags/TagTypeList/AddTagTypeButton/AddTagTypeButton.tsx new file mode 100644 index 0000000000..8355413026 --- /dev/null +++ b/frontend/src/component/tags/TagTypeList/AddTagTypeButton/AddTagTypeButton.tsx @@ -0,0 +1,36 @@ +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import PermissionButton from 'component/common/PermissionButton/PermissionButton'; +import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; +import { UPDATE_TAG_TYPE } from 'component/providers/AccessProvider/permissions'; +import useMediaQuery from '@mui/material/useMediaQuery'; +import { useNavigate } from 'react-router-dom'; + +import { Add } from '@mui/icons-material'; + +export const AddTagTypeButton = () => { + const navigate = useNavigate(); + const smallScreen = useMediaQuery('(max-width:700px)'); + + return ( + navigate('/tag-types/create')} + size="large" + permission={UPDATE_TAG_TYPE} + > + + + } + elseShow={ + navigate('/tag-types/create')} + > + New tag type + + } + /> + ); +}; diff --git a/frontend/src/component/tags/TagTypeList/TagTypeList.module.scss b/frontend/src/component/tags/TagTypeList/TagTypeList.module.scss deleted file mode 100644 index d24001c135..0000000000 --- a/frontend/src/component/tags/TagTypeList/TagTypeList.module.scss +++ /dev/null @@ -1,50 +0,0 @@ -.select { - min-width: 100px; -} - -.icon { - fill: #757575; -} - -.textfield { - margin-left: 15px; -} - -.header { - padding: var(--card-header-padding); - word-break: break-all; - border-bottom: var(--default-border); - display: flex; - align-items: center; - justify-content: space-between; -} - -.header h1 { - font-size: var(--h1-size); -} - -.container { - padding: var(--card-padding); -} - -.formButtons { - padding-top: 1rem; -} - -.tagListItem { - padding: 0; -} - -.tagListItem * > a { - text-decoration: none; - color: inherit; -} - -.tagTypeContainer { - max-width: 350px; -} - -.addTagTypeForm { - display: flex; - flex-direction: column; -} diff --git a/frontend/src/component/tags/TagTypeList/TagTypeList.tsx b/frontend/src/component/tags/TagTypeList/TagTypeList.tsx index bb1169c495..a8590c8148 100644 --- a/frontend/src/component/tags/TagTypeList/TagTypeList.tsx +++ b/frontend/src/component/tags/TagTypeList/TagTypeList.tsx @@ -1,15 +1,16 @@ -import { useContext, useState } from 'react'; -import { Link, useNavigate } from 'react-router-dom'; +import { useState, useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Box } from '@mui/material'; import { - Button, - IconButton, - List, - ListItem, - ListItemIcon, - ListItemText, - Tooltip, -} from '@mui/material'; -import { Add, Delete, Edit, Label } from '@mui/icons-material'; + Table, + SortableTableHeader, + TableBody, + TableCell, + TableRow, + TablePlaceholder, + TableSearch, +} from 'component/common/Table'; +import { Delete, Edit, Label } from '@mui/icons-material'; import { PageHeader } from 'component/common/PageHeader/PageHeader'; import { PageContent } from 'component/common/PageContent/PageContent'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; @@ -18,28 +19,152 @@ import { UPDATE_TAG_TYPE, } from 'component/providers/AccessProvider/permissions'; import { Dialogue } from 'component/common/Dialogue/Dialogue'; -import useMediaQuery from '@mui/material/useMediaQuery'; -import styles from './TagTypeList.module.scss'; -import AccessContext from 'contexts/AccessContext'; import useTagTypesApi from 'hooks/api/actions/useTagTypesApi/useTagTypesApi'; import useTagTypes from 'hooks/api/getters/useTagTypes/useTagTypes'; import useToast from 'hooks/useToast'; import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton'; import { formatUnknownError } from 'utils/formatUnknownError'; -import { ITagType } from 'interfaces/tags'; +import { useTable, useGlobalFilter, useSortBy } from 'react-table'; +import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext'; +import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell'; +import { sortTypes } from 'utils/sortTypes'; +import { AddTagTypeButton } from './AddTagTypeButton/AddTagTypeButton'; export const TagTypeList = () => { - const { hasAccess } = useContext(AccessContext); const [deletion, setDeletion] = useState<{ open: boolean; name?: string; }>({ open: false }); const navigate = useNavigate(); - const smallScreen = useMediaQuery('(max-width:700px)'); const { deleteTagType } = useTagTypesApi(); - const { tagTypes, refetch } = useTagTypes(); + const { tagTypes, refetch, loading } = useTagTypes(); const { setToastData, setToastApiError } = useToast(); + const data = useMemo(() => { + if (loading) { + return Array(5).fill({ + name: 'Tag type name', + description: 'Tag type description when loading', + }); + } + + return tagTypes.map(({ name, description }) => ({ + name, + description, + })); + }, [tagTypes, loading]); + + const columns = useMemo( + () => [ + { + id: 'Icon', + Cell: () => ( + + + ), + }, + { + Header: 'Name', + accessor: 'name', + width: '90%', + Cell: ({ + row: { + original: { name, description }, + }, + }: any) => { + return ( + + ); + }, + sortType: 'alphanumeric', + }, + { + Header: 'Actions', + id: 'Actions', + align: 'center', + Cell: ({ row: { original } }: any) => ( + + + navigate(`/tag-types/edit/${original.name}`) + } + permission={UPDATE_TAG_TYPE} + tooltipProps={{ title: 'Edit tag type' }} + > + + + + + setDeletion({ + open: true, + name: original.name, + }) + } + > + + + + ), + width: 150, + disableSortBy: true, + }, + { + accessor: 'description', + disableSortBy: true, + }, + ], + [] + ); + + const initialState = useMemo( + () => ({ + sortBy: [{ id: 'name', desc: false }], + hiddenColumns: ['description'], + }), + [] + ); + + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + state: { globalFilter }, + setGlobalFilter, + } = useTable( + { + columns: columns as any[], // TODO: fix after `react-table` v8 update + data, + initialState, + sortTypes, + autoResetGlobalFilter: false, + autoResetSortBy: false, + disableSortRemove: true, + }, + useGlobalFilter, + useSortBy + ); + const deleteTag = async () => { try { if (deletion.name) { @@ -57,100 +182,64 @@ export const TagTypeList = () => { } }; - let header = ( - - - navigate('/tag-types/create') - } - size="large" - > - - - - } - elseShow={ - - } - /> + return ( + + + + + } /> } - /> - ); - - const renderTagType = (tagType: ITagType) => { - let link = ( - - {tagType.name} - - ); - let deleteButton = ( - - - setDeletion({ - open: true, - name: tagType.name, - }) - } - size="large" - > - - - - ); - - return ( - - - - - - - - - - ); - }; - return ( - - - 0} - show={tagTypes.map(tagType => renderTagType(tagType))} - elseShow={No entries} - /> - + > + + + + + {rows.map(row => { + prepareRow(row); + return ( + + {row.cells.map(cell => ( + + {cell.render('Cell')} + + ))} + + ); + })} + +
+
+ 0} + show={ + + No tags found matching “ + {globalFilter} + ” + + } + elseShow={ + + No tags available. Get started by adding one. + + } + /> + } + /> - Tag Types + Tag types
+
+ + +
@@ -56,16 +108,950 @@ exports[`renders an empty list correctly 1`] = `
-
    -
  • - No entries -
  • -
+ + +
+   +
+ + + + + +
+ Actions +
+ + + + + + +
+ + + +
+ + + +
+ + Tag type name + + + Tag type description when loading + +
+
+ + +
+
+ +
+
+ +
+
+ + + + +
+ + + +
+ + + +
+ + Tag type name + + + Tag type description when loading + +
+
+ + +
+
+ +
+
+ +
+
+ + + + +
+ + + +
+ + + +
+ + Tag type name + + + Tag type description when loading + +
+
+ + +
+
+ +
+
+ +
+
+ + + + +
+ + + +
+ + + +
+ + Tag type name + + + Tag type description when loading + +
+
+ + +
+
+ +
+
+ +
+
+ + + + +
+ + + +
+ + + +
+ + Tag type name + + + Tag type description when loading + +
+
+ + +
+
+ +
+
+ +
+
+ + + +
, @@ -75,6 +1061,8 @@ exports[`renders an empty list correctly 1`] = ` className="tss-i8rqz1-container" data-testid="ANNOUNCER_ELEMENT_TEST_ID" role="status" - />, + > + Navigated to Tag types + , ] `; diff --git a/frontend/src/setupTests.ts b/frontend/src/setupTests.ts index 0f0f0ee439..d15f1080f9 100644 --- a/frontend/src/setupTests.ts +++ b/frontend/src/setupTests.ts @@ -1,4 +1,5 @@ import '@testing-library/jest-dom'; import 'whatwg-fetch'; +import 'regenerator-runtime'; process.env.TZ = 'UTC';