From abcf9bfc809451db685cd3a9bee781d9ac70cb7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 12 Sep 2024 14:57:41 -0300 Subject: [PATCH 01/42] feat(design system): new Offcanvas component --- dev-env/docker-compose-dev.yml | 2 - packages/design-system/CHANGELOG.md | 1 + .../assets/styles/bootstrap-customized.scss | 75 ++++++++++--------- .../lib/components/offcanvas/Offcanvas.tsx | 41 ++++++++++ .../components/offcanvas/OffcanvasBody.tsx | 9 +++ .../components/offcanvas/OffcanvasHeader.tsx | 19 +++++ .../components/offcanvas/OffcanvasTitle.tsx | 9 +++ packages/design-system/src/lib/index.ts | 1 + .../stories/offcanvas/Offcanvas.stories.tsx | 72 ++++++++++++++++++ 9 files changed, 191 insertions(+), 38 deletions(-) create mode 100644 packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx create mode 100644 packages/design-system/src/lib/components/offcanvas/OffcanvasBody.tsx create mode 100644 packages/design-system/src/lib/components/offcanvas/OffcanvasHeader.tsx create mode 100644 packages/design-system/src/lib/components/offcanvas/OffcanvasTitle.tsx create mode 100644 packages/design-system/src/lib/stories/offcanvas/Offcanvas.stories.tsx diff --git a/dev-env/docker-compose-dev.yml b/dev-env/docker-compose-dev.yml index 94b83bae0..cdefa5ea0 100644 --- a/dev-env/docker-compose-dev.yml +++ b/dev-env/docker-compose-dev.yml @@ -1,5 +1,3 @@ -version: '2.4' - services: dev_nginx: container_name: 'dev_nginx_proxy' diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index 992bef245..77383434c 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -48,6 +48,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **Stack:** NEW Stack element to manage layouts. - **TransferList:** NEW TransferList component to transfer items between two list, also sortable. - **Table:** extend Props Interface to accept `bordered` prop to add or remove borders on all sides of the table and cells. Defaults to true. +- **Offcanvas:** NEW Offcanvas component. # [1.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@1.0.1...@iqss/dataverse-design-system@1.1.0) (2024-03-12) diff --git a/packages/design-system/src/lib/assets/styles/bootstrap-customized.scss b/packages/design-system/src/lib/assets/styles/bootstrap-customized.scss index 117811b22..6ceaa59ee 100644 --- a/packages/design-system/src/lib/assets/styles/bootstrap-customized.scss +++ b/packages/design-system/src/lib/assets/styles/bootstrap-customized.scss @@ -1,5 +1,5 @@ -@import "design-tokens/typography.module"; -@import "design-tokens/colors.module"; +@import 'design-tokens/typography.module'; +@import 'design-tokens/colors.module'; // Theme @@ -10,9 +10,9 @@ $info: $dv-info-color; $warning: $dv-warning-color; $danger: $dv-danger-color; -@import "bootstrap/scss/functions"; -@import "bootstrap/scss/variables"; -@import "bootstrap/scss/mixins"; +@import 'bootstrap/scss/functions'; +@import 'bootstrap/scss/variables'; +@import 'bootstrap/scss/mixins'; // Body $body-color: $dv-text-color; @@ -36,73 +36,74 @@ $link-color: $dv-link-color; $link-hover-color: $dv-link-hover-color; // Base -@import "bootstrap/scss/maps"; -@import "bootstrap/scss/root"; -@import "bootstrap/scss/reboot"; -@import "bootstrap/scss/type"; +@import 'bootstrap/scss/maps'; +@import 'bootstrap/scss/root'; +@import 'bootstrap/scss/reboot'; +@import 'bootstrap/scss/type'; // Buttons and Dropdowns -@import "bootstrap/scss/buttons"; +@import 'bootstrap/scss/buttons'; @import 'bootstrap/scss/button-group'; -@import "bootstrap/scss/dropdown"; +@import 'bootstrap/scss/dropdown'; // Grid -@import "bootstrap/scss/utilities"; -@import "bootstrap/scss/utilities/api"; -@import "bootstrap/scss/containers"; -@import "bootstrap/scss/grid"; +@import 'bootstrap/scss/utilities'; +@import 'bootstrap/scss/utilities/api'; +@import 'bootstrap/scss/containers'; +@import 'bootstrap/scss/grid'; // Badge -@import "bootstrap/scss/badge"; +@import 'bootstrap/scss/badge'; // Forms $form-label-font-weight: $dv-font-weight-bold; -@import "bootstrap/scss/forms"; - +@import 'bootstrap/scss/forms'; // Table -@import "bootstrap/scss/tables"; - +@import 'bootstrap/scss/tables'; // Accordion -@import "bootstrap/scss/accordion"; +@import 'bootstrap/scss/accordion'; // Modal -@import "bootstrap/scss/modal"; -@import "bootstrap/scss/close"; +@import 'bootstrap/scss/modal'; +@import 'bootstrap/scss/close'; // Breadcrumb -$breadcrumb-divider: ">"; +$breadcrumb-divider: '>'; -@import "bootstrap/scss/breadcrumb"; +@import 'bootstrap/scss/breadcrumb'; // QuestionMarkTooltip $tooltip-max-width: 500px; -@import "bootstrap/scss/tooltip"; +@import 'bootstrap/scss/tooltip'; // Alert -@import "bootstrap/scss/alert"; +@import 'bootstrap/scss/alert'; // Pagination -@import "bootstrap/scss/pagination"; +@import 'bootstrap/scss/pagination'; // Card -@import "bootstrap/scss/card"; +@import 'bootstrap/scss/card'; // Progress -@import "bootstrap/scss/progress"; +@import 'bootstrap/scss/progress'; + +// OffCanvas +@import 'bootstrap/scss/offcanvas'; // Navbar $navbar-light-brand-color: $dv-brand-color; $navbar-brand-font-size: $dv-brand-font-size; -@import "bootstrap/scss/nav"; -@import "bootstrap/scss/navbar"; -@import "bootstrap/scss/transitions"; -@import "bootstrap/scss/helpers"; +@import 'bootstrap/scss/nav'; +@import 'bootstrap/scss/navbar'; +@import 'bootstrap/scss/transitions'; +@import 'bootstrap/scss/helpers'; .navbar-collapse { justify-content: end; @@ -124,12 +125,14 @@ th { vertical-align: middle; } -.btn-group > div:not(:last-child) > .btn-group > .btn, .btn-group-vertical >div:not(:last-child) > .btn-group > .btn { +.btn-group > div:not(:last-child) > .btn-group > .btn, +.btn-group-vertical > div:not(:last-child) > .btn-group > .btn { border-top-right-radius: 0; border-bottom-right-radius: 0; } -.btn-group > div:not(:first-child) > .btn-group > .btn, .btn-group-vertical > div:not(:first-child) > .btn-group > .btn { +.btn-group > div:not(:first-child) > .btn-group > .btn, +.btn-group-vertical > div:not(:first-child) > .btn-group > .btn { border-top-left-radius: 0; border-bottom-left-radius: 0; } diff --git a/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx b/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx new file mode 100644 index 000000000..5454be337 --- /dev/null +++ b/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx @@ -0,0 +1,41 @@ +import { Offcanvas as OffcanvasBS } from 'react-bootstrap' +import { OffcanvasHeader } from './OffcanvasHeader' +import { OffcanvasTitle } from './OffcanvasTitle' +import { OffcanvasBody } from './OffcanvasBody' + +// https://react-bootstrap.netlify.app/docs/components/offcanvas + +interface OffcanvasProps { + show: boolean + placement?: 'start' | 'end' | 'top' | 'bottom' + responsive?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl' + onShow?: () => void + onHide?: () => void + children: React.ReactNode +} + +const Offcanvas = ({ + show = false, + placement = 'start', + responsive, + onHide, + onShow, + children +}: OffcanvasProps) => { + return ( + + {children} + + ) +} + +Offcanvas.Header = OffcanvasHeader +Offcanvas.Title = OffcanvasTitle +Offcanvas.Body = OffcanvasBody + +export { Offcanvas } diff --git a/packages/design-system/src/lib/components/offcanvas/OffcanvasBody.tsx b/packages/design-system/src/lib/components/offcanvas/OffcanvasBody.tsx new file mode 100644 index 000000000..ff5353c11 --- /dev/null +++ b/packages/design-system/src/lib/components/offcanvas/OffcanvasBody.tsx @@ -0,0 +1,9 @@ +import { OffcanvasBody as OffcanvasBodyBS } from 'react-bootstrap' + +export interface OffcanvasBodyProps { + children: React.ReactNode +} + +export const OffcanvasBody = ({ children }: OffcanvasBodyProps) => { + return {children} +} diff --git a/packages/design-system/src/lib/components/offcanvas/OffcanvasHeader.tsx b/packages/design-system/src/lib/components/offcanvas/OffcanvasHeader.tsx new file mode 100644 index 000000000..20b753935 --- /dev/null +++ b/packages/design-system/src/lib/components/offcanvas/OffcanvasHeader.tsx @@ -0,0 +1,19 @@ +import { OffcanvasHeader as OffcanvasHeaderBS } from 'react-bootstrap' + +export interface OffcanvasHeaderProps { + closeLabel?: string + closeButton?: boolean + children: React.ReactNode +} + +export const OffcanvasHeader = ({ + closeLabel = 'Close', + closeButton = true, + children +}: OffcanvasHeaderProps) => { + return ( + + {children} + + ) +} diff --git a/packages/design-system/src/lib/components/offcanvas/OffcanvasTitle.tsx b/packages/design-system/src/lib/components/offcanvas/OffcanvasTitle.tsx new file mode 100644 index 000000000..662bc9c92 --- /dev/null +++ b/packages/design-system/src/lib/components/offcanvas/OffcanvasTitle.tsx @@ -0,0 +1,9 @@ +import { OffcanvasTitle as OffcanvasTitleBS } from 'react-bootstrap' + +export interface OffcanvasTitleProps { + children: React.ReactNode +} + +export const OffcanvasTitle = ({ children }: OffcanvasTitleProps) => { + return {children} +} diff --git a/packages/design-system/src/lib/index.ts b/packages/design-system/src/lib/index.ts index 5db023f6b..c8ebc6f28 100644 --- a/packages/design-system/src/lib/index.ts +++ b/packages/design-system/src/lib/index.ts @@ -29,3 +29,4 @@ export { Card } from './components/card/Card' export { ProgressBar } from './components/progress-bar/ProgressBar' export { Stack } from './components/stack/Stack' export { TransferList, type TransferListItem } from './components/transfer-list/TransferList' +export { Offcanvas } from './components/offcanvas/Offcanvas' diff --git a/packages/design-system/src/lib/stories/offcanvas/Offcanvas.stories.tsx b/packages/design-system/src/lib/stories/offcanvas/Offcanvas.stories.tsx new file mode 100644 index 000000000..3ab3ec7b8 --- /dev/null +++ b/packages/design-system/src/lib/stories/offcanvas/Offcanvas.stories.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react' +import type { Meta, StoryObj } from '@storybook/react' +import { Offcanvas } from '../../components/offcanvas/Offcanvas' +import { Button } from '../../components/button/Button' + +const meta: Meta = { + title: 'Offcanvas', + component: Offcanvas, + tags: ['autodocs'] +} + +export default meta +type Story = StoryObj + +const OffcanvasWithTrigger = ({ + placement = 'start', + responsive +}: { + placement?: 'start' | 'end' | 'top' | 'bottom' + responsive?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl' +}) => { + const [show, setShow] = useState(responsive ? true : false) + + const handleClose = () => setShow(false) + const handleShow = () => setShow(true) + + return ( +
+ {!responsive && } + + + + Offcanvas Title + + + {responsive ? ( +
+

Resize your browser to show the responsive offcanvas toggle.

+

+ Responsive offcanvas classes hide content outside the viewport from a specified + breakpoint and down. Above that breakpoint, the contents within will behave as + usual. +

+
+ ) : ( +

All the content goes here

+ )} +
+
+
+ ) +} + +export const Default: Story = { + render: () => +} + +export const TopPlacement: Story = { + render: () => +} + +export const EndPlacement: Story = { + render: () => +} + +export const BottomPlacement: Story = { + render: () => +} + +export const Responsive: Story = { + render: () => +} From e18cf47a86afe108e1e2f6fc44dd38d3cd210ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 12 Sep 2024 16:10:12 -0300 Subject: [PATCH 02/42] feat: initial layout --- packages/design-system/CHANGELOG.md | 2 +- .../form-input-group/FormInputGroup.tsx | 5 +- src/sections/Route.enum.ts | 3 +- .../collection/Collection.module.scss | 10 +--- src/sections/collection/Collection.tsx | 27 ++++++----- .../items-panel/ItemsPanel.module.scss | 34 +++++++++++++ .../collection/items-panel/ItemsPanel.tsx | 25 ++++++++++ .../filter-panel/FilterPanel.module.scss | 9 ++++ .../items-panel/filter-panel/FilterPanel.tsx | 30 ++++++++++++ .../items-list/ItemsList.module.scss | 3 ++ .../items-panel/items-list/ItemsList.tsx | 9 ++++ .../search-panel/SearchPanel.module.scss | 26 ++++++++++ .../items-panel/search-panel/SearchPanel.tsx | 48 +++++++++++++++++++ 13 files changed, 207 insertions(+), 24 deletions(-) create mode 100644 src/sections/collection/items-panel/ItemsPanel.module.scss create mode 100644 src/sections/collection/items-panel/ItemsPanel.tsx create mode 100644 src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss create mode 100644 src/sections/collection/items-panel/filter-panel/FilterPanel.tsx create mode 100644 src/sections/collection/items-panel/items-list/ItemsList.module.scss create mode 100644 src/sections/collection/items-panel/items-list/ItemsList.tsx create mode 100644 src/sections/collection/items-panel/search-panel/SearchPanel.module.scss create mode 100644 src/sections/collection/items-panel/search-panel/SearchPanel.tsx diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index 77383434c..e5323c591 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -32,7 +32,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **FormSelect:** remove withinMultipleFieldsGroup prop. - **FormText:** remove withinMultipleFieldsGroup prop. - **FormTextArea:** remove withinMultipleFieldsGroup prop. -- **FormInputGroup:** remove hasVisibleLabel prop. +- **FormInputGroup:** remove hasVisibleLabel prop and accepts className prop. - **FormInputGroupText:** refactor type. - **Card:** NEW card element to show header and body. - **ProgressBar:** NEW progress bar element to show progress. diff --git a/packages/design-system/src/lib/components/form/form-group/form-input-group/FormInputGroup.tsx b/packages/design-system/src/lib/components/form/form-group/form-input-group/FormInputGroup.tsx index 9694502f0..dceb62024 100644 --- a/packages/design-system/src/lib/components/form/form-group/form-input-group/FormInputGroup.tsx +++ b/packages/design-system/src/lib/components/form/form-group/form-input-group/FormInputGroup.tsx @@ -5,11 +5,12 @@ import { FormInputGroupText } from './FormInputGroupText' interface FormInputGroupProps { children: ReactNode hasValidation?: boolean + className?: string } -function FormInputGroup({ children, hasValidation }: FormInputGroupProps) { +function FormInputGroup({ children, hasValidation, className = '' }: FormInputGroupProps) { return ( - + {children} ) diff --git a/src/sections/Route.enum.ts b/src/sections/Route.enum.ts index ee104c035..93626237f 100644 --- a/src/sections/Route.enum.ts +++ b/src/sections/Route.enum.ts @@ -22,5 +22,6 @@ export const RouteWithParams = { export enum QueryParamKey { VERSION = 'version', - PERSISTENT_ID = 'persistentId' + PERSISTENT_ID = 'persistentId', + QUERY = 'q' } diff --git a/src/sections/collection/Collection.module.scss b/src/sections/collection/Collection.module.scss index 0b2d0fc18..25af1b00f 100644 --- a/src/sections/collection/Collection.module.scss +++ b/src/sections/collection/Collection.module.scss @@ -1,12 +1,6 @@ @import 'node_modules/bootstrap/scss/functions'; @import 'node_modules/bootstrap/scss/variables'; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; - -.container { - display: flex; - justify-content: flex-end; - margin-bottom: $spacer; -} +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; .header { margin-bottom: $spacer; @@ -19,4 +13,4 @@ .subtext { color: $dv-subtext-color; -} \ No newline at end of file +} diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 8a0e34aed..14b2feea4 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -15,6 +15,7 @@ import { CollectionInfo } from './CollectionInfo' import { Trans, useTranslation } from 'react-i18next' import { useScrollTop } from '../../shared/hooks/useScrollTop' import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions' +import { ItemsPanel } from './items-panel/ItemsPanel' interface CollectionProps { repository: CollectionRepository @@ -44,7 +45,7 @@ export function Collection({ const canUserAddCollection = Boolean(collectionUserPermissions?.canAddCollection) const canUserAddDataset = Boolean(collectionUserPermissions?.canAddDataset) - const showAddDataActions = user && (canUserAddCollection || canUserAddDataset) + const showAddDataActions = Boolean(user && (canUserAddCollection || canUserAddDataset)) const { t } = useTranslation('collection') @@ -78,18 +79,20 @@ export function Collection({ /> )} - {showAddDataActions && ( -
- -
- )} )} - {infiniteScrollEnabled ? ( + + ) : null + } + /> + {/* {infiniteScrollEnabled ? ( - )} + )} */} ) diff --git a/src/sections/collection/items-panel/ItemsPanel.module.scss b/src/sections/collection/items-panel/ItemsPanel.module.scss new file mode 100644 index 000000000..ca7dc14e1 --- /dev/null +++ b/src/sections/collection/items-panel/ItemsPanel.module.scss @@ -0,0 +1,34 @@ +.items-panel { + display: flex; + flex-direction: column; + gap: 1rem; + border: solid 1px red; +} + +.top-wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; + + @media (min-width: 768px) { + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + .add-data-slot { + align-self: flex-end; + } +} + +.bottom-wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; + + // TODO:ME Add media queries in variables + @media (min-width: 992px) { + display: grid; + grid-template-columns: 1fr 3fr; + } +} diff --git a/src/sections/collection/items-panel/ItemsPanel.tsx b/src/sections/collection/items-panel/ItemsPanel.tsx new file mode 100644 index 000000000..20404405c --- /dev/null +++ b/src/sections/collection/items-panel/ItemsPanel.tsx @@ -0,0 +1,25 @@ +import { FilterPanel } from './filter-panel/FilterPanel' +import { ItemsList } from './items-list/ItemsList' +import { SearchPanel } from './search-panel/SearchPanel' +import styles from './ItemsPanel.module.scss' + +interface ItemsPanelProps { + addDataSlot: JSX.Element | null +} + +export const ItemsPanel = ({ addDataSlot }: ItemsPanelProps) => { + return ( +
+
+ +
{addDataSlot}
+
+ +
+ + + +
+
+ ) +} diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss b/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss new file mode 100644 index 000000000..03b8acc71 --- /dev/null +++ b/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss @@ -0,0 +1,9 @@ +.filter-panel { + .toggle-canvas-btn { + display: block; + + @media (min-width: 992px) { + display: none; + } + } +} diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx b/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx new file mode 100644 index 000000000..9a5558da9 --- /dev/null +++ b/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx @@ -0,0 +1,30 @@ +import { useState } from 'react' +import { Button, Offcanvas } from '@iqss/dataverse-design-system' +import { FunnelFill } from 'react-bootstrap-icons' +import styles from './FilterPanel.module.scss' + +export const FilterPanel = () => { + const [show, setShow] = useState(false) + + const handleClose = () => setShow(false) + const handleShow = () => setShow(true) + + return ( +
+ + + + + Filter Results + + All the content goes here + +
+ ) +} diff --git a/src/sections/collection/items-panel/items-list/ItemsList.module.scss b/src/sections/collection/items-panel/items-list/ItemsList.module.scss new file mode 100644 index 000000000..861a22b9c --- /dev/null +++ b/src/sections/collection/items-panel/items-list/ItemsList.module.scss @@ -0,0 +1,3 @@ +.items-list { + border: solid 1px orange; +} diff --git a/src/sections/collection/items-panel/items-list/ItemsList.tsx b/src/sections/collection/items-panel/items-list/ItemsList.tsx new file mode 100644 index 000000000..9f5c89bf7 --- /dev/null +++ b/src/sections/collection/items-panel/items-list/ItemsList.tsx @@ -0,0 +1,9 @@ +import styles from './ItemsList.module.scss' + +export const ItemsList = () => { + return ( +
+

Items List

+
+ ) +} diff --git a/src/sections/collection/items-panel/search-panel/SearchPanel.module.scss b/src/sections/collection/items-panel/search-panel/SearchPanel.module.scss new file mode 100644 index 000000000..c246aa601 --- /dev/null +++ b/src/sections/collection/items-panel/search-panel/SearchPanel.module.scss @@ -0,0 +1,26 @@ +.search-panel { + display: flex; + flex-direction: column; + + @media (min-width: 768px) { + flex-direction: row; + gap: 0.5rem; + align-items: center; + justify-content: space-between; + } +} + +.search-form { + @media (min-width: 768px) { + min-width: 325px; + } +} + +.search-input-group { + margin-bottom: 0 !important; +} + +.advanced-search-btn { + width: fit-content; + min-width: fit-content; +} diff --git a/src/sections/collection/items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/items-panel/search-panel/SearchPanel.tsx new file mode 100644 index 000000000..5d3d8861b --- /dev/null +++ b/src/sections/collection/items-panel/search-panel/SearchPanel.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react' +import { Button, Form } from '@iqss/dataverse-design-system' +import { Search } from 'react-bootstrap-icons' +import { QueryParamKey, Route } from '../../../Route.enum' +import styles from './SearchPanel.module.scss' + +export const SearchPanel = () => { + const [searchValue, setSearchValue] = useState('') + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault() + + const trimmedSearchValue = searchValue.trim() + + if (!trimmedSearchValue) return + + console.log({ trimmedSearchValue }) + + // const encodedSearchValue = encodeURIComponent(trimmedSearchValue) + + // const searchParams = new URLSearchParams() + // searchParams.set(QueryParamKey.QUERY, encodedSearchValue) + } + + const handleSearchChange = (event: React.ChangeEvent) => { + setSearchValue(event.target.value) + } + + return ( +
+
+ + + +
+ ) +} From d8be05c9bafa06d8fd28817f74dfdb859cb640b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 13 Sep 2024 12:33:44 -0300 Subject: [PATCH 03/42] feat(design system): allow passing any react node as label content --- .github/workflows/test.yml | 2 +- packages/design-system/CHANGELOG.md | 1 + .../components/form/form-group/form-element/FormCheckbox.tsx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 848e3fa76..17c3bf460 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -143,7 +143,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Upload Cypress screenshots - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 if: failure() with: name: cypress-screenshots diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index e5323c591..60426fc19 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -49,6 +49,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **TransferList:** NEW TransferList component to transfer items between two list, also sortable. - **Table:** extend Props Interface to accept `bordered` prop to add or remove borders on all sides of the table and cells. Defaults to true. - **Offcanvas:** NEW Offcanvas component. +- **FormCheckbox:** modify Props Interface to allow any react node as `label` prop. # [1.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/dataverse-design-system@1.0.1...@iqss/dataverse-design-system@1.1.0) (2024-03-12) diff --git a/packages/design-system/src/lib/components/form/form-group/form-element/FormCheckbox.tsx b/packages/design-system/src/lib/components/form/form-group/form-element/FormCheckbox.tsx index 37dffe19a..d0a4c4c49 100644 --- a/packages/design-system/src/lib/components/form/form-group/form-element/FormCheckbox.tsx +++ b/packages/design-system/src/lib/components/form/form-group/form-element/FormCheckbox.tsx @@ -4,7 +4,7 @@ import * as React from 'react' export interface FormCheckboxProps extends Omit, 'type'> { id: string - label: string + label: React.ReactNode isValid?: boolean isInvalid?: boolean invalidFeedback?: string From ea3a5f3b02d5ec892748c17095689610f0f27d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 13 Sep 2024 12:34:35 -0300 Subject: [PATCH 04/42] feat: filters panel ui elements ready --- .../items-panel/ItemsPanel.module.scss | 7 ++- .../filter-panel/FilterPanel.module.scss | 7 +++ .../items-panel/filter-panel/FilterPanel.tsx | 17 +++--- .../type-filters/TypeFilters.module.scss | 16 ++++++ .../filter-panel/type-filters/TypeFilters.tsx | 53 +++++++++++++++++++ 5 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss create mode 100644 src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx diff --git a/src/sections/collection/items-panel/ItemsPanel.module.scss b/src/sections/collection/items-panel/ItemsPanel.module.scss index ca7dc14e1..4e4605390 100644 --- a/src/sections/collection/items-panel/ItemsPanel.module.scss +++ b/src/sections/collection/items-panel/ItemsPanel.module.scss @@ -1,8 +1,9 @@ +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; + .items-panel { display: flex; flex-direction: column; gap: 1rem; - border: solid 1px red; } .top-wrapper { @@ -25,10 +26,14 @@ display: flex; flex-direction: column; gap: 0.5rem; + padding-block: 1rem; + border: 1px solid $dv-border-color; + border-radius: 4px; // TODO:ME Add media queries in variables @media (min-width: 992px) { display: grid; grid-template-columns: 1fr 3fr; + gap: 1rem; } } diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss b/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss index 03b8acc71..c037f90d7 100644 --- a/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss +++ b/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss @@ -1,4 +1,6 @@ .filter-panel { + // border: solid 1px red; + .toggle-canvas-btn { display: block; @@ -6,4 +8,9 @@ display: none; } } + + .filters-wrapper { + width: 100%; + padding-inline: 1rem; + } } diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx b/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx index 9a5558da9..befd953f9 100644 --- a/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx +++ b/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx @@ -1,29 +1,34 @@ import { useState } from 'react' import { Button, Offcanvas } from '@iqss/dataverse-design-system' import { FunnelFill } from 'react-bootstrap-icons' +import { TypeFilters } from './type-filters/TypeFilters' import styles from './FilterPanel.module.scss' export const FilterPanel = () => { - const [show, setShow] = useState(false) + const [showOffcanvas, setShowOffcanvas] = useState(false) - const handleClose = () => setShow(false) - const handleShow = () => setShow(true) + const handleCloseOffcanvas = () => setShowOffcanvas(false) + const handleShowOffcanvas = () => setShowOffcanvas(true) return (
- + Filter Results - All the content goes here + +
+ +
+
) diff --git a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss new file mode 100644 index 000000000..2e03ee777 --- /dev/null +++ b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss @@ -0,0 +1,16 @@ +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; + +.type-filters { + input[type='checkbox'] { + cursor: pointer; + } + + .label-content-wrapper { + color: $dv-primary-color; + cursor: pointer; + + &.selected { + font-weight: bold; + } + } +} diff --git a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx new file mode 100644 index 000000000..e7b314348 --- /dev/null +++ b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -0,0 +1,53 @@ +import { ChangeEvent } from 'react' +import cn from 'classnames' +import { Form, Icon, IconName, Stack } from '@iqss/dataverse-design-system' +import styles from './TypeFilters.module.scss' + +export const TypeFilters = () => { + return ( + + ) => console.log(e.target.checked)} + label={ + + + Collections (19) + + } + // checked={Boolean(value as boolean)} // TODO:ME Handle this and other filter states in a useReducer + /> + ) => console.log(e.target.checked)} + label={ + + + Datasets (32) + + } + // checked={Boolean(value as boolean)} + /> + ) => console.log(e.target.checked)} + label={ + + + Files (10,081) + + } + // checked={Boolean(value as boolean)} + /> + + ) +} From 0dbb26335cc17c7d4f889e34a07a576fad53f6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 13 Sep 2024 16:11:44 -0300 Subject: [PATCH 05/42] feat: more layout work, separated panels --- src/sections/collection/Collection.tsx | 20 ++----------------- .../items-panel/ItemsPanel.module.scss | 3 --- .../collection/items-panel/ItemsPanel.tsx | 7 +++++-- .../filter-panel/FilterPanel.module.scss | 9 +++++++-- .../type-filters/TypeFilters.module.scss | 8 ++++---- .../filter-panel/type-filters/TypeFilters.tsx | 16 +++------------ .../items-list/ItemsList.module.scss | 10 +++++++++- .../items-panel/items-list/ItemsList.tsx | 10 ++++++++-- 8 files changed, 38 insertions(+), 45 deletions(-) diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 14b2feea4..892a871d1 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -1,10 +1,6 @@ import { Alert, Col, Row } from '@iqss/dataverse-design-system' import { DatasetRepository } from '../../dataset/domain/repositories/DatasetRepository' -import { DatasetsList } from './datasets-list/DatasetsList' -import { DatasetsListWithInfiniteScroll } from './datasets-list/DatasetsListWithInfiniteScroll' import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' - -import styles from './Collection.module.scss' import AddDataActionsButton from '../shared/add-data-actions/AddDataActionsButton' import { useSession } from '../session/SessionContext' import { useCollection } from './useCollection' @@ -82,6 +78,8 @@ export function Collection({ )} - {/* {infiniteScrollEnabled ? ( - - ) : ( - - )} */} ) diff --git a/src/sections/collection/items-panel/ItemsPanel.module.scss b/src/sections/collection/items-panel/ItemsPanel.module.scss index 4e4605390..5bc22306e 100644 --- a/src/sections/collection/items-panel/ItemsPanel.module.scss +++ b/src/sections/collection/items-panel/ItemsPanel.module.scss @@ -26,9 +26,6 @@ display: flex; flex-direction: column; gap: 0.5rem; - padding-block: 1rem; - border: 1px solid $dv-border-color; - border-radius: 4px; // TODO:ME Add media queries in variables @media (min-width: 992px) { diff --git a/src/sections/collection/items-panel/ItemsPanel.tsx b/src/sections/collection/items-panel/ItemsPanel.tsx index 20404405c..9cb37ceeb 100644 --- a/src/sections/collection/items-panel/ItemsPanel.tsx +++ b/src/sections/collection/items-panel/ItemsPanel.tsx @@ -1,13 +1,16 @@ +import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' import { FilterPanel } from './filter-panel/FilterPanel' import { ItemsList } from './items-list/ItemsList' import { SearchPanel } from './search-panel/SearchPanel' import styles from './ItemsPanel.module.scss' interface ItemsPanelProps { + collectionId: string + datasetRepository: DatasetRepository addDataSlot: JSX.Element | null } -export const ItemsPanel = ({ addDataSlot }: ItemsPanelProps) => { +export const ItemsPanel = ({ collectionId, datasetRepository, addDataSlot }: ItemsPanelProps) => { return (
@@ -18,7 +21,7 @@ export const ItemsPanel = ({ addDataSlot }: ItemsPanelProps) => {
- +
) diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss b/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss index c037f90d7..49655a3c7 100644 --- a/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss +++ b/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss @@ -1,5 +1,11 @@ +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; + .filter-panel { - // border: solid 1px red; + @media (min-width: 992px) { + padding: 1rem; + border: 1px solid $dv-border-color; + border-radius: 4px; + } .toggle-canvas-btn { display: block; @@ -11,6 +17,5 @@ .filters-wrapper { width: 100%; - padding-inline: 1rem; } } diff --git a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss index 2e03ee777..6f8131091 100644 --- a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss +++ b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss @@ -3,14 +3,14 @@ .type-filters { input[type='checkbox'] { cursor: pointer; + + &:checked + label { + font-weight: 500; + } } .label-content-wrapper { color: $dv-primary-color; cursor: pointer; - - &.selected { - font-weight: bold; - } } } diff --git a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx index e7b314348..54f851b30 100644 --- a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx +++ b/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -1,5 +1,4 @@ import { ChangeEvent } from 'react' -import cn from 'classnames' import { Form, Icon, IconName, Stack } from '@iqss/dataverse-design-system' import styles from './TypeFilters.module.scss' @@ -10,10 +9,7 @@ export const TypeFilters = () => { id="collections-type-check" onChange={(e: ChangeEvent) => console.log(e.target.checked)} label={ - + Collections (19) @@ -24,10 +20,7 @@ export const TypeFilters = () => { id="datasets-type-check" onChange={(e: ChangeEvent) => console.log(e.target.checked)} label={ - + Datasets (32) @@ -38,10 +31,7 @@ export const TypeFilters = () => { id="files-type-check" onChange={(e: ChangeEvent) => console.log(e.target.checked)} label={ - + Files (10,081) diff --git a/src/sections/collection/items-panel/items-list/ItemsList.module.scss b/src/sections/collection/items-panel/items-list/ItemsList.module.scss index 861a22b9c..9acace39c 100644 --- a/src/sections/collection/items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/items-panel/items-list/ItemsList.module.scss @@ -1,3 +1,11 @@ +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; + .items-list { - border: solid 1px orange; + padding: 0.5rem; + border: 1px solid $dv-border-color; + border-radius: 4px; + + @media (min-width: 768px) { + padding: 1rem; + } } diff --git a/src/sections/collection/items-panel/items-list/ItemsList.tsx b/src/sections/collection/items-panel/items-list/ItemsList.tsx index 9f5c89bf7..7b74d8a41 100644 --- a/src/sections/collection/items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/items-panel/items-list/ItemsList.tsx @@ -1,9 +1,15 @@ +import { DatasetRepository } from '../../../../dataset/domain/repositories/DatasetRepository' import styles from './ItemsList.module.scss' -export const ItemsList = () => { +interface ItemsListProps { + collectionId: string + datasetRepository: DatasetRepository +} + +export const ItemsList = ({ collectionId, datasetRepository }: ItemsListProps) => { return (
-

Items List

+

New List goes here

) } From 53e1ac68b6d78fb2c2c3987dfef7bd45271d9d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 13 Sep 2024 16:58:46 -0300 Subject: [PATCH 06/42] chore: renaming file --- src/sections/collection/Collection.tsx | 4 ++-- .../CollectionItemsPanel.module.scss} | 0 .../CollectionItemsPanel.tsx} | 8 ++++++-- .../filter-panel/FilterPanel.module.scss | 0 .../filter-panel/FilterPanel.tsx | 0 .../filter-panel/type-filters/TypeFilters.module.scss | 0 .../filter-panel/type-filters/TypeFilters.tsx | 0 .../items-list/ItemsList.module.scss | 0 .../items-list/ItemsList.tsx | 0 .../search-panel/SearchPanel.module.scss | 0 .../search-panel/SearchPanel.tsx | 0 .../collection-items-panel/useGetAccumulatedItems.tsx | 5 +++++ 12 files changed, 13 insertions(+), 4 deletions(-) rename src/sections/collection/{items-panel/ItemsPanel.module.scss => collection-items-panel/CollectionItemsPanel.module.scss} (100%) rename src/sections/collection/{items-panel/ItemsPanel.tsx => collection-items-panel/CollectionItemsPanel.tsx} (82%) rename src/sections/collection/{items-panel => collection-items-panel}/filter-panel/FilterPanel.module.scss (100%) rename src/sections/collection/{items-panel => collection-items-panel}/filter-panel/FilterPanel.tsx (100%) rename src/sections/collection/{items-panel => collection-items-panel}/filter-panel/type-filters/TypeFilters.module.scss (100%) rename src/sections/collection/{items-panel => collection-items-panel}/filter-panel/type-filters/TypeFilters.tsx (100%) rename src/sections/collection/{items-panel => collection-items-panel}/items-list/ItemsList.module.scss (100%) rename src/sections/collection/{items-panel => collection-items-panel}/items-list/ItemsList.tsx (100%) rename src/sections/collection/{items-panel => collection-items-panel}/search-panel/SearchPanel.module.scss (100%) rename src/sections/collection/{items-panel => collection-items-panel}/search-panel/SearchPanel.tsx (100%) create mode 100644 src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 892a871d1..65ab7126b 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -11,7 +11,7 @@ import { CollectionInfo } from './CollectionInfo' import { Trans, useTranslation } from 'react-i18next' import { useScrollTop } from '../../shared/hooks/useScrollTop' import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions' -import { ItemsPanel } from './items-panel/ItemsPanel' +import { CollectionItemsPanel } from './collection-items-panel/CollectionItemsPanel' interface CollectionProps { repository: CollectionRepository @@ -77,7 +77,7 @@ export function Collection({ )} )} - { +export const CollectionItemsPanel = ({ + collectionId, + datasetRepository, + addDataSlot +}: ItemsPanelProps) => { return (
diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss b/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.module.scss similarity index 100% rename from src/sections/collection/items-panel/filter-panel/FilterPanel.module.scss rename to src/sections/collection/collection-items-panel/filter-panel/FilterPanel.module.scss diff --git a/src/sections/collection/items-panel/filter-panel/FilterPanel.tsx b/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.tsx similarity index 100% rename from src/sections/collection/items-panel/filter-panel/FilterPanel.tsx rename to src/sections/collection/collection-items-panel/filter-panel/FilterPanel.tsx diff --git a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss similarity index 100% rename from src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.module.scss rename to src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss diff --git a/src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx similarity index 100% rename from src/sections/collection/items-panel/filter-panel/type-filters/TypeFilters.tsx rename to src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx diff --git a/src/sections/collection/items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss similarity index 100% rename from src/sections/collection/items-panel/items-list/ItemsList.module.scss rename to src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss diff --git a/src/sections/collection/items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx similarity index 100% rename from src/sections/collection/items-panel/items-list/ItemsList.tsx rename to src/sections/collection/collection-items-panel/items-list/ItemsList.tsx diff --git a/src/sections/collection/items-panel/search-panel/SearchPanel.module.scss b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.module.scss similarity index 100% rename from src/sections/collection/items-panel/search-panel/SearchPanel.module.scss rename to src/sections/collection/collection-items-panel/search-panel/SearchPanel.module.scss diff --git a/src/sections/collection/items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx similarity index 100% rename from src/sections/collection/items-panel/search-panel/SearchPanel.tsx rename to src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx diff --git a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx new file mode 100644 index 000000000..7dae4ff18 --- /dev/null +++ b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx @@ -0,0 +1,5 @@ +export const useGetAccumulatedItems = () => { + return { + blah: '' + } +} From 5603fbc50fbcc87df715411b2a346a6e21baaade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 13 Sep 2024 16:59:16 -0300 Subject: [PATCH 07/42] feat: domain & infra setup --- .../domain/models/CollectionItemSubset.ts | 8 ++++++++ .../domain/models/CollectionItemsPaginationInfo.ts | 7 +++++++ src/collection/domain/models/CollectionPreview.ts | 2 ++ .../domain/repositories/CollectionRepository.ts | 6 ++++++ .../domain/useCases/getCollectionItems.ts | 13 +++++++++++++ .../repositories/CollectionJSDataverseRepository.ts | 12 ++++++++++++ src/dataset/domain/models/DatasetPreview.ts | 3 +++ src/files/domain/models/FilePreview.ts | 2 ++ 8 files changed, 53 insertions(+) create mode 100644 src/collection/domain/models/CollectionItemSubset.ts create mode 100644 src/collection/domain/models/CollectionItemsPaginationInfo.ts create mode 100644 src/collection/domain/useCases/getCollectionItems.ts diff --git a/src/collection/domain/models/CollectionItemSubset.ts b/src/collection/domain/models/CollectionItemSubset.ts new file mode 100644 index 000000000..d0a0893ec --- /dev/null +++ b/src/collection/domain/models/CollectionItemSubset.ts @@ -0,0 +1,8 @@ +import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' +import { FilePreview } from '../../../files/domain/models/FilePreview' +import { CollectionPreview } from './CollectionPreview' + +export interface CollectionItemSubset { + items: (CollectionPreview | DatasetPreview | FilePreview)[] + totalItemCount: number +} diff --git a/src/collection/domain/models/CollectionItemsPaginationInfo.ts b/src/collection/domain/models/CollectionItemsPaginationInfo.ts new file mode 100644 index 000000000..1019a3806 --- /dev/null +++ b/src/collection/domain/models/CollectionItemsPaginationInfo.ts @@ -0,0 +1,7 @@ +import { PaginationInfo } from '../../../shared/pagination/domain/models/PaginationInfo' + +export class CollectionItemsPaginationInfo extends PaginationInfo { + constructor(page = 1, pageSize = 10, totalItems = 0, itemName = 'results') { + super(page, pageSize, totalItems, itemName) + } +} diff --git a/src/collection/domain/models/CollectionPreview.ts b/src/collection/domain/models/CollectionPreview.ts index c7842f611..af1ac9127 100644 --- a/src/collection/domain/models/CollectionPreview.ts +++ b/src/collection/domain/models/CollectionPreview.ts @@ -1,3 +1,5 @@ +// TODO:ME Once 181 is merged, update interface + export interface CollectionPreview { id: string name: string diff --git a/src/collection/domain/repositories/CollectionRepository.ts b/src/collection/domain/repositories/CollectionRepository.ts index 3cc8c3e96..ad94ba45a 100644 --- a/src/collection/domain/repositories/CollectionRepository.ts +++ b/src/collection/domain/repositories/CollectionRepository.ts @@ -1,5 +1,7 @@ import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' +import { CollectionItemsPaginationInfo } from '../models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '../models/CollectionItemSubset' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' import { CollectionDTO } from '../useCases/DTOs/CollectionDTO' @@ -8,4 +10,8 @@ export interface CollectionRepository { create(collection: CollectionDTO, hostCollection?: string): Promise getFacets(collectionIdOrAlias: number | string): Promise getUserPermissions(collectionIdOrAlias: number | string): Promise + getItems( + collectionIdOrAlias: number | string, + paginationInfo: CollectionItemsPaginationInfo + ): Promise } diff --git a/src/collection/domain/useCases/getCollectionItems.ts b/src/collection/domain/useCases/getCollectionItems.ts new file mode 100644 index 000000000..f051e97d2 --- /dev/null +++ b/src/collection/domain/useCases/getCollectionItems.ts @@ -0,0 +1,13 @@ +import { CollectionRepository } from '../repositories/CollectionRepository' +import { CollectionItemsPaginationInfo } from '../models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '../models/CollectionItemSubset' + +export async function getCollectionItems( + collectionRepository: CollectionRepository, + collectionId: string, + paginationInfo: CollectionItemsPaginationInfo +): Promise { + return collectionRepository.getItems(collectionId, paginationInfo).catch((error: Error) => { + throw new Error(error.message) + }) +} diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts index 0eda9f56c..0ad902767 100644 --- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts +++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts @@ -10,6 +10,8 @@ import { JSCollectionMapper } from '../mappers/JSCollectionMapper' import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO' import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' +import { CollectionItemsPaginationInfo } from '../../domain/models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' export class CollectionJSDataverseRepository implements CollectionRepository { getById(id: string): Promise { @@ -33,4 +35,14 @@ export class CollectionJSDataverseRepository implements CollectionRepository { .execute(collectionIdOrAlias) .then((jsCollectionUserPermissions) => jsCollectionUserPermissions) } + + getItems( + _collectionIdOrAlias: number | string, + _paginationInfo: CollectionItemsPaginationInfo + ): Promise { + return Promise.resolve({ + items: [], + totalItemCount: 0 + }) + } } diff --git a/src/dataset/domain/models/DatasetPreview.ts b/src/dataset/domain/models/DatasetPreview.ts index e7688499d..1b9e905b6 100644 --- a/src/dataset/domain/models/DatasetPreview.ts +++ b/src/dataset/domain/models/DatasetPreview.ts @@ -1,5 +1,8 @@ import { DatasetVersion } from './Dataset' +// TODO:ME Once 181 is merged, update interface +// TODO:ME A class is not needed, we can change to an interface, we can ellipse the text with CSS as done in CollectionCard and FileCard + export class DatasetPreview { constructor( public persistentId: string, diff --git a/src/files/domain/models/FilePreview.ts b/src/files/domain/models/FilePreview.ts index 19e8426d9..7ab731e94 100644 --- a/src/files/domain/models/FilePreview.ts +++ b/src/files/domain/models/FilePreview.ts @@ -7,6 +7,8 @@ import { } from '../../../dataset/domain/models/Dataset' import { FilePermissions } from './FilePermissions' +// TODO:ME Once 181 is merged, update interface + export interface FilePreview { id: number name: string From 4542881a22023adc36fb18ee549c29e6e0af4bc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 16 Sep 2024 13:43:19 -0300 Subject: [PATCH 08/42] feat: inital render items, empty items and error message --- public/locales/en/collection.json | 4 + .../domain/models/CollectionItemSubset.ts | 4 +- .../models/CollectionItemsPaginationInfo.ts | 2 +- .../repositories/CollectionRepository.ts | 2 +- .../CollectionJSDataverseRepository.ts | 13 +- src/sections/collection/Collection.tsx | 40 +++--- src/sections/collection/CollectionFactory.tsx | 11 +- .../CollectionItemsPanel.tsx | 95 +++++++++++++- .../items-list/ErrorItemsMessage.tsx | 11 ++ .../items-list/ItemsList.module.scss | 57 ++++++++- .../items-list/ItemsList.tsx | 97 +++++++++++++- .../items-list/NoItemsMessage.tsx | 24 ++++ .../useGetAccumulatedItems.tsx | 120 +++++++++++++++++- 13 files changed, 428 insertions(+), 52 deletions(-) create mode 100644 src/sections/collection/collection-items-panel/items-list/ErrorItemsMessage.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx diff --git a/public/locales/en/collection.json b/public/locales/en/collection.json index 79a4473af..8be9be719 100644 --- a/public/locales/en/collection.json +++ b/public/locales/en/collection.json @@ -3,5 +3,9 @@ "authenticated": "This collection currently has no datasets. You can add to it by using the Add Data button on this page.", "anonymous": "This collection currently has no datasets. Please <1>log in to see if you are able to add to it." }, + "noItemsMessage": { + "authenticated": "This collection currently has no collections, datasets or files. You can add to it by using the Add Data button on this page.", + "anonymous": "This collection currently has no collections, datasets or files. Please <1>log in to see if you are able to add to it." + }, "createdAlert": "You have successfully created your collection! To learn more about what you can do with your collection, check out the User Guide." } diff --git a/src/collection/domain/models/CollectionItemSubset.ts b/src/collection/domain/models/CollectionItemSubset.ts index d0a0893ec..f017b36fe 100644 --- a/src/collection/domain/models/CollectionItemSubset.ts +++ b/src/collection/domain/models/CollectionItemSubset.ts @@ -3,6 +3,8 @@ import { FilePreview } from '../../../files/domain/models/FilePreview' import { CollectionPreview } from './CollectionPreview' export interface CollectionItemSubset { - items: (CollectionPreview | DatasetPreview | FilePreview)[] + items: CollectionItem[] totalItemCount: number } + +export type CollectionItem = CollectionPreview | DatasetPreview | FilePreview diff --git a/src/collection/domain/models/CollectionItemsPaginationInfo.ts b/src/collection/domain/models/CollectionItemsPaginationInfo.ts index 1019a3806..084926342 100644 --- a/src/collection/domain/models/CollectionItemsPaginationInfo.ts +++ b/src/collection/domain/models/CollectionItemsPaginationInfo.ts @@ -1,7 +1,7 @@ import { PaginationInfo } from '../../../shared/pagination/domain/models/PaginationInfo' export class CollectionItemsPaginationInfo extends PaginationInfo { - constructor(page = 1, pageSize = 10, totalItems = 0, itemName = 'results') { + constructor(page = 1, pageSize = 10, totalItems = 0, itemName = 'result') { super(page, pageSize, totalItems, itemName) } } diff --git a/src/collection/domain/repositories/CollectionRepository.ts b/src/collection/domain/repositories/CollectionRepository.ts index ad94ba45a..a4aec8703 100644 --- a/src/collection/domain/repositories/CollectionRepository.ts +++ b/src/collection/domain/repositories/CollectionRepository.ts @@ -11,7 +11,7 @@ export interface CollectionRepository { getFacets(collectionIdOrAlias: number | string): Promise getUserPermissions(collectionIdOrAlias: number | string): Promise getItems( - collectionIdOrAlias: number | string, + collectionId: string, paginationInfo: CollectionItemsPaginationInfo ): Promise } diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts index 0ad902767..9a63d4e25 100644 --- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts +++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts @@ -4,7 +4,8 @@ import { createCollection, getCollection, getCollectionFacets, - getCollectionUserPermissions + getCollectionUserPermissions, + getCollectionItems } from '@iqss/dataverse-client-javascript' import { JSCollectionMapper } from '../mappers/JSCollectionMapper' import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO' @@ -36,13 +37,13 @@ export class CollectionJSDataverseRepository implements CollectionRepository { .then((jsCollectionUserPermissions) => jsCollectionUserPermissions) } + // TODO:ME After updating previews object to match the response models we should not see ts error anymore under the return keyword getItems( - _collectionIdOrAlias: number | string, + collectionId: string, _paginationInfo: CollectionItemsPaginationInfo ): Promise { - return Promise.resolve({ - items: [], - totalItemCount: 0 - }) + return getCollectionItems + .execute(collectionId) + .then((jsCollectionItemSubset) => jsCollectionItemSubset) } } diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 65ab7126b..9cb78e5a2 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -1,41 +1,38 @@ +import { Trans, useTranslation } from 'react-i18next' import { Alert, Col, Row } from '@iqss/dataverse-design-system' -import { DatasetRepository } from '../../dataset/domain/repositories/DatasetRepository' -import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' -import AddDataActionsButton from '../shared/add-data-actions/AddDataActionsButton' -import { useSession } from '../session/SessionContext' -import { useCollection } from './useCollection' import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository' -import { PageNotFound } from '../page-not-found/PageNotFound' -import { CollectionSkeleton } from './CollectionSkeleton' -import { CollectionInfo } from './CollectionInfo' -import { Trans, useTranslation } from 'react-i18next' +import { useCollection } from './useCollection' import { useScrollTop } from '../../shared/hooks/useScrollTop' +import { useSession } from '../session/SessionContext' import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions' +import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' +import AddDataActionsButton from '../shared/add-data-actions/AddDataActionsButton' import { CollectionItemsPanel } from './collection-items-panel/CollectionItemsPanel' +import { CollectionInfo } from './CollectionInfo' +import { CollectionSkeleton } from './CollectionSkeleton' +import { PageNotFound } from '../page-not-found/PageNotFound' interface CollectionProps { - repository: CollectionRepository - datasetRepository: DatasetRepository - id: string + collectionRepository: CollectionRepository + collectionId: string created: boolean page?: number infiniteScrollEnabled?: boolean } export function Collection({ - repository, - id, - datasetRepository, + collectionId, + collectionRepository, created, page, infiniteScrollEnabled = false }: CollectionProps) { useScrollTop() const { user } = useSession() - const { collection, isLoading } = useCollection(repository, id) + const { collection, isLoading } = useCollection(collectionRepository, collectionId) const { collectionUserPermissions } = useGetCollectionUserPermissions({ - collectionIdOrAlias: id, - collectionRepository: repository + collectionIdOrAlias: collectionId, + collectionRepository }) const canUserAddCollection = Boolean(collectionUserPermissions?.canAddCollection) @@ -78,12 +75,13 @@ export function Collection({ )} diff --git a/src/sections/collection/CollectionFactory.tsx b/src/sections/collection/CollectionFactory.tsx index 3d6af655c..f275ead18 100644 --- a/src/sections/collection/CollectionFactory.tsx +++ b/src/sections/collection/CollectionFactory.tsx @@ -1,12 +1,10 @@ import { ReactElement } from 'react' -import { Collection } from './Collection' -import { DatasetJSDataverseRepository } from '../../dataset/infrastructure/repositories/DatasetJSDataverseRepository' import { useLocation, useParams, useSearchParams } from 'react-router-dom' import { CollectionJSDataverseRepository } from '../../collection/infrastructure/repositories/CollectionJSDataverseRepository' +import { Collection } from './Collection' import { INFINITE_SCROLL_ENABLED } from './config' -const datasetRepository = new DatasetJSDataverseRepository() -const repository = new CollectionJSDataverseRepository() +const collectionRepository = new CollectionJSDataverseRepository() export class CollectionFactory { static create(): ReactElement { return @@ -23,10 +21,9 @@ function CollectionWithSearchParams() { return ( diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 538fc13f3..a40314b97 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -1,20 +1,97 @@ -import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' +import { useEffect, useState } from 'react' +import useInfiniteScroll from 'react-infinite-scroll-hook' +import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' +import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' +import { TemporarySearchCriteria, useGetAccumulatedItems } from './useGetAccumulatedItems' import { FilterPanel } from './filter-panel/FilterPanel' import { ItemsList } from './items-list/ItemsList' import { SearchPanel } from './search-panel/SearchPanel' import styles from './CollectionItemsPanel.module.scss' +import { useLoading } from '../../loading/LoadingContext' + +const PAGE_SIZE = 10 +const INITIAL_PAGE = 1 interface ItemsPanelProps { collectionId: string - datasetRepository: DatasetRepository + collectionRepository: CollectionRepository addDataSlot: JSX.Element | null } export const CollectionItemsPanel = ({ collectionId, - datasetRepository, + collectionRepository, addDataSlot }: ItemsPanelProps) => { + const { setIsLoading } = useLoading() + const [searchCriteria, setSearchCriteria] = useState({}) + const [paginationInfo, setPaginationInfo] = useState( + new CollectionItemsPaginationInfo(INITIAL_PAGE) + ) + const { + isLoadingItems, + accumulatedItems, + totalAvailable, + hasNextPage, + error, + loadMore, + isEmptyItems, + areItemsAvailable, + accumulatedCount + } = useGetAccumulatedItems({ + collectionRepository, + collectionId, + paginationInfo + }) + + const [sentryRef, { rootRef }] = useInfiniteScroll({ + loading: isLoadingItems, + hasNextPage: hasNextPage, + onLoadMore: () => void handleOnLoadMore(paginationInfo), + disabled: !!error, + rootMargin: '0px 0px 250px 0px' + }) + + console.log({ + accumulatedItems, + accumulatedCount, + areItemsAvailable, + error, + hasNextPage, + isLoadingItems, + isEmptyItems, + totalAvailable + }) + + async function handleOnLoadMore(currentPagination: CollectionItemsPaginationInfo) { + let paginationInfoToSend = currentPagination + if (totalAvailable !== undefined) { + paginationInfoToSend = currentPagination.goToNextPage() + } + const totalFilesCount = await loadMore(paginationInfoToSend, searchCriteria) + + if (totalFilesCount !== undefined) { + const paginationInfoUpdated = paginationInfoToSend.withTotal(totalFilesCount) + setPaginationInfo(paginationInfoUpdated) + } + } + + const handleCriteriaChange = async (newCriteria: any) => { + // scrollableContainerRef.current?.scrollTo({ top: 0 }) + // setCriteria(newCriteria) + // const resetedPaginationInfo = new FilePaginationInfo() + // setPaginationInfo(resetedPaginationInfo) + // const totalFilesCount = await loadMore(resetedPaginationInfo, newCriteria, true) + // if (totalFilesCount !== undefined) { + // const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalFilesCount) + // setPaginationInfo(paginationInfoUpdated) + // } + } + + useEffect(() => { + setIsLoading(isLoadingItems) + }, [isLoadingItems, setIsLoading]) + return (
@@ -25,7 +102,17 @@ export const CollectionItemsPanel = ({
- +
) diff --git a/src/sections/collection/collection-items-panel/items-list/ErrorItemsMessage.tsx b/src/sections/collection/collection-items-panel/items-list/ErrorItemsMessage.tsx new file mode 100644 index 000000000..248808710 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/ErrorItemsMessage.tsx @@ -0,0 +1,11 @@ +import { Alert } from '@iqss/dataverse-design-system' + +interface ErrorItemsMessageProps { + errorMessage: string +} + +export const ErrorItemsMessage = ({ errorMessage }: ErrorItemsMessageProps) => ( + + {errorMessage} + +) diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss index 9acace39c..8de160be9 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss @@ -1,11 +1,62 @@ @import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; .items-list { - padding: 0.5rem; + --inline-padding: 1rem; + + height: 650px; + max-height: 650px; + padding-inline: var(--inline-padding); + overflow-x: hidden; + overflow-y: auto; border: 1px solid $dv-border-color; border-radius: 4px; - @media (min-width: 768px) { - padding: 1rem; + @media screen and (max-width: 768px) { + --inline-padding: 0.5rem; + } + + @media screen and (min-width: 1280px) { + height: 60vh; + max-height: 60vh; + } + + &.empty-or-error { + padding-block: var(--inline-padding); + } + + .empty-message-container { + padding: 0.5em 1em; + background: $dv-warning-box-color; + } + + header { + position: sticky; + top: 0; + z-index: 10; + width: calc(100% + (var(--inline-padding) * 2)); + padding: 0.5rem var(--inline-padding); + background-color: var(--bs-white); + box-shadow: 0 0 10px 0 rgba(0 0 0 / 30%); + transform: translateX(calc(var(--inline-padding) * -1)); + } + + ul { + margin: 0; + padding: 0; + + li { + list-style: none; + } + } + + .pagination-results-skeleton { + position: sticky; + top: 0; + z-index: 10; + width: calc(100% + (var(--inline-padding) * 2)); + padding: 1rem var(--inline-padding) 0.5rem; + background-color: var(--bs-white); + box-shadow: 0 0 10px 0 rgba(0 0 0 / 30%); + transform: translateX(calc(var(--inline-padding) * -1)); } } diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 7b74d8a41..9679335ff 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -1,15 +1,100 @@ -import { DatasetRepository } from '../../../../dataset/domain/repositories/DatasetRepository' +import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' +import { UseInfiniteScrollHookRefCallback } from 'react-infinite-scroll-hook' +import cn from 'classnames' +import { CollectionItem } from '../../../../collection/domain/models/CollectionItemSubset' +import { CollectionItemsPaginationInfo } from '../../../../collection/domain/models/CollectionItemsPaginationInfo' +import { PaginationResultsInfo } from '../../../shared/pagination/PaginationResultsInfo' +import { ErrorItemsMessage } from './ErrorItemsMessage' +import { NoItemsMessage } from './NoItemsMessage' +import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' import styles from './ItemsList.module.scss' interface ItemsListProps { - collectionId: string - datasetRepository: DatasetRepository + items: CollectionItem[] + error: string | null + accumulatedCount: number + areItemsAvailable: boolean + hasNextPage: boolean + isEmptyItems: boolean + paginationInfo: CollectionItemsPaginationInfo + rootRef: any + sentryRef: UseInfiniteScrollHookRefCallback } -export const ItemsList = ({ collectionId, datasetRepository }: ItemsListProps) => { +export const ItemsList = ({ + items, + error, + accumulatedCount, + areItemsAvailable, + hasNextPage, + isEmptyItems, + paginationInfo, + rootRef, + sentryRef +}: ItemsListProps) => { return ( -
-

New List goes here

+
+ {isEmptyItems && } + + {error && } + + {areItemsAvailable && ( + <> +
+ +
+
    + {items.map((collectionItem, index) => { + return ( +
  • +

    A collection item

    +
  • + ) + })} +
+ + )} + + {hasNextPage && !error && !isEmptyItems && ( +
+ + {accumulatedCount === NO_COLLECTION_ITEMS && } + + +
+ )}
) } + +export const InitialLoadingSkeleton = () => ( + <> +
+ +
+ + + + + + + + +) + +export const LoadingSkeleton = () => ( + <> + + + + +) diff --git a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx new file mode 100644 index 000000000..bc200a8f0 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx @@ -0,0 +1,24 @@ +import { Trans, useTranslation } from 'react-i18next' +import { useSession } from '../../../session/SessionContext' +import { Route } from '../../../Route.enum' +import styles from './ItemsList.module.scss' + +export function NoItemsMessage() { + const { t } = useTranslation('collection') + const { user } = useSession() + + return ( +
+ {user ? ( +

{t('noItemsMessage.authenticated')}

+ ) : ( + +

+ This collection currently has no collections, datasets or files. Please{' '} + log in to see if you are able to add to it. +

+
+ )} +
+ ) +} diff --git a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx index 7dae4ff18..b657e196f 100644 --- a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx +++ b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx @@ -1,5 +1,121 @@ -export const useGetAccumulatedItems = () => { +import { useMemo, useState } from 'react' + +import { getCollectionItems } from '../../../collection/domain/useCases/getCollectionItems' +import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' +import { + CollectionItem, + CollectionItemSubset +} from '../../../collection/domain/models/CollectionItemSubset' +import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' + +export type TemporarySearchCriteria = {} + +export const NO_COLLECTION_ITEMS = 0 + +type UseGetAccumulatedItemsReturnType = { + isLoadingItems: boolean + accumulatedItems: CollectionItem[] + totalAvailable: number | undefined + hasNextPage: boolean + error: string | null + loadMore: ( + paginationInfo: CollectionItemsPaginationInfo, + criteria: TemporarySearchCriteria, + resetAccumulated?: boolean + ) => Promise + isEmptyItems: boolean + areItemsAvailable: boolean + accumulatedCount: number +} + +type UseGetAccumulatedItemsParams = { + collectionRepository: CollectionRepository + collectionId: string + paginationInfo: CollectionItemsPaginationInfo +} + +export const useGetAccumulatedItems = ({ + collectionRepository, + collectionId, + paginationInfo +}: UseGetAccumulatedItemsParams): UseGetAccumulatedItemsReturnType => { + const [isLoadingItems, setIsLoadingItems] = useState(false) + const [accumulatedItems, setAccumulatedItems] = useState([]) + const [hasNextPage, setHasNextPage] = useState(true) + const [totalAvailable, setTotalAvailable] = useState(undefined) + const [error, setError] = useState(null) + + const isEmptyItems = useMemo(() => totalAvailable === NO_COLLECTION_ITEMS, [totalAvailable]) + const areItemsAvailable = useMemo(() => { + return typeof totalAvailable === 'number' && totalAvailable > NO_COLLECTION_ITEMS && !error + }, [totalAvailable, error]) + const accumulatedCount = useMemo(() => accumulatedItems.length, [accumulatedItems]) + + const loadMore = async ( + pagination: CollectionItemsPaginationInfo, + criteria: TemporarySearchCriteria, + resetAccumulated = false + ): Promise => { + setIsLoadingItems(true) + + try { + const { items, totalItemCount } = await loadNextItems( + collectionRepository, + collectionId, + pagination, + criteria + ) + + const newAccumulatedItems = !resetAccumulated ? [...accumulatedItems, ...items] : items + + setAccumulatedItems(newAccumulatedItems) + + setTotalAvailable(totalItemCount) + + const isNextPage = !resetAccumulated + ? newAccumulatedItems.length < totalItemCount + : items.length < totalItemCount + + setHasNextPage(isNextPage) + + if (!isNextPage) { + setIsLoadingItems(false) + } + + return totalItemCount + } catch (err) { + const errorMessage = + err instanceof Error && err.message + ? err.message + : 'Something went wrong getting the collection items' + setError(errorMessage) + } finally { + setIsLoadingItems(false) + } + } + return { - blah: '' + isLoadingItems, + accumulatedItems, + totalAvailable, + hasNextPage, + error, + loadMore, + isEmptyItems, + areItemsAvailable, + accumulatedCount } } + +async function loadNextItems( + collectionRepository: CollectionRepository, + collectionId: string, + paginationInfo: CollectionItemsPaginationInfo, + _criteria: TemporarySearchCriteria +): Promise { + return getCollectionItems(collectionRepository, collectionId, paginationInfo).catch( + (err: Error) => { + throw new Error(err.message) + } + ) +} From 74d23465870da72d427b558dd7d99649afa1b903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 16 Sep 2024 16:45:14 -0300 Subject: [PATCH 09/42] feat: more work on infinite scrolling --- package-lock.json | 8 +- package.json | 2 +- .../repositories/CollectionRepository.ts | 4 +- .../domain/useCases/getCollectionItems.ts | 12 +- .../CollectionJSDataverseRepository.ts | 8 +- .../CollectionItemsPanel.tsx | 67 ++++----- .../items-list/ItemsList.module.scss | 3 + .../items-list/ItemsList.tsx | 130 +++++++++++------- .../useGetAccumulatedItems.tsx | 20 +-- 9 files changed, 140 insertions(+), 114 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6af6ac5b3..1c3c232d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-pr187.f29c0e6", + "@iqss/dataverse-client-javascript": "2.0.0-pr181.e386829", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -3674,9 +3674,9 @@ }, "node_modules/@iqss/dataverse-client-javascript": { "name": "@IQSS/dataverse-client-javascript", - "version": "2.0.0-pr187.f29c0e6", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-pr187.f29c0e6/324f487a2b1437df668e34159404161888279638", - "integrity": "sha512-0OX9nmh7dY3Gg5euE7buCqTeyh+1B+GhFcDz2gJoND4oM3kIZalYS+bLsEoEekR2o25agP6b+ANyQ5kvZeFuig==", + "version": "2.0.0-pr181.e386829", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-pr181.e386829/c76266af444e6b34809eb9be644e1f4303c69878", + "integrity": "sha512-mMsYgteE3egYeB3CFP+hgHJ/ZfvCjM0ScZ7KvLS2aA9D27MGnDzqoX8Zf4OzX185YW4R6xg80s2SxCALquRqAg==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", diff --git a/package.json b/package.json index 446462aaa..0b9df16ee 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-pr187.f29c0e6", + "@iqss/dataverse-client-javascript": "2.0.0-pr181.e386829", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", diff --git a/src/collection/domain/repositories/CollectionRepository.ts b/src/collection/domain/repositories/CollectionRepository.ts index a4aec8703..a62acd0e3 100644 --- a/src/collection/domain/repositories/CollectionRepository.ts +++ b/src/collection/domain/repositories/CollectionRepository.ts @@ -1,3 +1,4 @@ +import { TemporarySearchCriteria } from '../../../sections/collection/collection-items-panel/useGetAccumulatedItems' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' import { CollectionItemsPaginationInfo } from '../models/CollectionItemsPaginationInfo' @@ -12,6 +13,7 @@ export interface CollectionRepository { getUserPermissions(collectionIdOrAlias: number | string): Promise getItems( collectionId: string, - paginationInfo: CollectionItemsPaginationInfo + paginationInfo: CollectionItemsPaginationInfo, + searchCriteria?: TemporarySearchCriteria ): Promise } diff --git a/src/collection/domain/useCases/getCollectionItems.ts b/src/collection/domain/useCases/getCollectionItems.ts index f051e97d2..2e99b948f 100644 --- a/src/collection/domain/useCases/getCollectionItems.ts +++ b/src/collection/domain/useCases/getCollectionItems.ts @@ -1,13 +1,17 @@ import { CollectionRepository } from '../repositories/CollectionRepository' import { CollectionItemsPaginationInfo } from '../models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../models/CollectionItemSubset' +import { TemporarySearchCriteria } from '../../../sections/collection/collection-items-panel/useGetAccumulatedItems' export async function getCollectionItems( collectionRepository: CollectionRepository, collectionId: string, - paginationInfo: CollectionItemsPaginationInfo + paginationInfo: CollectionItemsPaginationInfo, + searchCriteria: TemporarySearchCriteria ): Promise { - return collectionRepository.getItems(collectionId, paginationInfo).catch((error: Error) => { - throw new Error(error.message) - }) + return collectionRepository + .getItems(collectionId, paginationInfo, searchCriteria) + .catch((error: Error) => { + throw new Error(error.message) + }) } diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts index 9a63d4e25..cef976696 100644 --- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts +++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts @@ -13,6 +13,7 @@ import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' import { CollectionItemsPaginationInfo } from '../../domain/models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' +import { TemporarySearchCriteria } from '../../../sections/collection/collection-items-panel/useGetAccumulatedItems' export class CollectionJSDataverseRepository implements CollectionRepository { getById(id: string): Promise { @@ -40,10 +41,13 @@ export class CollectionJSDataverseRepository implements CollectionRepository { // TODO:ME After updating previews object to match the response models we should not see ts error anymore under the return keyword getItems( collectionId: string, - _paginationInfo: CollectionItemsPaginationInfo + paginationInfo?: CollectionItemsPaginationInfo, + _searchCriteria?: TemporarySearchCriteria ): Promise { + console.log({ _searchCriteria }) + return getCollectionItems - .execute(collectionId) + .execute(collectionId, paginationInfo?.pageSize, paginationInfo?.offset) .then((jsCollectionItemSubset) => jsCollectionItemSubset) } } diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index a40314b97..c6af480d5 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -1,13 +1,12 @@ -import { useEffect, useState } from 'react' -import useInfiniteScroll from 'react-infinite-scroll-hook' +import { useEffect, useRef, useState } from 'react' import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' import { TemporarySearchCriteria, useGetAccumulatedItems } from './useGetAccumulatedItems' +import { useLoading } from '../../loading/LoadingContext' import { FilterPanel } from './filter-panel/FilterPanel' import { ItemsList } from './items-list/ItemsList' import { SearchPanel } from './search-panel/SearchPanel' import styles from './CollectionItemsPanel.module.scss' -import { useLoading } from '../../loading/LoadingContext' const PAGE_SIZE = 10 const INITIAL_PAGE = 1 @@ -26,8 +25,10 @@ export const CollectionItemsPanel = ({ const { setIsLoading } = useLoading() const [searchCriteria, setSearchCriteria] = useState({}) const [paginationInfo, setPaginationInfo] = useState( - new CollectionItemsPaginationInfo(INITIAL_PAGE) + new CollectionItemsPaginationInfo() ) + const itemsListContainerRef = useRef(null) + const { isLoadingItems, accumulatedItems, @@ -40,27 +41,7 @@ export const CollectionItemsPanel = ({ accumulatedCount } = useGetAccumulatedItems({ collectionRepository, - collectionId, - paginationInfo - }) - - const [sentryRef, { rootRef }] = useInfiniteScroll({ - loading: isLoadingItems, - hasNextPage: hasNextPage, - onLoadMore: () => void handleOnLoadMore(paginationInfo), - disabled: !!error, - rootMargin: '0px 0px 250px 0px' - }) - - console.log({ - accumulatedItems, - accumulatedCount, - areItemsAvailable, - error, - hasNextPage, - isLoadingItems, - isEmptyItems, - totalAvailable + collectionId }) async function handleOnLoadMore(currentPagination: CollectionItemsPaginationInfo) { @@ -68,24 +49,29 @@ export const CollectionItemsPanel = ({ if (totalAvailable !== undefined) { paginationInfoToSend = currentPagination.goToNextPage() } - const totalFilesCount = await loadMore(paginationInfoToSend, searchCriteria) + console.log('paginationInfoToSend', paginationInfoToSend) + const totalItemsCount = await loadMore(paginationInfoToSend, searchCriteria) - if (totalFilesCount !== undefined) { - const paginationInfoUpdated = paginationInfoToSend.withTotal(totalFilesCount) + if (totalItemsCount !== undefined) { + const paginationInfoUpdated = paginationInfoToSend.withTotal(totalItemsCount) setPaginationInfo(paginationInfoUpdated) } } - const handleCriteriaChange = async (newCriteria: any) => { - // scrollableContainerRef.current?.scrollTo({ top: 0 }) - // setCriteria(newCriteria) - // const resetedPaginationInfo = new FilePaginationInfo() - // setPaginationInfo(resetedPaginationInfo) - // const totalFilesCount = await loadMore(resetedPaginationInfo, newCriteria, true) - // if (totalFilesCount !== undefined) { - // const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalFilesCount) - // setPaginationInfo(paginationInfoUpdated) - // } + // This function is called when the user changes the search criteria (search input, filters, etc.) + const handleCriteriaChange = async (newCriteria: TemporarySearchCriteria) => { + itemsListContainerRef.current?.scrollTo({ top: 0 }) + + setSearchCriteria(newCriteria) + + const resetedPaginationInfo = new CollectionItemsPaginationInfo() + setPaginationInfo(resetedPaginationInfo) + + const totalItemsCount = await loadMore(resetedPaginationInfo, newCriteria, true) + if (totalItemsCount !== undefined) { + const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalItemsCount) + setPaginationInfo(paginationInfoUpdated) + } } useEffect(() => { @@ -106,12 +92,13 @@ export const CollectionItemsPanel = ({ items={accumulatedItems} error={error} accumulatedCount={accumulatedCount} + isLoadingItems={isLoadingItems} areItemsAvailable={areItemsAvailable} hasNextPage={hasNextPage} isEmptyItems={isEmptyItems} paginationInfo={paginationInfo} - rootRef={rootRef} - sentryRef={sentryRef} + onLoadMore={handleOnLoadMore} + ref={itemsListContainerRef} />
diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss index 8de160be9..53e8e3659 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss @@ -41,6 +41,9 @@ } ul { + display: flex; + flex-direction: column; + gap: 0.5rem; margin: 0; padding: 0; diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 9679335ff..d53c92fa0 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -1,7 +1,8 @@ +import { ForwardedRef, forwardRef } from 'react' import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' -import { UseInfiniteScrollHookRefCallback } from 'react-infinite-scroll-hook' +import useInfiniteScroll from 'react-infinite-scroll-hook' import cn from 'classnames' -import { CollectionItem } from '../../../../collection/domain/models/CollectionItemSubset' +import { type CollectionItem } from '../../../../collection/domain/models/CollectionItemSubset' import { CollectionItemsPaginationInfo } from '../../../../collection/domain/models/CollectionItemsPaginationInfo' import { PaginationResultsInfo } from '../../../shared/pagination/PaginationResultsInfo' import { ErrorItemsMessage } from './ErrorItemsMessage' @@ -13,66 +14,91 @@ interface ItemsListProps { items: CollectionItem[] error: string | null accumulatedCount: number + isLoadingItems: boolean areItemsAvailable: boolean hasNextPage: boolean isEmptyItems: boolean paginationInfo: CollectionItemsPaginationInfo - rootRef: any - sentryRef: UseInfiniteScrollHookRefCallback + onLoadMore: (paginationInfo: CollectionItemsPaginationInfo) => void } -export const ItemsList = ({ - items, - error, - accumulatedCount, - areItemsAvailable, - hasNextPage, - isEmptyItems, - paginationInfo, - rootRef, - sentryRef -}: ItemsListProps) => { - return ( -
- {isEmptyItems && } +export const ItemsList = forwardRef( + ( + { + items, + error, + accumulatedCount, + isLoadingItems, + areItemsAvailable, + hasNextPage, + isEmptyItems, + paginationInfo, + onLoadMore + }: ItemsListProps, + ref + ) => { + const [sentryRef, { rootRef }] = useInfiniteScroll({ + loading: isLoadingItems, + hasNextPage: hasNextPage, + onLoadMore: () => void onLoadMore(paginationInfo), + disabled: !!error, + rootMargin: '0px 0px 250px 0px' + }) + + return ( +
+
}> + {isEmptyItems && } + + {error && } + + {areItemsAvailable && ( + <> +
+ +
- {error && } + {/* TODO:ME After updating js-dataverse use case, assert by the type wich card to render */} +
    + {items.map((collectionItem, index) => { + console.log(collectionItem) - {areItemsAvailable && ( - <> -
    - -
    -
      - {items.map((collectionItem, index) => { - return ( -
    • -

      A collection item

      -
    • - ) - })} -
    - - )} + return ( +
  • +

    Assert type collection, dataset or file here

    +
  • + ) + })} +
+ + )} - {hasNextPage && !error && !isEmptyItems && ( -
- - {accumulatedCount === NO_COLLECTION_ITEMS && } - - + {hasNextPage && !error && !isEmptyItems && ( +
+ + {accumulatedCount === NO_COLLECTION_ITEMS && } + + +
+ )}
- )} -
- ) -} +
+ ) + } +) + +ItemsList.displayName = 'ItemsList' export const InitialLoadingSkeleton = () => ( <> diff --git a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx index b657e196f..3649a4496 100644 --- a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx +++ b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx @@ -1,5 +1,4 @@ import { useMemo, useState } from 'react' - import { getCollectionItems } from '../../../collection/domain/useCases/getCollectionItems' import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' import { @@ -31,13 +30,11 @@ type UseGetAccumulatedItemsReturnType = { type UseGetAccumulatedItemsParams = { collectionRepository: CollectionRepository collectionId: string - paginationInfo: CollectionItemsPaginationInfo } export const useGetAccumulatedItems = ({ collectionRepository, - collectionId, - paginationInfo + collectionId }: UseGetAccumulatedItemsParams): UseGetAccumulatedItemsReturnType => { const [isLoadingItems, setIsLoadingItems] = useState(false) const [accumulatedItems, setAccumulatedItems] = useState([]) @@ -111,11 +108,14 @@ async function loadNextItems( collectionRepository: CollectionRepository, collectionId: string, paginationInfo: CollectionItemsPaginationInfo, - _criteria: TemporarySearchCriteria + searchCriteria: TemporarySearchCriteria ): Promise { - return getCollectionItems(collectionRepository, collectionId, paginationInfo).catch( - (err: Error) => { - throw new Error(err.message) - } - ) + return getCollectionItems( + collectionRepository, + collectionId, + paginationInfo, + searchCriteria + ).catch((err: Error) => { + throw new Error(err.message) + }) } From 5c9d37187984edc72694e4af0fd00bcd5086ef81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 17 Sep 2024 10:39:36 -0300 Subject: [PATCH 10/42] feat: read query params from url to set initial search criteria --- package-lock.json | 8 +++--- package.json | 2 +- .../domain/models/CollectionItemType.ts | 5 ++++ .../models/CollectionSearchCriteria.tsx | 16 +++++++++++ .../repositories/CollectionRepository.ts | 4 +-- .../domain/useCases/getCollectionItems.ts | 4 +-- .../CollectionJSDataverseRepository.ts | 10 +++---- src/sections/Route.enum.ts | 4 ++- src/sections/collection/Collection.tsx | 6 ++-- src/sections/collection/CollectionFactory.tsx | 8 +++--- .../CollectionItemsPanel.tsx | 23 +++++++++++---- .../items-list/ItemsList.tsx | 6 +++- .../useGetAccumulatedItems.tsx | 9 +++--- .../collection/useCollectionQueryParams.ts | 28 +++++++++++++++++++ 14 files changed, 99 insertions(+), 34 deletions(-) create mode 100644 src/collection/domain/models/CollectionItemType.ts create mode 100644 src/collection/domain/models/CollectionSearchCriteria.tsx create mode 100644 src/sections/collection/useCollectionQueryParams.ts diff --git a/package-lock.json b/package-lock.json index 1c3c232d2..b8404adc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-pr181.e386829", + "@iqss/dataverse-client-javascript": "2.0.0-pr181.746affb", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -3674,9 +3674,9 @@ }, "node_modules/@iqss/dataverse-client-javascript": { "name": "@IQSS/dataverse-client-javascript", - "version": "2.0.0-pr181.e386829", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-pr181.e386829/c76266af444e6b34809eb9be644e1f4303c69878", - "integrity": "sha512-mMsYgteE3egYeB3CFP+hgHJ/ZfvCjM0ScZ7KvLS2aA9D27MGnDzqoX8Zf4OzX185YW4R6xg80s2SxCALquRqAg==", + "version": "2.0.0-pr181.746affb", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-pr181.746affb/4d6a6d19c1d9b4ffb260db63f97dfb992bbd8873", + "integrity": "sha512-HT2yHmg0Z1qRUWx6UDjD9o7OBqH++RpmwaHqXivd/GO8HikCA+t56qG0BmA1GiROO6jy+0zNNcetBcFKu7hbWQ==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", diff --git a/package.json b/package.json index 0b9df16ee..420985af5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-pr181.e386829", + "@iqss/dataverse-client-javascript": "2.0.0-pr181.746affb", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", diff --git a/src/collection/domain/models/CollectionItemType.ts b/src/collection/domain/models/CollectionItemType.ts new file mode 100644 index 000000000..a4343fc1f --- /dev/null +++ b/src/collection/domain/models/CollectionItemType.ts @@ -0,0 +1,5 @@ +export enum CollectionItemType { + FILE = 'file', + DATASET = 'dataset', + COLLECTION = 'collection' +} diff --git a/src/collection/domain/models/CollectionSearchCriteria.tsx b/src/collection/domain/models/CollectionSearchCriteria.tsx new file mode 100644 index 000000000..26590ae56 --- /dev/null +++ b/src/collection/domain/models/CollectionSearchCriteria.tsx @@ -0,0 +1,16 @@ +import { type CollectionItemType } from './CollectionItemType' + +export class CollectionSearchCriteria { + constructor( + public readonly searchText?: string, + public readonly itemTypes?: CollectionItemType[] + ) {} + + withSearchText(searchText: string | undefined): CollectionSearchCriteria { + return new CollectionSearchCriteria(searchText, this.itemTypes) + } + + withItemTypes(itemTypes: CollectionItemType[] | undefined): CollectionSearchCriteria { + return new CollectionSearchCriteria(this.searchText, itemTypes) + } +} diff --git a/src/collection/domain/repositories/CollectionRepository.ts b/src/collection/domain/repositories/CollectionRepository.ts index a62acd0e3..4d54cb2d2 100644 --- a/src/collection/domain/repositories/CollectionRepository.ts +++ b/src/collection/domain/repositories/CollectionRepository.ts @@ -1,8 +1,8 @@ -import { TemporarySearchCriteria } from '../../../sections/collection/collection-items-panel/useGetAccumulatedItems' import { Collection } from '../models/Collection' import { CollectionFacet } from '../models/CollectionFacet' import { CollectionItemsPaginationInfo } from '../models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../models/CollectionItemSubset' +import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' import { CollectionUserPermissions } from '../models/CollectionUserPermissions' import { CollectionDTO } from '../useCases/DTOs/CollectionDTO' @@ -14,6 +14,6 @@ export interface CollectionRepository { getItems( collectionId: string, paginationInfo: CollectionItemsPaginationInfo, - searchCriteria?: TemporarySearchCriteria + searchCriteria?: CollectionSearchCriteria ): Promise } diff --git a/src/collection/domain/useCases/getCollectionItems.ts b/src/collection/domain/useCases/getCollectionItems.ts index 2e99b948f..44b15c7eb 100644 --- a/src/collection/domain/useCases/getCollectionItems.ts +++ b/src/collection/domain/useCases/getCollectionItems.ts @@ -1,13 +1,13 @@ import { CollectionRepository } from '../repositories/CollectionRepository' import { CollectionItemsPaginationInfo } from '../models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../models/CollectionItemSubset' -import { TemporarySearchCriteria } from '../../../sections/collection/collection-items-panel/useGetAccumulatedItems' +import { CollectionSearchCriteria } from '../models/CollectionSearchCriteria' export async function getCollectionItems( collectionRepository: CollectionRepository, collectionId: string, paginationInfo: CollectionItemsPaginationInfo, - searchCriteria: TemporarySearchCriteria + searchCriteria: CollectionSearchCriteria ): Promise { return collectionRepository .getItems(collectionId, paginationInfo, searchCriteria) diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts index cef976696..17e7bdcd1 100644 --- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts +++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts @@ -13,7 +13,7 @@ import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' import { CollectionItemsPaginationInfo } from '../../domain/models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' -import { TemporarySearchCriteria } from '../../../sections/collection/collection-items-panel/useGetAccumulatedItems' +import { CollectionSearchCriteria } from '../../domain/models/CollectionSearchCriteria' export class CollectionJSDataverseRepository implements CollectionRepository { getById(id: string): Promise { @@ -41,13 +41,11 @@ export class CollectionJSDataverseRepository implements CollectionRepository { // TODO:ME After updating previews object to match the response models we should not see ts error anymore under the return keyword getItems( collectionId: string, - paginationInfo?: CollectionItemsPaginationInfo, - _searchCriteria?: TemporarySearchCriteria + paginationInfo: CollectionItemsPaginationInfo, + searchCriteria: CollectionSearchCriteria ): Promise { - console.log({ _searchCriteria }) - return getCollectionItems - .execute(collectionId, paginationInfo?.pageSize, paginationInfo?.offset) + .execute(collectionId, paginationInfo?.pageSize, paginationInfo?.offset, searchCriteria) .then((jsCollectionItemSubset) => jsCollectionItemSubset) } } diff --git a/src/sections/Route.enum.ts b/src/sections/Route.enum.ts index 93626237f..677b9ed52 100644 --- a/src/sections/Route.enum.ts +++ b/src/sections/Route.enum.ts @@ -23,5 +23,7 @@ export const RouteWithParams = { export enum QueryParamKey { VERSION = 'version', PERSISTENT_ID = 'persistentId', - QUERY = 'q' + QUERY = 'q', + COLLECTION_ITEM_TYPES = 'types', + PAGE = 'page' } diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 9cb78e5a2..3f13382b1 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -5,6 +5,7 @@ import { useCollection } from './useCollection' import { useScrollTop } from '../../shared/hooks/useScrollTop' import { useSession } from '../session/SessionContext' import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions' +import { type UseCollectionQueryParamsReturnType } from './useCollectionQueryParams' import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' import AddDataActionsButton from '../shared/add-data-actions/AddDataActionsButton' import { CollectionItemsPanel } from './collection-items-panel/CollectionItemsPanel' @@ -16,7 +17,7 @@ interface CollectionProps { collectionRepository: CollectionRepository collectionId: string created: boolean - page?: number + collectionQueryParams: UseCollectionQueryParamsReturnType infiniteScrollEnabled?: boolean } @@ -24,7 +25,7 @@ export function Collection({ collectionId, collectionRepository, created, - page, + collectionQueryParams, infiniteScrollEnabled = false }: CollectionProps) { useScrollTop() @@ -78,6 +79,7 @@ export function Collection({ key={collectionId} collectionId={collectionId} collectionRepository={collectionRepository} + collectionQueryParams={collectionQueryParams} addDataSlot={ showAddDataActions ? ( () const location = useLocation() - const page = searchParams.get('page') ? parseInt(searchParams.get('page') as string) : undefined const state = location.state as { created: boolean } | undefined const created = state?.created ?? false return ( ) diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index c6af480d5..e8edb5b9c 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -1,29 +1,40 @@ import { useEffect, useRef, useState } from 'react' import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' -import { TemporarySearchCriteria, useGetAccumulatedItems } from './useGetAccumulatedItems' +import { CollectionSearchCriteria } from '../../../collection/domain/models/CollectionSearchCriteria' +import { useGetAccumulatedItems } from './useGetAccumulatedItems' +import { UseCollectionQueryParamsReturnType } from '../useCollectionQueryParams' import { useLoading } from '../../loading/LoadingContext' import { FilterPanel } from './filter-panel/FilterPanel' import { ItemsList } from './items-list/ItemsList' import { SearchPanel } from './search-panel/SearchPanel' import styles from './CollectionItemsPanel.module.scss' -const PAGE_SIZE = 10 -const INITIAL_PAGE = 1 - interface ItemsPanelProps { collectionId: string collectionRepository: CollectionRepository + collectionQueryParams: UseCollectionQueryParamsReturnType addDataSlot: JSX.Element | null } export const CollectionItemsPanel = ({ collectionId, collectionRepository, + collectionQueryParams, addDataSlot }: ItemsPanelProps) => { const { setIsLoading } = useLoading() - const [searchCriteria, setSearchCriteria] = useState({}) + + const initialSearchCriteria = new CollectionSearchCriteria( + collectionQueryParams.searchQuery, + collectionQueryParams.typesQuery + ) + + console.log({ initialSearchCriteria }) + + const [searchCriteria, setSearchCriteria] = + useState(initialSearchCriteria) + const [paginationInfo, setPaginationInfo] = useState( new CollectionItemsPaginationInfo() ) @@ -59,7 +70,7 @@ export const CollectionItemsPanel = ({ } // This function is called when the user changes the search criteria (search input, filters, etc.) - const handleCriteriaChange = async (newCriteria: TemporarySearchCriteria) => { + const handleCriteriaChange = async (newCriteria: CollectionSearchCriteria) => { itemsListContainerRef.current?.scrollTo({ top: 0 }) setSearchCriteria(newCriteria) diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index d53c92fa0..b995b465a 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -54,6 +54,10 @@ export const ItemsList = forwardRef( ref={ref as ForwardedRef}> {isEmptyItems && } + {/* TODO:ME If there is a type or search applied, then message should not be no items message */} + {/* There are no dataverses, datasets, or files that match your search. Please try a new search by using other or broader terms. You can also check out the [search guide] for tips. */} + {/* https://guides.dataverse.org/en/latest/user/find-use-data.html */} + {error && } {areItemsAvailable && ( @@ -68,7 +72,7 @@ export const ItemsList = forwardRef( {/* TODO:ME After updating js-dataverse use case, assert by the type wich card to render */}
    {items.map((collectionItem, index) => { - console.log(collectionItem) + // console.log(collectionItem) return (
  • Promise isEmptyItems: boolean @@ -50,7 +49,7 @@ export const useGetAccumulatedItems = ({ const loadMore = async ( pagination: CollectionItemsPaginationInfo, - criteria: TemporarySearchCriteria, + criteria: CollectionSearchCriteria, resetAccumulated = false ): Promise => { setIsLoadingItems(true) @@ -108,7 +107,7 @@ async function loadNextItems( collectionRepository: CollectionRepository, collectionId: string, paginationInfo: CollectionItemsPaginationInfo, - searchCriteria: TemporarySearchCriteria + searchCriteria: CollectionSearchCriteria ): Promise { return getCollectionItems( collectionRepository, diff --git a/src/sections/collection/useCollectionQueryParams.ts b/src/sections/collection/useCollectionQueryParams.ts new file mode 100644 index 000000000..1d49f8412 --- /dev/null +++ b/src/sections/collection/useCollectionQueryParams.ts @@ -0,0 +1,28 @@ +import { useSearchParams } from 'react-router-dom' +import { QueryParamKey } from '../Route.enum' +import { CollectionItemType } from '../../collection/domain/models/CollectionItemType' + +export interface UseCollectionQueryParamsReturnType { + pageQuery: number + searchQuery?: string + typesQuery?: CollectionItemType[] +} + +export const useCollectionQueryParams = (): UseCollectionQueryParamsReturnType => { + const [searchParams] = useSearchParams() + + const pageQuery = searchParams.get('page') ? parseInt(searchParams.get('page') as string, 10) : 1 + + const searchQuery = searchParams.get(QueryParamKey.QUERY) ?? undefined + + const typesParam = searchParams.get(QueryParamKey.COLLECTION_ITEM_TYPES) ?? undefined + + const typesQuery = typesParam + ?.split(',') + .map((type) => decodeURIComponent(type)) + .filter((type) => + Object.values(CollectionItemType).includes(type as CollectionItemType) + ) as CollectionItemType[] + + return { pageQuery, searchQuery, typesQuery } +} From 5f7b9d67535c9a2ac26f3ba081f47fc9560e07bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 17 Sep 2024 11:01:19 -0300 Subject: [PATCH 11/42] feat: no search matches message --- public/locales/en/collection.json | 1 + .../models/CollectionSearchCriteria.tsx | 4 ++++ .../CollectionItemsPanel.tsx | 1 + .../items-list/ItemsList.module.scss | 2 +- .../items-list/ItemsList.tsx | 10 ++++---- .../items-list/NoItemsMessage.tsx | 2 +- .../items-list/NoSearchMatchesMessage.tsx | 24 +++++++++++++++++++ 7 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 src/sections/collection/collection-items-panel/items-list/NoSearchMatchesMessage.tsx diff --git a/public/locales/en/collection.json b/public/locales/en/collection.json index 8be9be719..026e3f87c 100644 --- a/public/locales/en/collection.json +++ b/public/locales/en/collection.json @@ -7,5 +7,6 @@ "authenticated": "This collection currently has no collections, datasets or files. You can add to it by using the Add Data button on this page.", "anonymous": "This collection currently has no collections, datasets or files. Please <1>log in to see if you are able to add to it." }, + "noSearchMatches": "There are no collections, datasets, or files that match your search. Please try a new search by using other or broader terms. You can also check out the search guide for tips.", "createdAlert": "You have successfully created your collection! To learn more about what you can do with your collection, check out the User Guide." } diff --git a/src/collection/domain/models/CollectionSearchCriteria.tsx b/src/collection/domain/models/CollectionSearchCriteria.tsx index 26590ae56..247439c7d 100644 --- a/src/collection/domain/models/CollectionSearchCriteria.tsx +++ b/src/collection/domain/models/CollectionSearchCriteria.tsx @@ -13,4 +13,8 @@ export class CollectionSearchCriteria { withItemTypes(itemTypes: CollectionItemType[] | undefined): CollectionSearchCriteria { return new CollectionSearchCriteria(this.searchText, itemTypes) } + + hasSearchText(): boolean { + return !!this.searchText + } } diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index e8edb5b9c..840ad6a5a 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -107,6 +107,7 @@ export const CollectionItemsPanel = ({ areItemsAvailable={areItemsAvailable} hasNextPage={hasNextPage} isEmptyItems={isEmptyItems} + hasSearchValue={searchCriteria.hasSearchText()} paginationInfo={paginationInfo} onLoadMore={handleOnLoadMore} ref={itemsListContainerRef} diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss index 53e8e3659..d5d52eaaf 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss @@ -24,7 +24,7 @@ padding-block: var(--inline-padding); } - .empty-message-container { + .custom-message-container { padding: 0.5em 1em; background: $dv-warning-box-color; } diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index b995b465a..f5772db2f 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -7,6 +7,7 @@ import { CollectionItemsPaginationInfo } from '../../../../collection/domain/mod import { PaginationResultsInfo } from '../../../shared/pagination/PaginationResultsInfo' import { ErrorItemsMessage } from './ErrorItemsMessage' import { NoItemsMessage } from './NoItemsMessage' +import { NoSearchMatchesMessage } from './NoSearchMatchesMessage' import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' import styles from './ItemsList.module.scss' @@ -18,6 +19,7 @@ interface ItemsListProps { areItemsAvailable: boolean hasNextPage: boolean isEmptyItems: boolean + hasSearchValue: boolean paginationInfo: CollectionItemsPaginationInfo onLoadMore: (paginationInfo: CollectionItemsPaginationInfo) => void } @@ -32,6 +34,7 @@ export const ItemsList = forwardRef( areItemsAvailable, hasNextPage, isEmptyItems, + hasSearchValue, paginationInfo, onLoadMore }: ItemsListProps, @@ -52,11 +55,8 @@ export const ItemsList = forwardRef( [styles['empty-or-error']]: isEmptyItems || error })} ref={ref as ForwardedRef}> - {isEmptyItems && } - - {/* TODO:ME If there is a type or search applied, then message should not be no items message */} - {/* There are no dataverses, datasets, or files that match your search. Please try a new search by using other or broader terms. You can also check out the [search guide] for tips. */} - {/* https://guides.dataverse.org/en/latest/user/find-use-data.html */} + {isEmptyItems && !hasSearchValue && } + {isEmptyItems && hasSearchValue && } {error && } diff --git a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx index bc200a8f0..0a497f438 100644 --- a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx +++ b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx @@ -8,7 +8,7 @@ export function NoItemsMessage() { const { user } = useSession() return ( -
    +
    {user ? (

    {t('noItemsMessage.authenticated')}

    ) : ( diff --git a/src/sections/collection/collection-items-panel/items-list/NoSearchMatchesMessage.tsx b/src/sections/collection/collection-items-panel/items-list/NoSearchMatchesMessage.tsx new file mode 100644 index 000000000..ded2e1726 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/NoSearchMatchesMessage.tsx @@ -0,0 +1,24 @@ +import { Trans, useTranslation } from 'react-i18next' +import styles from './ItemsList.module.scss' + +export const NoSearchMatchesMessage = () => { + const { t } = useTranslation('collection') + + return ( +
    + + ) + }} + /> +
    + ) +} From 50a7c49153f0ce3040df22289f15ff2a3102e7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 18 Sep 2024 02:03:28 -0300 Subject: [PATCH 12/42] feat: sync url query params --- src/sections/collection/Collection.tsx | 26 +--- src/sections/collection/CollectionFactory.tsx | 4 +- src/sections/collection/CreatedAlert.tsx | 23 ++++ .../CollectionItemsPanel.tsx | 114 +++++++++++++++--- .../CollectionsItemsPanelHelper.ts | 7 ++ .../filter-panel/FilterPanel.tsx | 21 +++- .../type-filters/TypeFilters.module.scss | 16 ++- .../filter-panel/type-filters/TypeFilters.tsx | 67 ++++++++-- .../items-list/ItemsList.tsx | 7 +- .../search-panel/SearchPanel.tsx | 31 +++-- .../useGetAccumulatedItems.tsx | 4 +- ...rams.ts => useGetCollectionQueryParams.ts} | 6 +- 12 files changed, 247 insertions(+), 79 deletions(-) create mode 100644 src/sections/collection/CreatedAlert.tsx create mode 100644 src/sections/collection/collection-items-panel/CollectionsItemsPanelHelper.ts rename src/sections/collection/{useCollectionQueryParams.ts => useGetCollectionQueryParams.ts} (77%) diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 3f13382b1..e8b554350 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -1,17 +1,17 @@ -import { Trans, useTranslation } from 'react-i18next' -import { Alert, Col, Row } from '@iqss/dataverse-design-system' +import { Col, Row } from '@iqss/dataverse-design-system' import { CollectionRepository } from '../../collection/domain/repositories/CollectionRepository' import { useCollection } from './useCollection' import { useScrollTop } from '../../shared/hooks/useScrollTop' import { useSession } from '../session/SessionContext' import { useGetCollectionUserPermissions } from '../../shared/hooks/useGetCollectionUserPermissions' -import { type UseCollectionQueryParamsReturnType } from './useCollectionQueryParams' +import { type UseCollectionQueryParamsReturnType } from './useGetCollectionQueryParams' import { BreadcrumbsGenerator } from '../shared/hierarchy/BreadcrumbsGenerator' import AddDataActionsButton from '../shared/add-data-actions/AddDataActionsButton' import { CollectionItemsPanel } from './collection-items-panel/CollectionItemsPanel' import { CollectionInfo } from './CollectionInfo' import { CollectionSkeleton } from './CollectionSkeleton' import { PageNotFound } from '../page-not-found/PageNotFound' +import { CreatedAlert } from './CreatedAlert' interface CollectionProps { collectionRepository: CollectionRepository @@ -41,8 +41,6 @@ export function Collection({ const showAddDataActions = Boolean(user && (canUserAddCollection || canUserAddDataset)) - const { t } = useTranslation('collection') - if (!isLoading && !collection) { return } @@ -56,23 +54,7 @@ export function Collection({ <> - {created && ( - - - ) - }} - /> - - )} + {created && } )} () const location = useLocation() const state = location.state as { created: boolean } | undefined diff --git a/src/sections/collection/CreatedAlert.tsx b/src/sections/collection/CreatedAlert.tsx new file mode 100644 index 000000000..6e33f6904 --- /dev/null +++ b/src/sections/collection/CreatedAlert.tsx @@ -0,0 +1,23 @@ +import { Alert } from '@iqss/dataverse-design-system' +import { Trans, useTranslation } from 'react-i18next' + +export const CreatedAlert = () => { + const { t } = useTranslation('collection') + return ( + + + ) + }} + /> + + ) +} diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 840ad6a5a..120b830a2 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -1,16 +1,20 @@ import { useEffect, useRef, useState } from 'react' +import { useSearchParams } from 'react-router-dom' import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' import { CollectionSearchCriteria } from '../../../collection/domain/models/CollectionSearchCriteria' import { useGetAccumulatedItems } from './useGetAccumulatedItems' -import { UseCollectionQueryParamsReturnType } from '../useCollectionQueryParams' +import { UseCollectionQueryParamsReturnType } from '../useGetCollectionQueryParams' import { useLoading } from '../../loading/LoadingContext' import { FilterPanel } from './filter-panel/FilterPanel' import { ItemsList } from './items-list/ItemsList' import { SearchPanel } from './search-panel/SearchPanel' +import { QueryParamKey } from '../../Route.enum' +import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType' +import { ItemTypeChange } from './filter-panel/type-filters/TypeFilters' import styles from './CollectionItemsPanel.module.scss' -interface ItemsPanelProps { +interface CollectionItemsPanelProps { collectionId: string collectionRepository: CollectionRepository collectionQueryParams: UseCollectionQueryParamsReturnType @@ -22,19 +26,20 @@ export const CollectionItemsPanel = ({ collectionRepository, collectionQueryParams, addDataSlot -}: ItemsPanelProps) => { +}: CollectionItemsPanelProps) => { const { setIsLoading } = useLoading() + const [_, setSearchParams] = useSearchParams() - const initialSearchCriteria = new CollectionSearchCriteria( + // This object will update every time we update a query param in the URL with the setSearchParams setter + const currentSearchCriteria = new CollectionSearchCriteria( collectionQueryParams.searchQuery, - collectionQueryParams.typesQuery + collectionQueryParams.typesQuery || [CollectionItemType.COLLECTION, CollectionItemType.DATASET] ) - console.log({ initialSearchCriteria }) - - const [searchCriteria, setSearchCriteria] = - useState(initialSearchCriteria) - + /* + TODO:ME For now I think we really shouldnt care about setting an inital page based on the page query param + Items in specific page of a list could change while browsing at different times so is not so useful for url sharing. + */ const [paginationInfo, setPaginationInfo] = useState( new CollectionItemsPaginationInfo() ) @@ -60,8 +65,8 @@ export const CollectionItemsPanel = ({ if (totalAvailable !== undefined) { paginationInfoToSend = currentPagination.goToNextPage() } - console.log('paginationInfoToSend', paginationInfoToSend) - const totalItemsCount = await loadMore(paginationInfoToSend, searchCriteria) + + const totalItemsCount = await loadMore(paginationInfoToSend, currentSearchCriteria) if (totalItemsCount !== undefined) { const paginationInfoUpdated = paginationInfoToSend.withTotal(totalItemsCount) @@ -69,16 +74,77 @@ export const CollectionItemsPanel = ({ } } - // This function is called when the user changes the search criteria (search input, filters, etc.) - const handleCriteriaChange = async (newCriteria: CollectionSearchCriteria) => { + const handleSearchSubmit = async (searchValue: string) => { itemsListContainerRef.current?.scrollTo({ top: 0 }) - setSearchCriteria(newCriteria) + const resetedPaginationInfo = new CollectionItemsPaginationInfo() + setPaginationInfo(resetedPaginationInfo) + + if (searchValue === '') { + // Update the URL without the search value, keep other querys + setSearchParams((currentSearchParams) => { + currentSearchParams.delete(QueryParamKey.QUERY) + return currentSearchParams + }) + } else { + // Update the URL with the search value ,keep other querys and include all item types always + setSearchParams((currentSearchParams) => ({ + ...currentSearchParams, + [QueryParamKey.COLLECTION_ITEM_TYPES]: [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ].join(','), + [QueryParamKey.QUERY]: searchValue + })) + } + + // WHEN SEARCHING, WE RESET THE PAGINATION INFO AND KEEP ALL ITEM TYPES!! + const newCollectionSearchCriteria = new CollectionSearchCriteria( + searchValue === '' ? undefined : searchValue, + [CollectionItemType.COLLECTION, CollectionItemType.DATASET, CollectionItemType.FILE] + ) + + const totalItemsCount = await loadMore(resetedPaginationInfo, newCollectionSearchCriteria, true) + + if (totalItemsCount !== undefined) { + const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalItemsCount) + setPaginationInfo(paginationInfoUpdated) + } + } + + // WHEN APPLYING FILTERS, WE RESET THE PAGINATION INFO AND IF SEARCH VALUE EXISTS, WE KEEP IT!! + const handleItemsTypeChange = async (itemTypeChange: ItemTypeChange) => { + console.log({ itemTypeChange }) + const { type, checked } = itemTypeChange + + const newItemsTypes = checked + ? [...new Set([...(currentSearchCriteria?.itemTypes ?? []), type])] + : (currentSearchCriteria.itemTypes ?? []).filter((itemType) => itemType !== type) + + console.log({ newItemsTypes }) + // KEEP SEARCH VALUE IF EXISTS + itemsListContainerRef.current?.scrollTo({ top: 0 }) const resetedPaginationInfo = new CollectionItemsPaginationInfo() setPaginationInfo(resetedPaginationInfo) - const totalItemsCount = await loadMore(resetedPaginationInfo, newCriteria, true) + // Update the URL with the new item types, keep other querys and include the search value if exists + setSearchParams((currentSearchParams) => ({ + ...currentSearchParams, + [QueryParamKey.COLLECTION_ITEM_TYPES]: newItemsTypes.join(','), + ...(currentSearchCriteria.searchText && { + [QueryParamKey.QUERY]: currentSearchCriteria.searchText + }) + })) + + const newCollectionSearchCriteria = new CollectionSearchCriteria( + currentSearchCriteria.searchText, + newItemsTypes + ) + + const totalItemsCount = await loadMore(resetedPaginationInfo, newCollectionSearchCriteria, true) + if (totalItemsCount !== undefined) { const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalItemsCount) setPaginationInfo(paginationInfoUpdated) @@ -92,12 +158,20 @@ export const CollectionItemsPanel = ({ return (
    - +
    {addDataSlot}
    - + { +interface FilterPanelProps { + currentItemTypes?: CollectionItemType[] + onItemTypesChange: (itemTypeChange: ItemTypeChange) => void + isLoadingCollectionItems: boolean +} + +export const FilterPanel = ({ + currentItemTypes, + onItemTypesChange, + isLoadingCollectionItems +}: FilterPanelProps) => { const [showOffcanvas, setShowOffcanvas] = useState(false) const handleCloseOffcanvas = () => setShowOffcanvas(false) @@ -26,7 +37,11 @@ export const FilterPanel = () => {
    - +
    diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss index 6f8131091..49b4d3a62 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss @@ -4,13 +4,21 @@ input[type='checkbox'] { cursor: pointer; + & + label { + color: $dv-primary-color; + cursor: pointer; + } + &:checked + label { font-weight: 500; } - } - .label-content-wrapper { - color: $dv-primary-color; - cursor: pointer; + &:disabled { + opacity: 0.7; + + & + label { + opacity: 0.7; + } + } } } diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx index 54f851b30..ac6ac96d9 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -1,42 +1,83 @@ import { ChangeEvent } from 'react' import { Form, Icon, IconName, Stack } from '@iqss/dataverse-design-system' import styles from './TypeFilters.module.scss' +import { CollectionItemType } from '../../../../../collection/domain/models/CollectionItemType' + +interface TypeFiltersProps { + currentItemTypes?: CollectionItemType[] + onItemTypesChange: (itemTypeChange: ItemTypeChange) => void + isLoadingCollectionItems: boolean +} + +export interface ItemTypeChange { + type: CollectionItemType + checked: boolean +} + +export const TypeFilters = ({ + currentItemTypes, + onItemTypesChange, + isLoadingCollectionItems +}: TypeFiltersProps) => { + const handleItemTypeChange = (type: CollectionItemType, checked: boolean) => { + onItemTypesChange({ type, checked }) + } + + const collectionCheckDisabled = + isLoadingCollectionItems || + (currentItemTypes?.length === 1 && currentItemTypes?.includes(CollectionItemType.COLLECTION)) + + const datasetCheckDisabled = + isLoadingCollectionItems || + (currentItemTypes?.length === 1 && currentItemTypes?.includes(CollectionItemType.DATASET)) + + const fileCheckDisabled = + isLoadingCollectionItems || + (currentItemTypes?.length === 1 && currentItemTypes?.includes(CollectionItemType.FILE)) -export const TypeFilters = () => { return ( ) => console.log(e.target.checked)} + onChange={(e: ChangeEvent) => + handleItemTypeChange(CollectionItemType.COLLECTION, e.target.checked) + } label={ - + <> Collections (19) - + } - // checked={Boolean(value as boolean)} // TODO:ME Handle this and other filter states in a useReducer + checked={Boolean(currentItemTypes?.includes(CollectionItemType.COLLECTION))} + disabled={collectionCheckDisabled} /> ) => console.log(e.target.checked)} + onChange={(e: ChangeEvent) => + handleItemTypeChange(CollectionItemType.DATASET, e.target.checked) + } label={ - + <> Datasets (32) - + } - // checked={Boolean(value as boolean)} + checked={Boolean(currentItemTypes?.includes(CollectionItemType.DATASET))} + disabled={datasetCheckDisabled} /> ) => console.log(e.target.checked)} + onChange={(e: ChangeEvent) => + handleItemTypeChange(CollectionItemType.FILE, e.target.checked) + } label={ - + <> Files (10,081) - + } - // checked={Boolean(value as boolean)} + checked={Boolean(currentItemTypes?.includes(CollectionItemType.FILE))} + disabled={fileCheckDisabled} /> ) diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index f5772db2f..022d1acfd 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -63,6 +63,7 @@ export const ItemsList = forwardRef( {areItemsAvailable && ( <>
    + {/* TODO:ME Maybe show skeleton while loading or prevent 0 in total results somehow */} -

    Assert type collection, dataset or file here

    +

    + This is a : {collectionItem?.type === 'file' && 'File'} + {collectionItem?.type === 'dataset' && 'Dataset'} + {collectionItem?.type === 'collection' && 'Collection'} +

  • ) })} diff --git a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx index 5d3d8861b..3506f55f4 100644 --- a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx +++ b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx @@ -1,25 +1,29 @@ import { useState } from 'react' import { Button, Form } from '@iqss/dataverse-design-system' import { Search } from 'react-bootstrap-icons' -import { QueryParamKey, Route } from '../../../Route.enum' import styles from './SearchPanel.module.scss' -export const SearchPanel = () => { - const [searchValue, setSearchValue] = useState('') +interface SearchPanelProps { + initialSearchValue?: string + isLoadingCollectionItems: boolean + onSubmitSearch: (searchValue: string) => void +} + +export const SearchPanel = ({ + initialSearchValue = '', + isLoadingCollectionItems, + onSubmitSearch +}: SearchPanelProps) => { + const [searchValue, setSearchValue] = useState(initialSearchValue) const handleSubmit = (event: React.FormEvent) => { event.preventDefault() const trimmedSearchValue = searchValue.trim() - if (!trimmedSearchValue) return - - console.log({ trimmedSearchValue }) + const encodedSearchValue = encodeURIComponent(trimmedSearchValue) - // const encodedSearchValue = encodeURIComponent(trimmedSearchValue) - - // const searchParams = new URLSearchParams() - // searchParams.set(QueryParamKey.QUERY, encodedSearchValue) + onSubmitSearch(encodedSearchValue) } const handleSearchChange = (event: React.ChangeEvent) => { @@ -37,7 +41,12 @@ export const SearchPanel = () => { value={searchValue} onChange={handleSearchChange} /> -
diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss index 49b4d3a62..ef546289e 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.module.scss @@ -17,7 +17,7 @@ opacity: 0.7; & + label { - opacity: 0.7; + opacity: 1; } } } diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx index ac6ac96d9..415da1516 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -1,7 +1,7 @@ import { ChangeEvent } from 'react' import { Form, Icon, IconName, Stack } from '@iqss/dataverse-design-system' -import styles from './TypeFilters.module.scss' import { CollectionItemType } from '../../../../../collection/domain/models/CollectionItemType' +import styles from './TypeFilters.module.scss' interface TypeFiltersProps { currentItemTypes?: CollectionItemType[] diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 022d1acfd..cb983011e 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -21,7 +21,7 @@ interface ItemsListProps { isEmptyItems: boolean hasSearchValue: boolean paginationInfo: CollectionItemsPaginationInfo - onLoadMore: (paginationInfo: CollectionItemsPaginationInfo) => void + onBottomReach: (paginationInfo: CollectionItemsPaginationInfo) => void } export const ItemsList = forwardRef( @@ -36,18 +36,24 @@ export const ItemsList = forwardRef( isEmptyItems, hasSearchValue, paginationInfo, - onLoadMore + onBottomReach }: ItemsListProps, ref ) => { const [sentryRef, { rootRef }] = useInfiniteScroll({ loading: isLoadingItems, hasNextPage: hasNextPage, - onLoadMore: () => void onLoadMore(paginationInfo), + onLoadMore: () => void onBottomReach(paginationInfo), disabled: !!error, rootMargin: '0px 0px 250px 0px' }) + const showNoItemsMessage = !isLoadingItems && isEmptyItems && !hasSearchValue + const showNoSearchMatchesMessage = !isLoadingItems && isEmptyItems && hasSearchValue + + const showSentrySkeleton = hasNextPage && !error && !isEmptyItems + const showNotSentrySkeleton = isLoadingItems && isEmptyItems + return (
}> - {isEmptyItems && !hasSearchValue && } - {isEmptyItems && hasSearchValue && } + {showNoItemsMessage && } + {showNoSearchMatchesMessage && } {error && } {areItemsAvailable && ( <>
- {/* TODO:ME Maybe show skeleton while loading or prevent 0 in total results somehow */} - + {isLoadingItems ? ( + + + + ) : ( + + )}
{/* TODO:ME After updating js-dataverse use case, assert by the type wich card to render */} @@ -93,14 +104,19 @@ export const ItemsList = forwardRef( )} - {hasNextPage && !error && !isEmptyItems && ( + {showSentrySkeleton && (
{accumulatedCount === NO_COLLECTION_ITEMS && } - +
)} + {showNotSentrySkeleton && ( + + + + )}
) @@ -126,10 +142,10 @@ export const InitialLoadingSkeleton = () => ( ) -export const LoadingSkeleton = () => ( +export const LoadingSkeleton = ({ numOfSkeletons }: { numOfSkeletons: number }) => ( <> - - - + {Array.from({ length: numOfSkeletons }).map((_, index) => ( + + ))} ) From 50eb5143c573651e46a8e675ad349742cdfcc43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 18 Sep 2024 10:17:28 -0300 Subject: [PATCH 14/42] feat(design system): type search to form input --- packages/design-system/CHANGELOG.md | 2 +- .../form/form-group/form-element/FormInput.tsx | 2 +- .../design-system/src/lib/stories/form/Form.stories.tsx | 9 +++++++++ .../collection-items-panel/search-panel/SearchPanel.tsx | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/design-system/CHANGELOG.md b/packages/design-system/CHANGELOG.md index 60426fc19..fbbd06469 100644 --- a/packages/design-system/CHANGELOG.md +++ b/packages/design-system/CHANGELOG.md @@ -42,7 +42,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline - **NavbarDropdownItem:** Now accepts `as` prop and takes `as` Element props. - **FormInputGroup:** extend Props Interface to accept `hasValidation` prop to properly show rounded corners in an with validation - **Button:** extend Props Interface to accept `size` prop. -- **FormInput:** extend Props Interface to accept `autoFocus` prop. +- **FormInput:** extend Props Interface to accept `autoFocus` and `type: 'search'` prop. - **FormTextArea:** extend Props Interface to accept `autoFocus` prop. - **FormSelect:** extend Props Interface to accept `autoFocus` prop. - **Stack:** NEW Stack element to manage layouts. diff --git a/packages/design-system/src/lib/components/form/form-group/form-element/FormInput.tsx b/packages/design-system/src/lib/components/form/form-group/form-element/FormInput.tsx index 106a77110..a4a81daa6 100644 --- a/packages/design-system/src/lib/components/form/form-group/form-element/FormInput.tsx +++ b/packages/design-system/src/lib/components/form/form-group/form-element/FormInput.tsx @@ -4,7 +4,7 @@ import * as React from 'react' export type FormInputElement = HTMLInputElement | HTMLTextAreaElement export interface FormInputProps extends React.HTMLAttributes { - type?: 'text' | 'email' | 'password' + type?: 'text' | 'email' | 'password' | 'search' readOnly?: boolean name?: string isValid?: boolean diff --git a/packages/design-system/src/lib/stories/form/Form.stories.tsx b/packages/design-system/src/lib/stories/form/Form.stories.tsx index c8ebb26f6..3a41e0dcd 100644 --- a/packages/design-system/src/lib/stories/form/Form.stories.tsx +++ b/packages/design-system/src/lib/stories/form/Form.stories.tsx @@ -78,6 +78,15 @@ export const AllInputTypes: Story = { + + + + Search something + + + + + ) } diff --git a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx index 3506f55f4..405db8fa5 100644 --- a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx +++ b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx @@ -35,7 +35,7 @@ export const SearchPanel = ({
Date: Thu, 19 Sep 2024 12:12:46 -0300 Subject: [PATCH 15/42] feat: map and render collections cards done --- .../domain/models/CollectionPreview.ts | 15 +-- .../mappers/JSCollectionPreviewMapper.ts | 24 ++++ .../CollectionJSDataverseRepository.ts | 18 ++- .../items-list/ItemsList.module.scss | 4 +- .../items-list/ItemsList.tsx | 26 +++- .../search-panel/SearchPanel.tsx | 4 +- .../CollectionCard.module.scss | 112 +++++++++++------- .../collection-card/CollectionCard.tsx | 11 +- .../collection-card/CollectionCardHeader.tsx | 42 ++++--- .../collection-card/CollectionCardInfo.tsx | 31 +++-- .../CollectionCardThumbnail.tsx | 10 +- .../core/domain/models/PublicationStatus.ts | 5 + 12 files changed, 194 insertions(+), 108 deletions(-) create mode 100644 src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts create mode 100644 src/shared/core/domain/models/PublicationStatus.ts diff --git a/src/collection/domain/models/CollectionPreview.ts b/src/collection/domain/models/CollectionPreview.ts index af1ac9127..d7922bdb4 100644 --- a/src/collection/domain/models/CollectionPreview.ts +++ b/src/collection/domain/models/CollectionPreview.ts @@ -1,13 +1,14 @@ -// TODO:ME Once 181 is merged, update interface +import { CollectionItemType } from './CollectionItemType' export interface CollectionPreview { - id: string - name: string + type: CollectionItemType.COLLECTION isReleased: boolean + name: string + alias: string + description?: string // it could be undefined before ? + affiliation?: string // it could be undefined before ? releaseOrCreateDate: Date - parentCollectionId?: string - parentCollectionName?: string - description?: string - affiliation?: string thumbnail?: string + parentCollectionName: string + parentCollectionAlias: string } diff --git a/src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts b/src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts new file mode 100644 index 000000000..ee8d604a5 --- /dev/null +++ b/src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts @@ -0,0 +1,24 @@ +import { CollectionPreview as JSCollectionPreview } from '@iqss/dataverse-client-javascript' +import { CollectionPreview } from '../../domain/models/CollectionPreview' +import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' + +export class JSCollectionPreviewMapper { + static toCollectionPreview(jsDatasetPreview: JSCollectionPreview): CollectionPreview { + return { + type: jsDatasetPreview.type, + isReleased: JSCollectionPreviewMapper.toIsRelased(jsDatasetPreview.publicationStatuses), + name: jsDatasetPreview.name, + alias: jsDatasetPreview.alias, + description: jsDatasetPreview.description, + affiliation: jsDatasetPreview.affiliation, + releaseOrCreateDate: jsDatasetPreview.releaseOrCreateDate, + thumbnail: jsDatasetPreview.imageUrl, + parentCollectionName: jsDatasetPreview.parentName, + parentCollectionAlias: jsDatasetPreview.parentAlias + } + } + + static toIsRelased(jsPublicationStatus: PublicationStatus[]): boolean { + return jsPublicationStatus.includes(PublicationStatus.Published) + } +} diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts index 17e7bdcd1..1c7c2636c 100644 --- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts +++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts @@ -8,12 +8,14 @@ import { getCollectionItems } from '@iqss/dataverse-client-javascript' import { JSCollectionMapper } from '../mappers/JSCollectionMapper' +import { JSCollectionPreviewMapper } from '../mappers/JSCollectionPreviewMapper' import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO' import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' import { CollectionItemsPaginationInfo } from '../../domain/models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' import { CollectionSearchCriteria } from '../../domain/models/CollectionSearchCriteria' +import { CollectionItemType } from '../../domain/models/CollectionItemType' export class CollectionJSDataverseRepository implements CollectionRepository { getById(id: string): Promise { @@ -46,6 +48,20 @@ export class CollectionJSDataverseRepository implements CollectionRepository { ): Promise { return getCollectionItems .execute(collectionId, paginationInfo?.pageSize, paginationInfo?.offset, searchCriteria) - .then((jsCollectionItemSubset) => jsCollectionItemSubset) + .then((jsCollectionItemSubset) => { + const collectionPreviewsMapped = jsCollectionItemSubset.items + .filter((item) => item.type === CollectionItemType.COLLECTION) + .map((item) => JSCollectionPreviewMapper.toCollectionPreview(item)) + + // TODO:ME This will change, I need a mapper for all types + const jsCollectionItemsWithoutCollections = jsCollectionItemSubset.items.filter( + (item) => item.type !== CollectionItemType.COLLECTION + ) + + return { + items: [...jsCollectionItemsWithoutCollections, ...collectionPreviewsMapped], + totalItemCount: jsCollectionItemSubset.totalItemCount + } + }) } } diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss index d5d52eaaf..481195b2b 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss @@ -43,9 +43,9 @@ ul { display: flex; flex-direction: column; - gap: 0.5rem; + gap: 0.75rem; margin: 0; - padding: 0; + padding: 0.5rem 0; li { list-style: none; diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index cb983011e..5d5041c75 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -10,6 +10,8 @@ import { NoItemsMessage } from './NoItemsMessage' import { NoSearchMatchesMessage } from './NoSearchMatchesMessage' import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' import styles from './ItemsList.module.scss' +import { CollectionItemType } from '../../../../collection/domain/models/CollectionItemType' +import { CollectionCard } from '../../datasets-list/collection-card/CollectionCard' interface ItemsListProps { items: CollectionItem[] @@ -85,18 +87,36 @@ export const ItemsList = forwardRef(
    {items.map((collectionItem, index) => { // console.log(collectionItem) + // if (collectionItem?.type === CollectionItemType.COLLECTION) { + // return ( + //
  • + //

    This is a Collection

    + //
  • + // ) + // } + // TODO:ME make unique keys specially for datasets and files also maybe? return (
  • -

    + {collectionItem?.type === CollectionItemType.COLLECTION && ( + + )} + {collectionItem?.type === CollectionItemType.DATASET && ( +

    This is a Dataset

    + )} + {collectionItem?.type === CollectionItemType.FILE &&

    This is a File

    } + {/*

    This is a : {collectionItem?.type === 'file' && 'File'} {collectionItem?.type === 'dataset' && 'Dataset'} {collectionItem?.type === 'collection' && 'Collection'} -

    +

    */}
  • ) })} diff --git a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx index 405db8fa5..b482f26f1 100644 --- a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx +++ b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx @@ -49,9 +49,9 @@ export const SearchPanel = ({ /> - + */} ) } diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss b/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss index 3bc6554fc..f2dd23ce1 100644 --- a/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss +++ b/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss @@ -1,70 +1,90 @@ -@use 'sass:color'; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module"; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module'; -.container { - margin: 6px 0; - padding: 4px 10px; +.card-main-container { + display: flex; + flex-direction: column; + gap: 6px; + padding: 6px 10px; border: 1px solid $dv-collection-border-color; + border-radius: 4px; } -.header { +.thumbnail-and-info-wrapper { display: flex; - justify-content: space-between; + gap: 1rem; } -.title { - display: flex;gap: 8px; -} +.card-header-container { + display: flex; + gap: 1rem; + justify-content: space-between; -.icon { - margin-top: 2px; - color: $dv-collection-border-color; - font-size: 1.3em; - line-height: 1.1; + .title { + display: flex; + flex-wrap: wrap; + column-gap: 8px; - > div >span { - margin-right: 0; + .affiliation { + margin: 0; + color: $dv-subtext-color; + } + } + + .top-right-icon { + height: fit-content; + color: $dv-collection-border-color; + font-size: 1.3em; + line-height: 1.1; + + > span { + margin-right: 0; + } } } -.thumbnail { - width: 48px; - margin: 8px 12px 6px 0; - font-size: 2.8em; +.card-thumbnail-container { + padding-block: 0.25rem; img { - vertical-align: top; + max-width: 40px; + max-height: 48px; + vertical-align: top; } -} -.info { - display: flex; - color: $dv-subtext-color; + .icon { + height: fit-content; + color: $dv-collection-border-color; + + > span { + margin-right: 0; + font-size: 40px; + line-height: 1; + } + } } .card-info-container { - display: flex; + align-self: flex-start; font-size: $dv-font-size-sm; -} - -.description { - display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; - flex-direction: column; - width: 100%; - overflow: hidden; - color: black; -} -.date { - color: $dv-subtext-color; + .date-link-wrapper { + display: flex; + flex-wrap: wrap; + column-gap: 0.5rem; + } -} + .date { + color: $dv-subtext-color; + } -.affiliation { - color: $dv-subtext-color; + .description { + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + width: 100%; + overflow: hidden; + color: black; + } } - -.badge { - margin-right: 0.5em; -} \ No newline at end of file diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx index c7c15691a..00a0a3968 100644 --- a/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx +++ b/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx @@ -1,7 +1,6 @@ import { CollectionCardHeader } from './CollectionCardHeader' import { CollectionCardThumbnail } from './CollectionCardThumbnail' import { CollectionCardInfo } from './CollectionCardInfo' -import { Stack } from '@iqss/dataverse-design-system' import { CollectionPreview } from '../../../../collection/domain/models/CollectionPreview' import styles from './CollectionCard.module.scss' @@ -11,13 +10,11 @@ interface CollectionCardProps { export function CollectionCard({ collectionPreview }: CollectionCardProps) { return ( -
    +
    -
    - - - - +
    + +
    ) diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx index 14f075034..65182d972 100644 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx +++ b/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx @@ -11,29 +11,27 @@ interface CollectionCardHeaderProps { export function CollectionCardHeader({ collectionPreview }: CollectionCardHeaderProps) { return ( - <> -
    -
    - - {collectionPreview.name} - - {collectionPreview.affiliation && ( - ({collectionPreview.affiliation}) - )} - {!collectionPreview.isReleased && ( -
    - Unpublished -
    - )} -
    +
    +
    + + {collectionPreview.name} + + {collectionPreview.affiliation && ( +

    ({collectionPreview.affiliation})

    + )} + {!collectionPreview.isReleased && ( +
    + Unpublished +
    + )} +
    -
    - -
    +
    +
    - +
    ) } diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx index 93c9b44e7..ccc61c006 100644 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx +++ b/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx @@ -1,34 +1,43 @@ -import styles from './CollectionCard.module.scss' -import { DateHelper } from '../../../../shared/helpers/DateHelper' +import { useParams } from 'react-router-dom' import { Stack } from '@iqss/dataverse-design-system' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' import { CollectionPreview } from '../../../../collection/domain/models/CollectionPreview' +import { DateHelper } from '../../../../shared/helpers/DateHelper' import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { Route } from '../../../Route.enum' +import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' +import styles from './CollectionCard.module.scss' +import { ROOT_COLLECTION_ALIAS } from '../../../../collection/domain/models/Collection' interface CollectionCardInfoProps { collectionPreview: CollectionPreview } export function CollectionCardInfo({ collectionPreview }: CollectionCardInfoProps) { + const { collectionId = ROOT_COLLECTION_ALIAS } = useParams<{ collectionId: string }>() + const isStandingOnParentCollectionPage = collectionPreview.parentCollectionAlias === collectionId + return (
    - - +
    + + {!isStandingOnParentCollectionPage && ( + searchParams={{ id: collectionPreview.parentCollectionAlias.toString() }}> {collectionPreview.parentCollectionName} )} - +
    -

    {collectionPreview.description}

    + {collectionPreview.description && ( +

    {collectionPreview.description}

    + )}
    ) diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx index b5eac9791..2d0383d3a 100644 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx +++ b/src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx @@ -11,17 +11,13 @@ interface CollectionCardCardThumbnailProps { export function CollectionCardThumbnail({ collectionPreview }: CollectionCardCardThumbnailProps) { return ( -
    +
    + searchParams={{ id: collectionPreview.alias.toString() }}> {collectionPreview.thumbnail ? ( - {collectionPreview.name} + {collectionPreview.name} ) : (
    diff --git a/src/shared/core/domain/models/PublicationStatus.ts b/src/shared/core/domain/models/PublicationStatus.ts new file mode 100644 index 000000000..a56386f35 --- /dev/null +++ b/src/shared/core/domain/models/PublicationStatus.ts @@ -0,0 +1,5 @@ +export enum PublicationStatus { + Published = 'Published', + Unpublished = 'Unpublished', + Draft = 'Draft' +} From 4034cc7e839905c0be820fb0dd42be19e4c0c7f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 20 Sep 2024 12:10:57 -0300 Subject: [PATCH 16/42] feat: remove old located collection card in datasets list --- .../CollectionCard.module.scss | 90 ------------------- .../collection-card/CollectionCard.tsx | 21 ----- .../collection-card/CollectionCardHeader.tsx | 37 -------- .../collection-card/CollectionCardHelper.ts | 17 ---- .../collection-card/CollectionCardInfo.tsx | 44 --------- 5 files changed, 209 deletions(-) delete mode 100644 src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss delete mode 100644 src/sections/collection/datasets-list/collection-card/CollectionCard.tsx delete mode 100644 src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx delete mode 100644 src/sections/collection/datasets-list/collection-card/CollectionCardHelper.ts delete mode 100644 src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss b/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss deleted file mode 100644 index f2dd23ce1..000000000 --- a/src/sections/collection/datasets-list/collection-card/CollectionCard.module.scss +++ /dev/null @@ -1,90 +0,0 @@ -@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; -@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module'; - -.card-main-container { - display: flex; - flex-direction: column; - gap: 6px; - padding: 6px 10px; - border: 1px solid $dv-collection-border-color; - border-radius: 4px; -} - -.thumbnail-and-info-wrapper { - display: flex; - gap: 1rem; -} - -.card-header-container { - display: flex; - gap: 1rem; - justify-content: space-between; - - .title { - display: flex; - flex-wrap: wrap; - column-gap: 8px; - - .affiliation { - margin: 0; - color: $dv-subtext-color; - } - } - - .top-right-icon { - height: fit-content; - color: $dv-collection-border-color; - font-size: 1.3em; - line-height: 1.1; - - > span { - margin-right: 0; - } - } -} - -.card-thumbnail-container { - padding-block: 0.25rem; - - img { - max-width: 40px; - max-height: 48px; - vertical-align: top; - } - - .icon { - height: fit-content; - color: $dv-collection-border-color; - - > span { - margin-right: 0; - font-size: 40px; - line-height: 1; - } - } -} - -.card-info-container { - align-self: flex-start; - font-size: $dv-font-size-sm; - - .date-link-wrapper { - display: flex; - flex-wrap: wrap; - column-gap: 0.5rem; - } - - .date { - color: $dv-subtext-color; - } - - .description { - display: -webkit-box; - -webkit-line-clamp: 3; - line-clamp: 3; - -webkit-box-orient: vertical; - width: 100%; - overflow: hidden; - color: black; - } -} diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx deleted file mode 100644 index 00a0a3968..000000000 --- a/src/sections/collection/datasets-list/collection-card/CollectionCard.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { CollectionCardHeader } from './CollectionCardHeader' -import { CollectionCardThumbnail } from './CollectionCardThumbnail' -import { CollectionCardInfo } from './CollectionCardInfo' -import { CollectionPreview } from '../../../../collection/domain/models/CollectionPreview' -import styles from './CollectionCard.module.scss' - -interface CollectionCardProps { - collectionPreview: CollectionPreview -} - -export function CollectionCard({ collectionPreview }: CollectionCardProps) { - return ( -
    - -
    - - -
    -
    - ) -} diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx deleted file mode 100644 index 65182d972..000000000 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardHeader.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { CollectionPreview } from '../../../../collection/domain/models/CollectionPreview' -import styles from './CollectionCard.module.scss' -import { Badge, Icon, IconName } from '@iqss/dataverse-design-system' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' - -interface CollectionCardHeaderProps { - collectionPreview: CollectionPreview -} - -export function CollectionCardHeader({ collectionPreview }: CollectionCardHeaderProps) { - return ( -
    -
    - - {collectionPreview.name} - - {collectionPreview.affiliation && ( -

    ({collectionPreview.affiliation})

    - )} - {!collectionPreview.isReleased && ( -
    - Unpublished -
    - )} -
    - -
    - -
    -
    - ) -} diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardHelper.ts b/src/sections/collection/datasets-list/collection-card/CollectionCardHelper.ts deleted file mode 100644 index 28d49fe70..000000000 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardHelper.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { - DatasetLabel, - DatasetLabelSemanticMeaning, - DatasetLabelValue -} from '../../../../dataset/domain/models/Dataset' -export class CollectionCardHelper { - static getLabel(isReleased: boolean) { - const labels: DatasetLabel[] = [] - - if (!isReleased) { - labels.push( - new DatasetLabel(DatasetLabelSemanticMeaning.WARNING, DatasetLabelValue.UNPUBLISHED) - ) - } - return labels - } -} diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx b/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx deleted file mode 100644 index ccc61c006..000000000 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardInfo.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useParams } from 'react-router-dom' -import { Stack } from '@iqss/dataverse-design-system' -import { CollectionPreview } from '../../../../collection/domain/models/CollectionPreview' -import { DateHelper } from '../../../../shared/helpers/DateHelper' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { Route } from '../../../Route.enum' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import styles from './CollectionCard.module.scss' -import { ROOT_COLLECTION_ALIAS } from '../../../../collection/domain/models/Collection' - -interface CollectionCardInfoProps { - collectionPreview: CollectionPreview -} - -export function CollectionCardInfo({ collectionPreview }: CollectionCardInfoProps) { - const { collectionId = ROOT_COLLECTION_ALIAS } = useParams<{ collectionId: string }>() - const isStandingOnParentCollectionPage = collectionPreview.parentCollectionAlias === collectionId - - return ( -
    - -
    - - {!isStandingOnParentCollectionPage && ( - - {collectionPreview.parentCollectionName} - - )} -
    - - {collectionPreview.description && ( -

    {collectionPreview.description}

    - )} -
    -
    - ) -} From e89df02187b3b3455eca8de2574ccb57efd8e2ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 20 Sep 2024 17:10:10 -0300 Subject: [PATCH 17/42] feat: collection card and dataset cards ready --- .../domain/models/CollectionItemSubset.ts | 6 +- ...review.ts => CollectionItemTypePreview.ts} | 2 +- .../domain/models/FileItemTypePreview.ts | 29 ++++++ .../mappers/JSCollectionItemsMapper.ts | 54 +++++++++++ .../mappers/JSCollectionPreviewMapper.ts | 24 ----- .../mappers/JSFileItemTypePreviewMapper.ts | 28 ++++++ .../CollectionJSDataverseRepository.ts | 15 +-- src/dataset/domain/models/DatasetPreview.ts | 17 +++- .../mappers/JSDatasetPreviewMapper.ts | 18 ++-- src/files/domain/models/FilePreview.ts | 2 - .../items-list/ItemsList.module.scss | 2 +- .../items-list/ItemsList.tsx | 39 +++----- .../CollectionCard.module.scss | 90 ++++++++++++++++++ .../collection-card/CollectionCard.tsx | 21 +++++ .../collection-card/CollectionCardHeader.tsx | 37 ++++++++ .../collection-card/CollectionCardHelper.ts | 18 ++++ .../collection-card/CollectionCardInfo.tsx | 44 +++++++++ .../CollectionCardThumbnail.tsx | 12 +-- .../dataset-card/DatasetCard.module.scss | 91 +++++++++++++++++++ .../items-list/dataset-card/DatasetCard.tsx | 32 +++++++ .../dataset-card/DatasetCardHeader.tsx | 23 ++--- .../dataset-card/DatasetCardInfo.tsx | 36 ++++++++ .../dataset-card/DatasetCardThumbnail.tsx | 15 +-- .../file-card/FileCard.module.scss} | 20 ++-- .../items-list/file-card/FileCard.tsx | 24 +++++ .../items-list/file-card/FileCardHeader.tsx | 43 +++++++++ .../items-list/file-card/FileCardHelper.ts | 47 ++++++++++ .../items-list/file-card/FileCardIcon.tsx | 14 +++ .../items-list/file-card/FileCardInfo.tsx | 46 ++++++++++ .../file-card/FileCardThumbnail.tsx | 31 +++++++ .../dataset-card/DatasetCard.tsx | 29 ------ .../dataset-card/DatasetCardInfo.tsx | 31 ------- .../datasets-list/DatasetCard.stories.tsx | 6 +- .../domain/models/CollectionPreviewMother.ts | 34 +++---- .../domain/models/DatasetPreviewMother.ts | 6 ++ .../dataset-card/DatasetCard.spec.tsx | 6 +- .../dataset-card/DatasetCardHeader.spec.tsx | 2 +- .../dataset-card/DatasetCardInfo.spec.tsx | 8 +- .../DatasetCardThumbnail.spec.tsx | 2 +- 39 files changed, 809 insertions(+), 195 deletions(-) rename src/collection/domain/models/{CollectionPreview.ts => CollectionItemTypePreview.ts} (89%) create mode 100644 src/collection/domain/models/FileItemTypePreview.ts create mode 100644 src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts delete mode 100644 src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts create mode 100644 src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts create mode 100644 src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss create mode 100644 src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts create mode 100644 src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx rename src/sections/collection/{datasets-list => collection-items-panel/items-list}/collection-card/CollectionCardThumbnail.tsx (67%) create mode 100644 src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss create mode 100644 src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx rename src/sections/collection/{datasets-list => collection-items-panel/items-list}/dataset-card/DatasetCardHeader.tsx (62%) create mode 100644 src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx rename src/sections/collection/{datasets-list => collection-items-panel/items-list}/dataset-card/DatasetCardThumbnail.tsx (59%) rename src/sections/collection/{datasets-list/dataset-card/DatasetCard.module.scss => collection-items-panel/items-list/file-card/FileCard.module.scss} (79%) create mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts create mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx create mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx delete mode 100644 src/sections/collection/datasets-list/dataset-card/DatasetCard.tsx delete mode 100644 src/sections/collection/datasets-list/dataset-card/DatasetCardInfo.tsx diff --git a/src/collection/domain/models/CollectionItemSubset.ts b/src/collection/domain/models/CollectionItemSubset.ts index f017b36fe..836c3a119 100644 --- a/src/collection/domain/models/CollectionItemSubset.ts +++ b/src/collection/domain/models/CollectionItemSubset.ts @@ -1,10 +1,10 @@ +import { CollectionItemTypePreview } from './CollectionItemTypePreview' import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' -import { FilePreview } from '../../../files/domain/models/FilePreview' -import { CollectionPreview } from './CollectionPreview' +import { FileItemTypePreview } from './FileItemTypePreview' export interface CollectionItemSubset { items: CollectionItem[] totalItemCount: number } -export type CollectionItem = CollectionPreview | DatasetPreview | FilePreview +export type CollectionItem = CollectionItemTypePreview | DatasetPreview | FileItemTypePreview diff --git a/src/collection/domain/models/CollectionPreview.ts b/src/collection/domain/models/CollectionItemTypePreview.ts similarity index 89% rename from src/collection/domain/models/CollectionPreview.ts rename to src/collection/domain/models/CollectionItemTypePreview.ts index d7922bdb4..c2a24a3e4 100644 --- a/src/collection/domain/models/CollectionPreview.ts +++ b/src/collection/domain/models/CollectionItemTypePreview.ts @@ -1,6 +1,6 @@ import { CollectionItemType } from './CollectionItemType' -export interface CollectionPreview { +export interface CollectionItemTypePreview { type: CollectionItemType.COLLECTION isReleased: boolean name: string diff --git a/src/collection/domain/models/FileItemTypePreview.ts b/src/collection/domain/models/FileItemTypePreview.ts new file mode 100644 index 000000000..9d03a58a0 --- /dev/null +++ b/src/collection/domain/models/FileItemTypePreview.ts @@ -0,0 +1,29 @@ +import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' +import { CollectionItemType } from './CollectionItemType' + +export interface FileItemTypePreview { + type: CollectionItemType.FILE + id: number + name: string + persistentId?: string + url: string + imageUrl?: string + description: string + fileType: string + fileContentType: string + sizeInBytes: number + md5?: string + checksum?: FilePreviewChecksum + unf: string + datasetName: string + datasetId: number + datasetPersistentId: string + datasetCitation: string + publicationStatuses: PublicationStatus[] + releaseOrCreateDate: Date +} + +export interface FilePreviewChecksum { + type: string + value: string +} diff --git a/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts b/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts new file mode 100644 index 000000000..1f77764a6 --- /dev/null +++ b/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts @@ -0,0 +1,54 @@ +import { + CollectionPreview as JSCollectionPreview, + DatasetPreview as JSDatasetPreview, + FilePreview as JSFilePreview, + CollectionItemType as JSCollectionItemType +} from '@iqss/dataverse-client-javascript' +import { CollectionItemTypePreview } from '../../domain/models/CollectionItemTypePreview' +import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' +import { CollectionItem } from '../../domain/models/CollectionItemSubset' +import { JSDatasetPreviewMapper } from '../../../dataset/infrastructure/mappers/JSDatasetPreviewMapper' +import { JSFileItemTypePreviewMapper } from './JSFileItemTypePreviewMapper' + +export class JSCollectionItemsMapper { + static toCollectionItemsPreviews( + jsCollectionItems: (JSCollectionPreview | JSDatasetPreview | JSFilePreview)[] + ): CollectionItem[] { + const items: CollectionItem[] = [] + + jsCollectionItems.forEach((item: JSCollectionPreview | JSDatasetPreview | JSFilePreview) => { + if (item.type === JSCollectionItemType.FILE) { + // TODO:ME Here we should like make all the necessary extra calls to get info for satisfied FilePreview model?? + items.push(JSFileItemTypePreviewMapper.toFileItemTypePreview(item)) + } else if (item.type === JSCollectionItemType.DATASET) { + items.push(JSDatasetPreviewMapper.toDatasetPreview(item)) + } else if (item.type === JSCollectionItemType.COLLECTION) { + items.push(this.toCollectionItemTypePreview(item)) + } + }) + return items + } + + static toCollectionItemTypePreview( + jsDatasetPreview: JSCollectionPreview + ): CollectionItemTypePreview { + return { + type: jsDatasetPreview.type, + isReleased: JSCollectionItemsMapper.toIsRelasedCollection( + jsDatasetPreview.publicationStatuses + ), + name: jsDatasetPreview.name, + alias: jsDatasetPreview.alias, + description: jsDatasetPreview.description, + affiliation: jsDatasetPreview.affiliation, + releaseOrCreateDate: jsDatasetPreview.releaseOrCreateDate, + thumbnail: jsDatasetPreview.imageUrl, + parentCollectionName: jsDatasetPreview.parentName, + parentCollectionAlias: jsDatasetPreview.parentAlias + } + } + + static toIsRelasedCollection(jsPublicationStatus: PublicationStatus[]): boolean { + return jsPublicationStatus.includes(PublicationStatus.Published) + } +} diff --git a/src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts b/src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts deleted file mode 100644 index ee8d604a5..000000000 --- a/src/collection/infrastructure/mappers/JSCollectionPreviewMapper.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { CollectionPreview as JSCollectionPreview } from '@iqss/dataverse-client-javascript' -import { CollectionPreview } from '../../domain/models/CollectionPreview' -import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' - -export class JSCollectionPreviewMapper { - static toCollectionPreview(jsDatasetPreview: JSCollectionPreview): CollectionPreview { - return { - type: jsDatasetPreview.type, - isReleased: JSCollectionPreviewMapper.toIsRelased(jsDatasetPreview.publicationStatuses), - name: jsDatasetPreview.name, - alias: jsDatasetPreview.alias, - description: jsDatasetPreview.description, - affiliation: jsDatasetPreview.affiliation, - releaseOrCreateDate: jsDatasetPreview.releaseOrCreateDate, - thumbnail: jsDatasetPreview.imageUrl, - parentCollectionName: jsDatasetPreview.parentName, - parentCollectionAlias: jsDatasetPreview.parentAlias - } - } - - static toIsRelased(jsPublicationStatus: PublicationStatus[]): boolean { - return jsPublicationStatus.includes(PublicationStatus.Published) - } -} diff --git a/src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts b/src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts new file mode 100644 index 000000000..020b69333 --- /dev/null +++ b/src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts @@ -0,0 +1,28 @@ +import { FilePreview as JSFilePreview } from '@iqss/dataverse-client-javascript' +import { FileItemTypePreview } from '../../domain/models/FileItemTypePreview' + +export class JSFileItemTypePreviewMapper { + static toFileItemTypePreview(jsFilePreview: JSFilePreview): FileItemTypePreview { + return { + type: jsFilePreview.type, + id: jsFilePreview.fileId, + name: jsFilePreview.name, + persistentId: jsFilePreview.filePersistentId, + url: jsFilePreview.url, + imageUrl: jsFilePreview.imageUrl, + description: jsFilePreview.description, + fileType: jsFilePreview.fileType, + fileContentType: jsFilePreview.fileContentType, + sizeInBytes: jsFilePreview.sizeInBytes, + md5: jsFilePreview.md5, + checksum: jsFilePreview.checksum, + unf: jsFilePreview.unf, + datasetName: jsFilePreview.datasetName, + datasetId: jsFilePreview.datasetId, + datasetPersistentId: jsFilePreview.datasetPersistentId, + datasetCitation: jsFilePreview.datasetCitation, + publicationStatuses: jsFilePreview.publicationStatuses, + releaseOrCreateDate: jsFilePreview.releaseOrCreateDate + } + } +} diff --git a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts index 1c7c2636c..85f526ad2 100644 --- a/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts +++ b/src/collection/infrastructure/repositories/CollectionJSDataverseRepository.ts @@ -8,14 +8,13 @@ import { getCollectionItems } from '@iqss/dataverse-client-javascript' import { JSCollectionMapper } from '../mappers/JSCollectionMapper' -import { JSCollectionPreviewMapper } from '../mappers/JSCollectionPreviewMapper' import { CollectionDTO } from '../../domain/useCases/DTOs/CollectionDTO' import { CollectionFacet } from '../../domain/models/CollectionFacet' import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions' import { CollectionItemsPaginationInfo } from '../../domain/models/CollectionItemsPaginationInfo' import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset' import { CollectionSearchCriteria } from '../../domain/models/CollectionSearchCriteria' -import { CollectionItemType } from '../../domain/models/CollectionItemType' +import { JSCollectionItemsMapper } from '../mappers/JSCollectionItemsMapper' export class CollectionJSDataverseRepository implements CollectionRepository { getById(id: string): Promise { @@ -40,7 +39,6 @@ export class CollectionJSDataverseRepository implements CollectionRepository { .then((jsCollectionUserPermissions) => jsCollectionUserPermissions) } - // TODO:ME After updating previews object to match the response models we should not see ts error anymore under the return keyword getItems( collectionId: string, paginationInfo: CollectionItemsPaginationInfo, @@ -49,17 +47,12 @@ export class CollectionJSDataverseRepository implements CollectionRepository { return getCollectionItems .execute(collectionId, paginationInfo?.pageSize, paginationInfo?.offset, searchCriteria) .then((jsCollectionItemSubset) => { - const collectionPreviewsMapped = jsCollectionItemSubset.items - .filter((item) => item.type === CollectionItemType.COLLECTION) - .map((item) => JSCollectionPreviewMapper.toCollectionPreview(item)) - - // TODO:ME This will change, I need a mapper for all types - const jsCollectionItemsWithoutCollections = jsCollectionItemSubset.items.filter( - (item) => item.type !== CollectionItemType.COLLECTION + const collectionItemsPreviewsMapped = JSCollectionItemsMapper.toCollectionItemsPreviews( + jsCollectionItemSubset.items ) return { - items: [...jsCollectionItemsWithoutCollections, ...collectionPreviewsMapped], + items: collectionItemsPreviewsMapped, totalItemCount: jsCollectionItemSubset.totalItemCount } }) diff --git a/src/dataset/domain/models/DatasetPreview.ts b/src/dataset/domain/models/DatasetPreview.ts index 1b9e905b6..782b26260 100644 --- a/src/dataset/domain/models/DatasetPreview.ts +++ b/src/dataset/domain/models/DatasetPreview.ts @@ -1,8 +1,22 @@ +import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType' +import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' import { DatasetVersion } from './Dataset' -// TODO:ME Once 181 is merged, update interface // TODO:ME A class is not needed, we can change to an interface, we can ellipse the text with CSS as done in CollectionCard and FileCard +export interface DatasetPreview { + type: CollectionItemType.DATASET + persistentId: string + version: DatasetVersion + releaseOrCreateDate: Date + description: string + thumbnail?: string + publicationStatuses: PublicationStatus[] + parentCollectionName: string + parentCollectionAlias: string +} + +/* export class DatasetPreview { constructor( public persistentId: string, @@ -19,3 +33,4 @@ export class DatasetPreview { return this.description } } + */ diff --git a/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts b/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts index e6e69b930..5dcd67c51 100644 --- a/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts +++ b/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts @@ -5,18 +5,22 @@ import { JSDatasetVersionMapper } from './JSDatasetVersionMapper' export class JSDatasetPreviewMapper { static toDatasetPreview(jsDatasetPreview: JSDatasetPreview): DatasetPreview { - return new DatasetPreview( - jsDatasetPreview.persistentId, - JSDatasetVersionMapper.toVersion( + return { + type: jsDatasetPreview.type, + persistentId: jsDatasetPreview.persistentId, + version: JSDatasetVersionMapper.toVersion( jsDatasetPreview.versionId, jsDatasetPreview.versionInfo, jsDatasetPreview.title, jsDatasetPreview.citation ), - JSDatasetPreviewMapper.toPreviewDate(jsDatasetPreview.versionInfo), - jsDatasetPreview.description, - undefined // TODO: get dataset thumbnail from Dataverse https://github.com/IQSS/dataverse-frontend/issues/203 - ) + releaseOrCreateDate: JSDatasetPreviewMapper.toPreviewDate(jsDatasetPreview.versionInfo), + description: jsDatasetPreview.description, + thumbnail: undefined, // TODO: get dataset thumbnail from Dataverse https://github.com/IQSS/dataverse-frontend/issues/203 + publicationStatuses: jsDatasetPreview.publicationStatuses, + parentCollectionName: jsDatasetPreview.parentCollectionName, + parentCollectionAlias: jsDatasetPreview.parentCollectionAlias + } } static toPreviewDate(jsVersionInfo: JSDatasetVersionInfo): Date { diff --git a/src/files/domain/models/FilePreview.ts b/src/files/domain/models/FilePreview.ts index 7ab731e94..19e8426d9 100644 --- a/src/files/domain/models/FilePreview.ts +++ b/src/files/domain/models/FilePreview.ts @@ -7,8 +7,6 @@ import { } from '../../../dataset/domain/models/Dataset' import { FilePermissions } from './FilePermissions' -// TODO:ME Once 181 is merged, update interface - export interface FilePreview { id: number name: string diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss index 481195b2b..25a5c1ce9 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss @@ -45,7 +45,7 @@ flex-direction: column; gap: 0.75rem; margin: 0; - padding: 0.5rem 0; + padding: 1rem 0; li { list-style: none; diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 5d5041c75..567cb429d 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -9,9 +9,11 @@ import { ErrorItemsMessage } from './ErrorItemsMessage' import { NoItemsMessage } from './NoItemsMessage' import { NoSearchMatchesMessage } from './NoSearchMatchesMessage' import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' -import styles from './ItemsList.module.scss' import { CollectionItemType } from '../../../../collection/domain/models/CollectionItemType' -import { CollectionCard } from '../../datasets-list/collection-card/CollectionCard' +import { CollectionCard } from './collection-card/CollectionCard' +import { DatasetCard } from './dataset-card/DatasetCard' +// import { FileCard } from './file-card/FileCard' +import styles from './ItemsList.module.scss' interface ItemsListProps { items: CollectionItem[] @@ -56,6 +58,8 @@ export const ItemsList = forwardRef( const showSentrySkeleton = hasNextPage && !error && !isEmptyItems const showNotSentrySkeleton = isLoadingItems && isEmptyItems + // TODO:ME When search is too fast, switching from no results to loading skeleton to results gives a flicker effect + return (
    - {/* TODO:ME After updating js-dataverse use case, assert by the type wich card to render */}
      {items.map((collectionItem, index) => { - // console.log(collectionItem) - // if (collectionItem?.type === CollectionItemType.COLLECTION) { - // return ( - //
    • - //

      This is a Collection

      - //
    • - // ) - // } // TODO:ME make unique keys specially for datasets and files also maybe? return ( -
    • +
    • {collectionItem?.type === CollectionItemType.COLLECTION && ( )} {collectionItem?.type === CollectionItemType.DATASET && ( -

      This is a Dataset

      + + )} + + {collectionItem?.type === CollectionItemType.FILE && ( +

      Here goes a File Card

      + // )} - {collectionItem?.type === CollectionItemType.FILE &&

      This is a File

      } - {/*

      - This is a : {collectionItem?.type === 'file' && 'File'} - {collectionItem?.type === 'dataset' && 'Dataset'} - {collectionItem?.type === 'collection' && 'Collection'} -

      */}
    • ) })} diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss new file mode 100644 index 000000000..f2dd23ce1 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss @@ -0,0 +1,90 @@ +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module'; + +.card-main-container { + display: flex; + flex-direction: column; + gap: 6px; + padding: 6px 10px; + border: 1px solid $dv-collection-border-color; + border-radius: 4px; +} + +.thumbnail-and-info-wrapper { + display: flex; + gap: 1rem; +} + +.card-header-container { + display: flex; + gap: 1rem; + justify-content: space-between; + + .title { + display: flex; + flex-wrap: wrap; + column-gap: 8px; + + .affiliation { + margin: 0; + color: $dv-subtext-color; + } + } + + .top-right-icon { + height: fit-content; + color: $dv-collection-border-color; + font-size: 1.3em; + line-height: 1.1; + + > span { + margin-right: 0; + } + } +} + +.card-thumbnail-container { + padding-block: 0.25rem; + + img { + max-width: 40px; + max-height: 48px; + vertical-align: top; + } + + .icon { + height: fit-content; + color: $dv-collection-border-color; + + > span { + margin-right: 0; + font-size: 40px; + line-height: 1; + } + } +} + +.card-info-container { + align-self: flex-start; + font-size: $dv-font-size-sm; + + .date-link-wrapper { + display: flex; + flex-wrap: wrap; + column-gap: 0.5rem; + } + + .date { + color: $dv-subtext-color; + } + + .description { + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + width: 100%; + overflow: hidden; + color: black; + } +} diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx new file mode 100644 index 000000000..0492c056f --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx @@ -0,0 +1,21 @@ +import { CollectionCardHeader } from './CollectionCardHeader' +import { CollectionCardThumbnail } from './CollectionCardThumbnail' +import { CollectionCardInfo } from './CollectionCardInfo' +import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' +import styles from './CollectionCard.module.scss' + +interface CollectionCardProps { + collectionPreview: CollectionItemTypePreview +} + +export function CollectionCard({ collectionPreview }: CollectionCardProps) { + return ( +
      + +
      + + +
      +
      + ) +} diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx new file mode 100644 index 000000000..13b36ce39 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx @@ -0,0 +1,37 @@ +import { Badge, Icon, IconName } from '@iqss/dataverse-design-system' +import { Route } from '../../../../Route.enum' +import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import styles from './CollectionCard.module.scss' + +interface CollectionCardHeaderProps { + collectionPreview: CollectionItemTypePreview +} + +export function CollectionCardHeader({ collectionPreview }: CollectionCardHeaderProps) { + return ( +
      +
      + + {collectionPreview.name} + + {collectionPreview.affiliation && ( +

      ({collectionPreview.affiliation})

      + )} + {!collectionPreview.isReleased && ( +
      + Unpublished +
      + )} +
      + +
      + +
      +
      + ) +} diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts new file mode 100644 index 000000000..1bc0c9f24 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts @@ -0,0 +1,18 @@ +import { + DatasetLabel, + DatasetLabelSemanticMeaning, + DatasetLabelValue +} from '../../../../../dataset/domain/models/Dataset' + +export class CollectionCardHelper { + static getLabel(isReleased: boolean) { + const labels: DatasetLabel[] = [] + + if (!isReleased) { + labels.push( + new DatasetLabel(DatasetLabelSemanticMeaning.WARNING, DatasetLabelValue.UNPUBLISHED) + ) + } + return labels + } +} diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx new file mode 100644 index 000000000..41084ab26 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx @@ -0,0 +1,44 @@ +import { useParams } from 'react-router-dom' +import { Stack } from '@iqss/dataverse-design-system' +import { Route } from '../../../../Route.enum' +import { ROOT_COLLECTION_ALIAS } from '../../../../../collection/domain/models/Collection' +import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' +import { DateHelper } from '../../../../../shared/helpers/DateHelper' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import styles from './CollectionCard.module.scss' + +interface CollectionCardInfoProps { + collectionPreview: CollectionItemTypePreview +} + +export function CollectionCardInfo({ collectionPreview }: CollectionCardInfoProps) { + const { collectionId = ROOT_COLLECTION_ALIAS } = useParams<{ collectionId: string }>() + const isStandingOnParentCollectionPage = collectionPreview.parentCollectionAlias === collectionId + + return ( +
      + +
      + + {!isStandingOnParentCollectionPage && ( + + {collectionPreview.parentCollectionName} + + )} +
      + + {collectionPreview.description && ( +

      {collectionPreview.description}

      + )} +
      +
      + ) +} diff --git a/src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx similarity index 67% rename from src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx rename to src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx index 2d0383d3a..d0759f190 100644 --- a/src/sections/collection/datasets-list/collection-card/CollectionCardThumbnail.tsx +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx @@ -1,12 +1,12 @@ -import styles from './CollectionCard.module.scss' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' import { Icon, IconName } from '@iqss/dataverse-design-system' -import { CollectionPreview } from '../../../../collection/domain/models/CollectionPreview' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { Route } from '../../../../Route.enum' +import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import styles from './CollectionCard.module.scss' interface CollectionCardCardThumbnailProps { - collectionPreview: CollectionPreview + collectionPreview: CollectionItemTypePreview } export function CollectionCardThumbnail({ collectionPreview }: CollectionCardCardThumbnailProps) { diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss new file mode 100644 index 000000000..4fa59ce5c --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss @@ -0,0 +1,91 @@ +@use 'sass:color'; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module'; + +.card-main-container { + display: flex; + flex-direction: column; + gap: 6px; + padding: 6px 10px; + border: 1px solid $dv-dataset-border-color; + border-radius: 4px; +} + +.thumbnail-and-info-wrapper { + display: flex; + gap: 1rem; +} + +.card-header-container { + display: flex; + gap: 1rem; + justify-content: space-between; + + .title { + display: flex; + flex-wrap: wrap; + column-gap: 8px; + } + + .top-right-icon { + height: fit-content; + font-size: 1.3em; + + & span { + margin-right: 0; + } + } +} + +.card-thumbnail-container { + padding-block: 0.25rem; + + img { + max-width: 40px; + max-height: 48px; + vertical-align: top; + } + + :global .icon-dataset { + margin-right: 0; + font-size: 40px; + line-height: 1; + } +} + +.card-info-container { + display: flex; + flex-direction: column; + gap: 4px; + align-self: flex-start; + width: 100%; + font-size: $dv-font-size-sm; + + .date { + color: $dv-subtext-color; + } + + .citation-box { + padding: 4px; + background-color: color.adjust($dv-primary-color, $lightness: 48%); + border-radius: 4px; + + &.deaccesioned { + background-color: color.adjust($dv-danger-box-color, $lightness: 6%); + } + + & > div { + margin-bottom: 0; + } + } + + .description { + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + width: 100%; + overflow: hidden; + color: black; + } +} diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx new file mode 100644 index 000000000..edf9573e0 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx @@ -0,0 +1,32 @@ +import { DatasetPreview } from '../../../../../dataset/domain/models/DatasetPreview' +import { DatasetCardHeader } from './DatasetCardHeader' +import { DatasetCardThumbnail } from './DatasetCardThumbnail' +import { DatasetCardInfo } from './DatasetCardInfo' +import styles from './DatasetCard.module.scss' + +interface DatasetCardProps { + datasetPreview: DatasetPreview +} + +export function DatasetCard({ datasetPreview }: DatasetCardProps) { + return ( +
      + +
      + + +
      +
      + ) +} diff --git a/src/sections/collection/datasets-list/dataset-card/DatasetCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx similarity index 62% rename from src/sections/collection/datasets-list/dataset-card/DatasetCardHeader.tsx rename to src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx index 942f338f9..18faa1242 100644 --- a/src/sections/collection/datasets-list/dataset-card/DatasetCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx @@ -1,14 +1,14 @@ -import styles from './DatasetCard.module.scss' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { DatasetLabels } from '../../../dataset/dataset-labels/DatasetLabels' -import { DatasetIcon } from '../../../dataset/dataset-icon/DatasetIcon' +import { Route } from '../../../../Route.enum' import { + DatasetNonNumericVersionSearchParam, DatasetPublishingStatus, - DatasetVersion, - DatasetNonNumericVersionSearchParam -} from '../../../../dataset/domain/models/Dataset' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' + DatasetVersion +} from '../../../../../dataset/domain/models/Dataset' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { DatasetIcon } from '../../../../dataset/dataset-icon/DatasetIcon' +import { DatasetLabels } from '../../../../dataset/dataset-labels/DatasetLabels' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import styles from './DatasetCard.module.scss' interface DatasetCardHeaderProps { persistentId: string @@ -19,6 +19,7 @@ function getSearchParams( publishingStatus: DatasetPublishingStatus ): Record { const params: Record = { persistentId: persistentId } + if (publishingStatus === DatasetPublishingStatus.DRAFT) { params.version = DatasetNonNumericVersionSearchParam.DRAFT } @@ -26,7 +27,7 @@ function getSearchParams( } export function DatasetCardHeader({ persistentId, version }: DatasetCardHeaderProps) { return ( -
      +
      -
      +
      diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx new file mode 100644 index 000000000..2e0bc330f --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx @@ -0,0 +1,36 @@ +import cn from 'classnames' +import { + DatasetPublishingStatus, + DatasetVersion +} from '../../../../../dataset/domain/models/Dataset' +import { DateHelper } from '../../../../../shared/helpers/DateHelper' +import { CitationDescription } from '../../../../shared/citation/CitationDescription' +import styles from './DatasetCard.module.scss' + +interface DatasetCardInfoProps { + version: DatasetVersion + releaseOrCreateDate: Date + description: string +} + +export function DatasetCardInfo({ + version, + releaseOrCreateDate, + description +}: DatasetCardInfoProps) { + return ( +
      + +
      + +
      +

      {description}

      +
      + ) +} diff --git a/src/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx similarity index 59% rename from src/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.tsx rename to src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx index 78af22d41..b020f2677 100644 --- a/src/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx @@ -1,9 +1,12 @@ +import { Route } from '../../../../Route.enum' +import { + DatasetPublishingStatus, + DatasetVersion +} from '../../../../../dataset/domain/models/Dataset' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { DatasetThumbnail } from '../../../../dataset/dataset-thumbnail/DatasetThumbnail' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' import styles from './DatasetCard.module.scss' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { DatasetThumbnail } from '../../../dataset/dataset-thumbnail/DatasetThumbnail' -import { DatasetPublishingStatus, DatasetVersion } from '../../../../dataset/domain/models/Dataset' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' interface DatasetCardThumbnailProps { persistentId: string @@ -17,7 +20,7 @@ export function DatasetCardThumbnail({ thumbnail }: DatasetCardThumbnailProps) { return ( -
      +
      * { - margin-right: .5em; - } + display: flex;gap: 8px; } .icon { margin-top: 2px; font-size: 1.3em; + line-height: 1.1; > div >span { margin-right: 0; @@ -42,13 +39,20 @@ .info { display: flex; + color: $dv-subtext-color; } -.description { +.card-info-container { display: flex; + font-size: $dv-font-size-sm; +} + +.description { + display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; flex-direction: column; width: 100%; - font-size: $dv-font-size-sm; + overflow: hidden; + color: black; } .date { diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx new file mode 100644 index 000000000..e8cd8db0d --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx @@ -0,0 +1,24 @@ +import { Stack } from '@iqss/dataverse-design-system' +import { FileItemTypePreview } from '../../../../../collection/domain/models/FileItemTypePreview' +import { FileCardHeader } from './FileCardHeader' +import { FileCardThumbnail } from './FileCardThumbnail' +import { FileCardInfo } from './FileCardInfo' +import styles from './FileCard.module.scss' + +interface FileCardProps { + filePreview: FileItemTypePreview +} + +export function FileCard({ filePreview }: FileCardProps) { + return ( +
      + +
      + + + {/* */} + +
      +
      + ) +} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx new file mode 100644 index 000000000..025561802 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx @@ -0,0 +1,43 @@ +import { FileItemTypePreview } from '../../../../../collection/domain/models/FileItemTypePreview' +import { FileType } from '../../../../../files/domain/models/FileMetadata' +import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { Route } from '../../../../Route.enum' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { DatasetLabels } from '../../../../dataset/dataset-labels/DatasetLabels' +import { FileCardHelper } from './FileCardHelper' +import { FileCardIcon } from './FileCardIcon' +import styles from './FileCard.module.scss' + +interface FileCardHeaderProps { + filePreview: FileItemTypePreview +} + +export function FileCardHeader({ filePreview }: FileCardHeaderProps) { + const iconFileType = new FileType('text/tab-separated-values', 'Comma Separated Values') + + return ( +
      +
      + + {filePreview.name} + + +
      +
      + +
      +
      + ) +} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts new file mode 100644 index 000000000..5ccc098a3 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts @@ -0,0 +1,47 @@ +import { + DatasetLabel, + DatasetLabelSemanticMeaning, + DatasetLabelValue, + DatasetNonNumericVersionSearchParam, + DatasetPublishingStatus +} from '../../../../../dataset/domain/models/Dataset' +import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' + +export class FileCardHelper { + static getDatasetSearchParams( + persistentId: string, + publishingStatus: DatasetPublishingStatus + ): Record { + const params: Record = { persistentId: persistentId } + if (publishingStatus === DatasetPublishingStatus.DRAFT) { + params.version = DatasetNonNumericVersionSearchParam.DRAFT + } + return params + } + static getFileSearchParams(id: number, isDraft: boolean): Record { + const params: Record = { id: id.toString() } + if (isDraft) { + params.datasetVersion = DatasetNonNumericVersionSearchParam.DRAFT + } + return params + } + + static getDatasetLabels( + publicationStatuses: PublicationStatus[], + someDatasetVersionHasBeenReleased: boolean | undefined + ) { + const labels: DatasetLabel[] = [] + if (publicationStatuses.includes(PublicationStatus.Draft)) { + labels.push(new DatasetLabel(DatasetLabelSemanticMeaning.DATASET, DatasetLabelValue.DRAFT)) + } + if ( + someDatasetVersionHasBeenReleased == undefined || + someDatasetVersionHasBeenReleased == false + ) { + labels.push( + new DatasetLabel(DatasetLabelSemanticMeaning.WARNING, DatasetLabelValue.UNPUBLISHED) + ) + } + return labels + } +} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx new file mode 100644 index 000000000..becbfcb16 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx @@ -0,0 +1,14 @@ +import { IconName } from '@iqss/dataverse-design-system' +import { FileTypeToFileIconMap } from '../../../../file/file-preview/FileTypeToFileIconMap' +import { FileType } from '../../../../../files/domain/models/FileMetadata' +import styles from './FileCard.module.scss' + +export function FileCardIcon({ type }: { type: FileType }) { + const icon = FileTypeToFileIconMap[type.value] || IconName.OTHER + + return ( + + {icon} + + ) +} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx new file mode 100644 index 000000000..3976299e6 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx @@ -0,0 +1,46 @@ +import styles from './FileCard.module.scss' +import { DateHelper } from '../../../../shared/helpers/DateHelper' +import { FileItemTypePreview } from '../../../../collection/domain/models/FileItemTypePreview' +import { Stack } from '@iqss/dataverse-design-system' +import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' +import { Route } from '../../../Route.enum' +import { FileChecksum } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileChecksum' +import { FileTabularData } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileTabularData' +import { FileCardHelper } from './FileCardHelper' +import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { FileLabels } from '../../../file/file-labels/FileLabels' + +interface FileCardInfoProps { + filePreview: FileItemTypePreview + persistentId: string +} + +export function FileCardInfo({ filePreview, persistentId }: FileCardInfoProps) { + return ( +
      + + + {DateHelper.toDisplayFormat(filePreview.metadata.depositDate)} -{' '} + + {filePreview.datasetName} + + + + + {filePreview.metadata.type.toDisplayFormat()} - {filePreview.metadata.size.toString()} + + + + + +

      {filePreview.metadata.description}

      +
      +
      + ) +} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx new file mode 100644 index 000000000..f88b0b69b --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx @@ -0,0 +1,31 @@ +import { FileItemTypePreview } from '../../../../../collection/domain/models/FileItemTypePreview' +import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { Route } from '../../../../Route.enum' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { FileCardHelper } from './FileCardHelper' +import styles from './FileCard.module.scss' + +interface FileCardThumbnailProps { + filePreview: FileItemTypePreview + thumbnail?: string +} + +export function FileCardThumbnail({ filePreview }: FileCardThumbnailProps) { + console.log({ filePreview }) + + return ( +
      + + asd + {/* */} + +
      + ) +} diff --git a/src/sections/collection/datasets-list/dataset-card/DatasetCard.tsx b/src/sections/collection/datasets-list/dataset-card/DatasetCard.tsx deleted file mode 100644 index 5e8712da2..000000000 --- a/src/sections/collection/datasets-list/dataset-card/DatasetCard.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { DatasetPreview } from '../../../../dataset/domain/models/DatasetPreview' -import styles from './DatasetCard.module.scss' -import { DatasetCardHeader } from './DatasetCardHeader' -import { DatasetCardThumbnail } from './DatasetCardThumbnail' -import { DatasetCardInfo } from './DatasetCardInfo' - -interface DatasetCardProps { - dataset: DatasetPreview -} - -export function DatasetCard({ dataset }: DatasetCardProps) { - return ( -
      - -
      - - -
      -
      - ) -} diff --git a/src/sections/collection/datasets-list/dataset-card/DatasetCardInfo.tsx b/src/sections/collection/datasets-list/dataset-card/DatasetCardInfo.tsx deleted file mode 100644 index c8f46f8f6..000000000 --- a/src/sections/collection/datasets-list/dataset-card/DatasetCardInfo.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import styles from './DatasetCard.module.scss' -import { DateHelper } from '../../../../shared/helpers/DateHelper' -import { CitationDescription } from '../../../shared/citation/CitationDescription' -import { DatasetPublishingStatus, DatasetVersion } from '../../../../dataset/domain/models/Dataset' - -interface DatasetCardInfoProps { - version: DatasetVersion - releaseOrCreateDate: Date - abbreviatedDescription: string -} - -export function DatasetCardInfo({ - version, - releaseOrCreateDate, - abbreviatedDescription -}: DatasetCardInfoProps) { - return ( -
      - {DateHelper.toDisplayFormat(releaseOrCreateDate)} - - - - {abbreviatedDescription} -
      - ) -} diff --git a/src/stories/collection/datasets-list/DatasetCard.stories.tsx b/src/stories/collection/datasets-list/DatasetCard.stories.tsx index 1a83b7327..90910b822 100644 --- a/src/stories/collection/datasets-list/DatasetCard.stories.tsx +++ b/src/stories/collection/datasets-list/DatasetCard.stories.tsx @@ -1,6 +1,6 @@ import { Meta, StoryObj } from '@storybook/react' import { WithI18next } from '../../WithI18next' -import { DatasetCard } from '../../../sections/collection/datasets-list/dataset-card/DatasetCard' +import { DatasetCard } from '../../../sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard' import { DatasetPreviewMother } from '../../../../tests/component/dataset/domain/models/DatasetPreviewMother' const meta: Meta = { @@ -13,9 +13,9 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => + render: () => } export const Deaccessioned: Story = { - render: () => + render: () => } diff --git a/tests/component/collection/domain/models/CollectionPreviewMother.ts b/tests/component/collection/domain/models/CollectionPreviewMother.ts index 62e9509a3..7135d972b 100644 --- a/tests/component/collection/domain/models/CollectionPreviewMother.ts +++ b/tests/component/collection/domain/models/CollectionPreviewMother.ts @@ -1,16 +1,18 @@ import { faker } from '@faker-js/faker' import { FakerHelper } from '../../../shared/FakerHelper' -import { CollectionPreview } from '../../../../../src/collection/domain/models/CollectionPreview' +import { CollectionItemTypePreview } from '../../../../../src/collection/domain/models/CollectionItemTypePreview' +import { CollectionItemType } from '../../../../../src/collection/domain/models/CollectionItemType' export class CollectionPreviewMother { - static create(props?: Partial): CollectionPreview { + static create(props?: Partial): CollectionItemTypePreview { return { - id: faker.datatype.uuid(), + type: CollectionItemType.COLLECTION, + alias: faker.datatype.string(10), name: faker.lorem.words(3), isReleased: faker.datatype.boolean(), releaseOrCreateDate: faker.date.recent(), - parentCollectionId: faker.datatype.boolean() ? faker.datatype.uuid() : undefined, - parentCollectionName: faker.datatype.boolean() ? faker.lorem.words(3) : undefined, + parentCollectionAlias: faker.datatype.string(10), + parentCollectionName: faker.lorem.words(3), description: faker.datatype.boolean() ? `${faker.lorem.paragraph()} **${faker.lorem.sentence()}** ${faker.lorem.paragraph()}` : undefined, @@ -19,22 +21,23 @@ export class CollectionPreviewMother { } } - static createRealistic(): CollectionPreview { + static createRealistic(): CollectionItemTypePreview { return CollectionPreviewMother.create({ - id: 'science', isReleased: true, name: 'Scientific Research Collection', + alias: 'scientific-research-collection', releaseOrCreateDate: new Date('2021-01-01'), - parentCollectionId: 'parentId', + parentCollectionAlias: 'parent-alias', parentCollectionName: 'University Parent Collection', description: 'We do all the science.', affiliation: 'Scientific Research University' }) } - static createWithOnlyRequiredFields(props?: Partial): CollectionPreview { + static createWithOnlyRequiredFields( + props?: Partial + ): CollectionItemTypePreview { return CollectionPreviewMother.create({ - id: faker.datatype.uuid(), name: FakerHelper.collectionName(), isReleased: faker.datatype.boolean(), affiliation: undefined, @@ -43,31 +46,30 @@ export class CollectionPreviewMother { }) } - static createComplete(): CollectionPreview { + static createComplete(): CollectionItemTypePreview { return CollectionPreviewMother.create({ - id: faker.datatype.uuid(), isReleased: faker.datatype.boolean(), name: FakerHelper.collectionName(), - parentCollectionId: faker.datatype.uuid(), + parentCollectionAlias: faker.datatype.string(10), parentCollectionName: faker.lorem.words(3), releaseOrCreateDate: FakerHelper.pastDate(), description: FakerHelper.paragraph(), affiliation: FakerHelper.affiliation() }) } - static createUnpublished(): CollectionPreview { + static createUnpublished(): CollectionItemTypePreview { return CollectionPreviewMother.createWithOnlyRequiredFields({ isReleased: false, affiliation: FakerHelper.affiliation() }) } - static createWithDescription(): CollectionPreview { + static createWithDescription(): CollectionItemTypePreview { return CollectionPreviewMother.createWithOnlyRequiredFields({ description: FakerHelper.paragraph() }) } - static createWithAffiliation(): CollectionPreview { + static createWithAffiliation(): CollectionItemTypePreview { return CollectionPreviewMother.createWithOnlyRequiredFields({ affiliation: FakerHelper.affiliation() }) diff --git a/tests/component/dataset/domain/models/DatasetPreviewMother.ts b/tests/component/dataset/domain/models/DatasetPreviewMother.ts index e596c26bd..18f740298 100644 --- a/tests/component/dataset/domain/models/DatasetPreviewMother.ts +++ b/tests/component/dataset/domain/models/DatasetPreviewMother.ts @@ -2,6 +2,8 @@ import { faker } from '@faker-js/faker' import { DatasetPreview } from '../../../../../src/dataset/domain/models/DatasetPreview' import { DatasetVersionMother } from './DatasetMother' import { FakerHelper } from '../../../shared/FakerHelper' +import { CollectionItemType } from '../../../../../src/collection/domain/models/CollectionItemType' +import { PublicationStatus } from '../../../../../src/shared/core/domain/models/PublicationStatus' export class DatasetPreviewMother { static createMany(count: number): DatasetPreview[] { @@ -23,10 +25,14 @@ export class DatasetPreviewMother { } return new DatasetPreview( + CollectionItemType.DATASET, datasetPreview.persistentId, datasetPreview.version, datasetPreview.releaseOrCreateDate, datasetPreview.description, + [PublicationStatus.Published], + faker.lorem.word(), + faker.lorem.word(), datasetPreview.thumbnail ) } diff --git a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCard.spec.tsx b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCard.spec.tsx index 1e6af96bc..f4368b580 100644 --- a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCard.spec.tsx +++ b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCard.spec.tsx @@ -1,12 +1,12 @@ import { DatasetPreviewMother } from '../../../../dataset/domain/models/DatasetPreviewMother' -import { DatasetCard } from '../../../../../../src/sections/collection/datasets-list/dataset-card/DatasetCard' +import { DatasetCard } from '../../../../../../src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard' import { DateHelper } from '../../../../../../src/shared/helpers/DateHelper' import styles from '../../../../../../src/sections/collection/datasets-list/dataset-card/DatasetCard.module.scss' describe('DatasetCard', () => { it('should render the card', () => { const dataset = DatasetPreviewMother.createWithThumbnail() - cy.customMount() + cy.customMount() cy.findByText(dataset.version.title).should('exist') @@ -17,6 +17,6 @@ describe('DatasetCard', () => { .parent() .parent() .should('have.class', styles['citation-box']) - cy.findByText(dataset.abbreviatedDescription).should('exist') + cy.findByText(dataset.description).should('exist') }) }) diff --git a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardHeader.spec.tsx b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardHeader.spec.tsx index e621ac768..0fd136c70 100644 --- a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardHeader.spec.tsx +++ b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardHeader.spec.tsx @@ -1,4 +1,4 @@ -import { DatasetCardHeader } from '../../../../../../src/sections/collection/datasets-list/dataset-card/DatasetCardHeader' +import { DatasetCardHeader } from '../../../../../../src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader' import { DatasetPreviewMother } from '../../../../dataset/domain/models/DatasetPreviewMother' describe('DatasetCardHeader', () => { diff --git a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardInfo.spec.tsx b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardInfo.spec.tsx index 0c4c3608f..5039358eb 100644 --- a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardInfo.spec.tsx +++ b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardInfo.spec.tsx @@ -1,7 +1,7 @@ import { DatasetPreviewMother } from '../../../../dataset/domain/models/DatasetPreviewMother' import { DateHelper } from '../../../../../../src/shared/helpers/DateHelper' +import { DatasetCardInfo } from '../../../../../../src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo' import styles from '../../../../../../src/sections/collection/datasets-list/dataset-card/DatasetCard.module.scss' -import { DatasetCardInfo } from '../../../../../../src/sections/collection/datasets-list/dataset-card/DatasetCardInfo' describe('DatasetCardInfo', () => { it('should render the dataset info', () => { @@ -10,7 +10,7 @@ describe('DatasetCardInfo', () => { ) @@ -20,7 +20,7 @@ describe('DatasetCardInfo', () => { .parent() .parent() .should('have.class', styles['citation-box']) - cy.findByText(dataset.abbreviatedDescription).should('exist') + cy.findByText(dataset.description).should('exist') }) it('should render the citation with the deaccessioned background if the dataset is deaccessioned', () => { @@ -29,7 +29,7 @@ describe('DatasetCardInfo', () => { ) diff --git a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.spec.tsx b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.spec.tsx index c703c353c..3916191aa 100644 --- a/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.spec.tsx +++ b/tests/component/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail.spec.tsx @@ -1,5 +1,5 @@ +import { DatasetCardThumbnail } from '../../../../../../src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail' import { DatasetPreviewMother } from '../../../../dataset/domain/models/DatasetPreviewMother' -import { DatasetCardThumbnail } from '../../../../../../src/sections/collection/datasets-list/dataset-card/DatasetCardThumbnail' describe('DatasetCardThumbnail', () => { it('should render the thumbnail', () => { From e7d8163bf3fe899978efb86e97286825acccce58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 23 Sep 2024 09:36:03 -0300 Subject: [PATCH 18/42] feat: update to latest version of js-dataverse --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8404adc6..35572dceb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-pr181.746affb", + "@iqss/dataverse-client-javascript": "2.0.0-pr192.6406015", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", @@ -3674,9 +3674,9 @@ }, "node_modules/@iqss/dataverse-client-javascript": { "name": "@IQSS/dataverse-client-javascript", - "version": "2.0.0-pr181.746affb", - "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-pr181.746affb/4d6a6d19c1d9b4ffb260db63f97dfb992bbd8873", - "integrity": "sha512-HT2yHmg0Z1qRUWx6UDjD9o7OBqH++RpmwaHqXivd/GO8HikCA+t56qG0BmA1GiROO6jy+0zNNcetBcFKu7hbWQ==", + "version": "2.0.0-pr192.6406015", + "resolved": "https://npm.pkg.github.com/download/@IQSS/dataverse-client-javascript/2.0.0-pr192.6406015/4d6562b1e287c872e92ef3a551ee8aa63c5262b5", + "integrity": "sha512-1DmypaaV1cXS4y+kbx33GLmRLwI/8Cwj82MLhpq9gdy7+racUOyzVs3MFKJmdLLWTy7pAWKtj6a+c8Pt6DmtzQ==", "license": "MIT", "dependencies": { "@types/node": "^18.15.11", diff --git a/package.json b/package.json index 420985af5..9b7141154 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@faker-js/faker": "7.6.0", - "@iqss/dataverse-client-javascript": "2.0.0-pr181.746affb", + "@iqss/dataverse-client-javascript": "2.0.0-pr192.6406015", "@iqss/dataverse-design-system": "*", "@istanbuljs/nyc-config-typescript": "1.0.2", "@tanstack/react-table": "8.9.2", From 7714313692d325f2443856f8f386abaee7bce6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 23 Sep 2024 09:37:58 -0300 Subject: [PATCH 19/42] feat: some refactor and cleanup --- .../CollectionItemsPanel.tsx | 21 ++++++++++++ .../filter-panel/type-filters/TypeFilters.tsx | 6 ++-- .../items-list/ItemsList.tsx | 34 +++++++++---------- .../dataset-card/DatasetCard.module.scss | 2 ++ .../dataset-card/DatasetCardHeader.tsx | 22 ++++-------- .../dataset-card/DatasetCardHelper.ts | 18 ++++++++++ .../dataset-card/DatasetCardThumbnail.tsx | 6 +++- 7 files changed, 71 insertions(+), 38 deletions(-) create mode 100644 src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 513ddb53b..96101aa76 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -60,6 +60,27 @@ export const CollectionItemsPanel = ({ collectionId }) + // TODO:ME Detect with popstate event when the user goes back or forward in the browser history to perform the search again. + /* + Could be good to move it to a custom hook that receives the currentSearchCriteria and the loadMore function + CurrentSearchCriteria will be outdated, due to change only when is set by setSearchParams + Could be good to instead of calling load more, call the setSearchParams with the new search params but reading them with URLSearchParams + And also create a reausable function that accepts searchParams from both useSearchParams and URLSearchParams + */ + useEffect(() => { + const handlePopState = (event: PopStateEvent) => { + console.log(currentSearchCriteria) + console.log('Navegación hacia atrás o adelante detectada', event) + } + + window.addEventListener('popstate', handlePopState) + + return () => { + console.log('Removing popstate event listener') + window.removeEventListener('popstate', handlePopState) + } + }, []) + async function handleLoadMoreOnBottomReach(currentPagination: CollectionItemsPaginationInfo) { let paginationInfoToSend = currentPagination if (totalAvailable !== undefined) { diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx index 415da1516..1cf3ab2bd 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -45,7 +45,7 @@ export const TypeFilters = ({ label={ <> - Collections (19) + Collections } checked={Boolean(currentItemTypes?.includes(CollectionItemType.COLLECTION))} @@ -59,7 +59,7 @@ export const TypeFilters = ({ label={ <> - Datasets (32) + Datasets } checked={Boolean(currentItemTypes?.includes(CollectionItemType.DATASET))} @@ -73,7 +73,7 @@ export const TypeFilters = ({ label={ <> - Files (10,081) + Files } checked={Boolean(currentItemTypes?.includes(CollectionItemType.FILE))} diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 567cb429d..9506e5b63 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -88,25 +88,23 @@ export const ItemsList = forwardRef(
        - {items.map((collectionItem, index) => { - // TODO:ME make unique keys specially for datasets and files also maybe? + {items.map((collectionItem, index) => ( +
      • + {collectionItem?.type === CollectionItemType.COLLECTION && ( + + )} + {collectionItem?.type === CollectionItemType.DATASET && ( + + )} - return ( -
      • - {collectionItem?.type === CollectionItemType.COLLECTION && ( - - )} - {collectionItem?.type === CollectionItemType.DATASET && ( - - )} - - {collectionItem?.type === CollectionItemType.FILE && ( -

        Here goes a File Card

        - // - )} -
      • - ) - })} + {collectionItem?.type === CollectionItemType.FILE && ( +

        + Here goes a File Card +

        + // + )} + + ))}
      )} diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss index 4fa59ce5c..e0bfedeca 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss @@ -70,6 +70,8 @@ background-color: color.adjust($dv-primary-color, $lightness: 48%); border-radius: 4px; + // TODO:ME Could be good to apply line-clamp here also to avoid long citations, we will need to create a selector to match + &.deaccesioned { background-color: color.adjust($dv-danger-box-color, $lightness: 6%); } diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx index 18faa1242..5085b8fbd 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx @@ -1,9 +1,6 @@ import { Route } from '../../../../Route.enum' -import { - DatasetNonNumericVersionSearchParam, - DatasetPublishingStatus, - DatasetVersion -} from '../../../../../dataset/domain/models/Dataset' +import { DatasetCardHelper } from './DatasetCardHelper' +import { DatasetVersion } from '../../../../../dataset/domain/models/Dataset' import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' import { DatasetIcon } from '../../../../dataset/dataset-icon/DatasetIcon' import { DatasetLabels } from '../../../../dataset/dataset-labels/DatasetLabels' @@ -14,17 +11,7 @@ interface DatasetCardHeaderProps { persistentId: string version: DatasetVersion } -function getSearchParams( - persistentId: string, - publishingStatus: DatasetPublishingStatus -): Record { - const params: Record = { persistentId: persistentId } - if (publishingStatus === DatasetPublishingStatus.DRAFT) { - params.version = DatasetNonNumericVersionSearchParam.DRAFT - } - return params -} export function DatasetCardHeader({ persistentId, version }: DatasetCardHeaderProps) { return (
      @@ -32,7 +19,10 @@ export function DatasetCardHeader({ persistentId, version }: DatasetCardHeaderPr + searchParams={DatasetCardHelper.getDatasetSearchParams( + persistentId, + version.publishingStatus + )}> {version.title} diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts new file mode 100644 index 000000000..484641bb2 --- /dev/null +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts @@ -0,0 +1,18 @@ +import { + DatasetNonNumericVersionSearchParam, + DatasetPublishingStatus +} from '../../../../../dataset/domain/models/Dataset' + +export class DatasetCardHelper { + static getDatasetSearchParams( + persistentId: string, + publishingStatus: DatasetPublishingStatus + ): Record { + const params: Record = { persistentId: persistentId } + + if (publishingStatus === DatasetPublishingStatus.DRAFT) { + params.version = DatasetNonNumericVersionSearchParam.DRAFT + } + return params + } +} diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx index b020f2677..fa5e49d82 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardThumbnail.tsx @@ -3,6 +3,7 @@ import { DatasetPublishingStatus, DatasetVersion } from '../../../../../dataset/domain/models/Dataset' +import { DatasetCardHelper } from './DatasetCardHelper' import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' import { DatasetThumbnail } from '../../../../dataset/dataset-thumbnail/DatasetThumbnail' import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' @@ -24,7 +25,10 @@ export function DatasetCardThumbnail({ + searchParams={DatasetCardHelper.getDatasetSearchParams( + persistentId, + version.publishingStatus + )}> Date: Mon, 23 Sep 2024 10:00:18 -0300 Subject: [PATCH 20/42] feat: home navigation to collection updated --- src/sections/homepage/search-input/SearchInput.tsx | 5 +++++ .../e2e-integration/e2e/sections/homepage/Homepage.spec.tsx | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/sections/homepage/search-input/SearchInput.tsx b/src/sections/homepage/search-input/SearchInput.tsx index 2e5b51f35..204a09023 100644 --- a/src/sections/homepage/search-input/SearchInput.tsx +++ b/src/sections/homepage/search-input/SearchInput.tsx @@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom' import { Form, CloseButton } from '@iqss/dataverse-design-system' import { Search as SearchIcon } from 'react-bootstrap-icons' import { QueryParamKey, Route } from '../../Route.enum' +import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType' import styles from './SearchInput.module.scss' export const SearchInput = () => { @@ -25,6 +26,10 @@ export const SearchInput = () => { const searchParams = new URLSearchParams() searchParams.set(QueryParamKey.QUERY, encodedSearchValue) + searchParams.set( + QueryParamKey.COLLECTION_ITEM_TYPES, + [CollectionItemType.COLLECTION, CollectionItemType.DATASET, CollectionItemType.FILE].join(',') + ) const collectionUrlWithQuery = `${Route.COLLECTIONS_BASE}?${searchParams.toString()}` diff --git a/tests/e2e-integration/e2e/sections/homepage/Homepage.spec.tsx b/tests/e2e-integration/e2e/sections/homepage/Homepage.spec.tsx index 1e245ad3c..09c19beab 100644 --- a/tests/e2e-integration/e2e/sections/homepage/Homepage.spec.tsx +++ b/tests/e2e-integration/e2e/sections/homepage/Homepage.spec.tsx @@ -1,3 +1,4 @@ +import { CollectionItemType } from '../../../../../src/collection/domain/models/CollectionItemType' import { QueryParamKey } from '../../../../../src/sections/Route.enum' describe('Homepage', () => { @@ -11,6 +12,10 @@ describe('Homepage', () => { const searchParams = new URLSearchParams() searchParams.set(QueryParamKey.QUERY, encodedSearchValue) + searchParams.set( + QueryParamKey.COLLECTION_ITEM_TYPES, + [CollectionItemType.COLLECTION, CollectionItemType.DATASET, CollectionItemType.FILE].join(',') + ) cy.url().should('include', `/collections?${searchParams.toString()}`) }) From 338fac5db1889b5124a01da067e570d24e8d012c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 23 Sep 2024 15:30:38 -0300 Subject: [PATCH 21/42] file card ready --- .../domain/models/CollectionItemSubset.ts | 2 +- .../mappers/JSCollectionItemsMapper.ts | 18 ++- .../domain/models/FileItemTypePreview.ts | 4 +- .../mappers/JSFileItemTypePreviewMapper.ts | 2 +- .../items-list/ItemsList.module.scss | 4 +- .../items-list/ItemsList.tsx | 8 +- .../CollectionCard.module.scss | 18 +-- .../collection-card/CollectionCardHeader.tsx | 6 +- .../dataset-card/DatasetCard.module.scss | 2 +- .../dataset-card/DatasetCardHeader.tsx | 6 +- .../items-list/file-card/FileCard.module.scss | 133 +++++++++++------- .../items-list/file-card/FileCard.tsx | 13 +- .../items-list/file-card/FileCardHeader.tsx | 22 +-- .../items-list/file-card/FileCardHelper.ts | 21 ++- .../items-list/file-card/FileCardIcon.tsx | 14 -- .../items-list/file-card/FileCardInfo.tsx | 57 ++++---- .../file-card/FileCardThumbnail.tsx | 31 +++- .../CopyToClipboard.module.scss | 8 +- .../dataset-labels/DatasetLabels.module.scss | 9 +- .../dataset/dataset-labels/DatasetLabels.tsx | 2 +- 20 files changed, 219 insertions(+), 161 deletions(-) rename src/{collection => files}/domain/models/FileItemTypePreview.ts (84%) rename src/{collection => files}/infrastructure/mappers/JSFileItemTypePreviewMapper.ts (96%) delete mode 100644 src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx diff --git a/src/collection/domain/models/CollectionItemSubset.ts b/src/collection/domain/models/CollectionItemSubset.ts index 836c3a119..4e8b0541f 100644 --- a/src/collection/domain/models/CollectionItemSubset.ts +++ b/src/collection/domain/models/CollectionItemSubset.ts @@ -1,6 +1,6 @@ import { CollectionItemTypePreview } from './CollectionItemTypePreview' import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' -import { FileItemTypePreview } from './FileItemTypePreview' +import { FileItemTypePreview } from '../../../files/domain/models/FileItemTypePreview' export interface CollectionItemSubset { items: CollectionItem[] diff --git a/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts b/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts index 1f77764a6..82e4d8ba8 100644 --- a/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts +++ b/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts @@ -4,11 +4,11 @@ import { FilePreview as JSFilePreview, CollectionItemType as JSCollectionItemType } from '@iqss/dataverse-client-javascript' -import { CollectionItemTypePreview } from '../../domain/models/CollectionItemTypePreview' import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' import { CollectionItem } from '../../domain/models/CollectionItemSubset' +import { CollectionItemTypePreview } from '../../domain/models/CollectionItemTypePreview' import { JSDatasetPreviewMapper } from '../../../dataset/infrastructure/mappers/JSDatasetPreviewMapper' -import { JSFileItemTypePreviewMapper } from './JSFileItemTypePreviewMapper' +import { JSFileItemTypePreviewMapper } from '../../../files/infrastructure/mappers/JSFileItemTypePreviewMapper' export class JSCollectionItemsMapper { static toCollectionItemsPreviews( @@ -17,15 +17,19 @@ export class JSCollectionItemsMapper { const items: CollectionItem[] = [] jsCollectionItems.forEach((item: JSCollectionPreview | JSDatasetPreview | JSFilePreview) => { + if (item.type === JSCollectionItemType.COLLECTION) { + items.push(this.toCollectionItemTypePreview(item)) + } + + if (item.type === JSCollectionItemType.DATASET) { + items.push(JSDatasetPreviewMapper.toDatasetPreview(item)) + } + if (item.type === JSCollectionItemType.FILE) { - // TODO:ME Here we should like make all the necessary extra calls to get info for satisfied FilePreview model?? items.push(JSFileItemTypePreviewMapper.toFileItemTypePreview(item)) - } else if (item.type === JSCollectionItemType.DATASET) { - items.push(JSDatasetPreviewMapper.toDatasetPreview(item)) - } else if (item.type === JSCollectionItemType.COLLECTION) { - items.push(this.toCollectionItemTypePreview(item)) } }) + return items } diff --git a/src/collection/domain/models/FileItemTypePreview.ts b/src/files/domain/models/FileItemTypePreview.ts similarity index 84% rename from src/collection/domain/models/FileItemTypePreview.ts rename to src/files/domain/models/FileItemTypePreview.ts index 9d03a58a0..0e7a5659c 100644 --- a/src/collection/domain/models/FileItemTypePreview.ts +++ b/src/files/domain/models/FileItemTypePreview.ts @@ -1,5 +1,5 @@ +import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType' import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' -import { CollectionItemType } from './CollectionItemType' export interface FileItemTypePreview { type: CollectionItemType.FILE @@ -7,7 +7,7 @@ export interface FileItemTypePreview { name: string persistentId?: string url: string - imageUrl?: string + thumbnail?: string description: string fileType: string fileContentType: string diff --git a/src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts b/src/files/infrastructure/mappers/JSFileItemTypePreviewMapper.ts similarity index 96% rename from src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts rename to src/files/infrastructure/mappers/JSFileItemTypePreviewMapper.ts index 020b69333..e3ae4dea3 100644 --- a/src/collection/infrastructure/mappers/JSFileItemTypePreviewMapper.ts +++ b/src/files/infrastructure/mappers/JSFileItemTypePreviewMapper.ts @@ -9,7 +9,7 @@ export class JSFileItemTypePreviewMapper { name: jsFilePreview.name, persistentId: jsFilePreview.filePersistentId, url: jsFilePreview.url, - imageUrl: jsFilePreview.imageUrl, + thumbnail: jsFilePreview.imageUrl, description: jsFilePreview.description, fileType: jsFilePreview.fileType, fileContentType: jsFilePreview.fileContentType, diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss index 25a5c1ce9..253026076 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.module.scss @@ -29,7 +29,7 @@ background: $dv-warning-box-color; } - header { + > header { position: sticky; top: 0; z-index: 10; @@ -40,7 +40,7 @@ transform: translateX(calc(var(--inline-padding) * -1)); } - ul { + > ul { display: flex; flex-direction: column; gap: 0.75rem; diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 9506e5b63..19a79cca8 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -12,7 +12,7 @@ import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' import { CollectionItemType } from '../../../../collection/domain/models/CollectionItemType' import { CollectionCard } from './collection-card/CollectionCard' import { DatasetCard } from './dataset-card/DatasetCard' -// import { FileCard } from './file-card/FileCard' +import { FileCard } from './file-card/FileCard' import styles from './ItemsList.module.scss' interface ItemsListProps { @@ -96,12 +96,8 @@ export const ItemsList = forwardRef( {collectionItem?.type === CollectionItemType.DATASET && ( )} - {collectionItem?.type === CollectionItemType.FILE && ( -

      - Here goes a File Card -

      - // + )} ))} diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss index f2dd23ce1..d40e87775 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss @@ -10,17 +10,12 @@ border-radius: 4px; } -.thumbnail-and-info-wrapper { - display: flex; - gap: 1rem; -} - .card-header-container { display: flex; gap: 1rem; justify-content: space-between; - .title { + .left-side-content { display: flex; flex-wrap: wrap; column-gap: 8px; @@ -43,6 +38,11 @@ } } +.thumbnail-and-info-wrapper { + display: flex; + gap: 1rem; +} + .card-thumbnail-container { padding-block: 0.25rem; @@ -72,10 +72,10 @@ display: flex; flex-wrap: wrap; column-gap: 0.5rem; - } - .date { - color: $dv-subtext-color; + .date { + color: $dv-subtext-color; + } } .description { diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx index 13b36ce39..49af8c024 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx @@ -11,8 +11,8 @@ interface CollectionCardHeaderProps { export function CollectionCardHeader({ collectionPreview }: CollectionCardHeaderProps) { return ( -
      -
      +
      +
      -
      + ) } diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss index e0bfedeca..c1aa46400 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss @@ -21,7 +21,7 @@ gap: 1rem; justify-content: space-between; - .title { + .left-side-content { display: flex; flex-wrap: wrap; column-gap: 8px; diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx index 5085b8fbd..484dc4b9b 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx @@ -14,8 +14,8 @@ interface DatasetCardHeaderProps { export function DatasetCardHeader({ persistentId, version }: DatasetCardHeaderProps) { return ( -
      -
      +
      +
      -
      + ) } diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.module.scss b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.module.scss index ff436a6f8..7514bb2d1 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.module.scss @@ -1,75 +1,112 @@ @use 'sass:color'; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module"; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module'; -.container { - margin: 6px 0; - padding: 4px 10px; +.card-main-container { + display: flex; + flex-direction: column; + gap: 6px; + padding: 6px 10px; border: 1px solid $dv-file-border-color; + border-radius: 4px; } -.header { +.card-header-container { display: flex; + gap: 1rem; justify-content: space-between; -} -.title { - display: flex;gap: 8px; -} + .left-side-content { + display: flex; + flex-wrap: wrap; + gap: 8px; + } -.icon { - margin-top: 2px; - font-size: 1.3em; - line-height: 1.1; + .top-right-icon { + height: fit-content; + color: $dv-file-border-color; + font-size: 1.3em; + line-height: 1.1; - > div >span { - margin-right: 0; + > span { + margin-right: 0; + } } } -.thumbnail { - width: 48px; - margin: 8px 12px 6px 0; - font-size: 2.8em; +.thumbnail-and-info-wrapper { + display: flex; + gap: 1rem; +} + +.card-thumbnail-container { + padding-block: 0.25rem; img { - vertical-align: top; + width: 64px; + max-width: 64px; + height: 48px; + max-height: 48px; + vertical-align: top; + } + + .icon { + height: fit-content; + color: $dv-file-border-color; + + > span { + margin-right: 0; + font-size: 40px; + line-height: 1; + } } } -.info { - display: flex; - color: $dv-subtext-color; +.tooltip { + display: table-cell; + width: 430px; + height: 430px; + vertical-align: middle; + + img { + width: 100%; + max-width: 390px; + } } .card-info-container { - display: flex; + align-self: flex-start; font-size: $dv-font-size-sm; -} -.description { - display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; - flex-direction: column; - width: 100%; - overflow: hidden; - color: black; -} + .date-link-wrapper { + display: flex; + flex-wrap: wrap; + column-gap: 0.5rem; -.date { - color: $dv-subtext-color; + .date { + color: $dv-subtext-color; + } + } -} + .info { + display: flex; + flex-wrap: wrap; + gap: 4px; + align-items: center; + color: $dv-subtext-color; -.citation-box { - margin-top: 4px; - margin-bottom: .5em; - padding: 4px; - background-color: color.adjust($dv-primary-color, $lightness: 51%) ; -} + > p { + margin: 0; + } + } -.citation-box-deaccessioned { - margin-top: 4px; - margin-bottom: .5em; - padding: 4px; - background-color: color.adjust($dv-danger-box-color, $lightness: 6%); -} \ No newline at end of file + .description { + display: -webkit-box; + -webkit-line-clamp: 3; + line-clamp: 3; + -webkit-box-orient: vertical; + width: 100%; + overflow: hidden; + color: black; + } +} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx index e8cd8db0d..479549b81 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx @@ -1,5 +1,4 @@ -import { Stack } from '@iqss/dataverse-design-system' -import { FileItemTypePreview } from '../../../../../collection/domain/models/FileItemTypePreview' +import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' import { FileCardHeader } from './FileCardHeader' import { FileCardThumbnail } from './FileCardThumbnail' import { FileCardInfo } from './FileCardInfo' @@ -11,13 +10,11 @@ interface FileCardProps { export function FileCard({ filePreview }: FileCardProps) { return ( -
      +
      -
      - - - {/* */} - +
      + +
      ) diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx index 025561802..aa8bbc30c 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx @@ -1,12 +1,13 @@ -import { FileItemTypePreview } from '../../../../../collection/domain/models/FileItemTypePreview' -import { FileType } from '../../../../../files/domain/models/FileMetadata' +import { Icon, IconName } from '@iqss/dataverse-design-system' import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' +import { FileType } from '../../../../../files/domain/models/FileMetadata' +import { FileTypeToFileIconMap } from '../../../../file/file-preview/FileTypeToFileIconMap' +import { FileCardHelper } from './FileCardHelper' import { Route } from '../../../../Route.enum' import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' import { DatasetLabels } from '../../../../dataset/dataset-labels/DatasetLabels' -import { FileCardHelper } from './FileCardHelper' -import { FileCardIcon } from './FileCardIcon' import styles from './FileCard.module.scss' interface FileCardHeaderProps { @@ -14,11 +15,12 @@ interface FileCardHeaderProps { } export function FileCardHeader({ filePreview }: FileCardHeaderProps) { - const iconFileType = new FileType('text/tab-separated-values', 'Comma Separated Values') + const iconFileType = new FileType(filePreview.fileContentType) + const iconName = FileTypeToFileIconMap[iconFileType.value] || IconName.OTHER return ( -
      -
      +
      +
      -
      - +
      +
      -
      +
      ) } diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts index 5ccc098a3..85590c8e1 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts @@ -2,18 +2,14 @@ import { DatasetLabel, DatasetLabelSemanticMeaning, DatasetLabelValue, - DatasetNonNumericVersionSearchParam, - DatasetPublishingStatus + DatasetNonNumericVersionSearchParam } from '../../../../../dataset/domain/models/Dataset' import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' export class FileCardHelper { - static getDatasetSearchParams( - persistentId: string, - publishingStatus: DatasetPublishingStatus - ): Record { + static getDatasetSearchParams(persistentId: string, isDraft: boolean): Record { const params: Record = { persistentId: persistentId } - if (publishingStatus === DatasetPublishingStatus.DRAFT) { + if (isDraft) { params.version = DatasetNonNumericVersionSearchParam.DRAFT } return params @@ -44,4 +40,15 @@ export class FileCardHelper { } return labels } + + static formatBytesToCompactNumber(bytes: number): string { + const byteValueNumberFormatter = Intl.NumberFormat(undefined, { + notation: 'compact', + style: 'unit', + unit: 'byte', + unitDisplay: 'narrow' + }) + + return byteValueNumberFormatter.format(bytes) + } } diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx deleted file mode 100644 index becbfcb16..000000000 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardIcon.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { IconName } from '@iqss/dataverse-design-system' -import { FileTypeToFileIconMap } from '../../../../file/file-preview/FileTypeToFileIconMap' -import { FileType } from '../../../../../files/domain/models/FileMetadata' -import styles from './FileCard.module.scss' - -export function FileCardIcon({ type }: { type: FileType }) { - const icon = FileTypeToFileIconMap[type.value] || IconName.OTHER - - return ( - - {icon} - - ) -} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx index 3976299e6..ce9507e4f 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx @@ -1,45 +1,52 @@ -import styles from './FileCard.module.scss' -import { DateHelper } from '../../../../shared/helpers/DateHelper' -import { FileItemTypePreview } from '../../../../collection/domain/models/FileItemTypePreview' import { Stack } from '@iqss/dataverse-design-system' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { FileChecksum } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileChecksum' -import { FileTabularData } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileTabularData' +import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' +import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' +import { DateHelper } from '../../../../../shared/helpers/DateHelper' import { FileCardHelper } from './FileCardHelper' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { FileLabels } from '../../../file/file-labels/FileLabels' +import { Route } from '../../../../Route.enum' +import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { CopyToClipboardButton } from '../../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboardButton' +import styles from './FileCard.module.scss' interface FileCardInfoProps { filePreview: FileItemTypePreview - persistentId: string } -export function FileCardInfo({ filePreview, persistentId }: FileCardInfoProps) { +export function FileCardInfo({ filePreview }: FileCardInfoProps) { + const bytesFormatted = FileCardHelper.formatBytesToCompactNumber(filePreview.sizeInBytes) + return (
      - - {DateHelper.toDisplayFormat(filePreview.metadata.depositDate)} -{' '} +
      + {filePreview.datasetName} - - - - {filePreview.metadata.type.toDisplayFormat()} - {filePreview.metadata.size.toString()} - - - - - -

      {filePreview.metadata.description}

      +
      + +
      + {filePreview.fileType} + {`- ${bytesFormatted}`} + {filePreview.checksum && ( + + {`- ${filePreview.checksum.type}:`} + + + )} +
      +

      {filePreview.description}

      ) diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx index f88b0b69b..6f859f648 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx @@ -1,21 +1,24 @@ -import { FileItemTypePreview } from '../../../../../collection/domain/models/FileItemTypePreview' +import { Icon, IconName, Tooltip } from '@iqss/dataverse-design-system' +import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' +import { FileType } from '../../../../../files/domain/models/FileMetadata' +import { FileTypeToFileIconMap } from '../../../../file/file-preview/FileTypeToFileIconMap' +import { FileCardHelper } from './FileCardHelper' import { Route } from '../../../../Route.enum' import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' -import { FileCardHelper } from './FileCardHelper' import styles from './FileCard.module.scss' interface FileCardThumbnailProps { filePreview: FileItemTypePreview - thumbnail?: string } export function FileCardThumbnail({ filePreview }: FileCardThumbnailProps) { - console.log({ filePreview }) + const iconFileType = new FileType(filePreview.fileContentType) + const iconName = FileTypeToFileIconMap[iconFileType.value] || IconName.OTHER return ( -
      +
      - asd - {/* */} + {filePreview.thumbnail ? ( + + {filePreview.name} +
      + } + placement="top" + maxWidth={430}> + {filePreview.name} + + ) : ( +
      + +
      + )}
      ) diff --git a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboard.module.scss b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboard.module.scss index 621a9a2aa..60570d0b8 100644 --- a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboard.module.scss +++ b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboard.module.scss @@ -1,8 +1,10 @@ -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; +@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; .container { + display: inline-flex; + align-items: center; margin: 0 3px; - cursor: pointer + cursor: pointer; } %icon { @@ -17,4 +19,4 @@ @extend %icon; color: $dv-success-color; -} \ No newline at end of file +} diff --git a/src/sections/dataset/dataset-labels/DatasetLabels.module.scss b/src/sections/dataset/dataset-labels/DatasetLabels.module.scss index ec77535e7..1a5cf5aa0 100644 --- a/src/sections/dataset/dataset-labels/DatasetLabels.module.scss +++ b/src/sections/dataset/dataset-labels/DatasetLabels.module.scss @@ -1,3 +1,6 @@ -.container > * { - margin-right: 0.5em; -} \ No newline at end of file +.dataset-labels-container { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; +} diff --git a/src/sections/dataset/dataset-labels/DatasetLabels.tsx b/src/sections/dataset/dataset-labels/DatasetLabels.tsx index e60f0cff5..e6ba9d096 100644 --- a/src/sections/dataset/dataset-labels/DatasetLabels.tsx +++ b/src/sections/dataset/dataset-labels/DatasetLabels.tsx @@ -21,7 +21,7 @@ interface DatasetLabelsProps { export function DatasetLabels({ labels }: DatasetLabelsProps) { return ( -
      +
      {labels.map((label: DatasetLabel, index) => { return ( Date: Mon, 23 Sep 2024 15:31:17 -0300 Subject: [PATCH 22/42] feat: remove file card from dataset-list --- .../file-card/FileCard.module.scss | 75 ------------------- .../datasets-list/file-card/FileCard.tsx | 25 ------- .../file-card/FileCardHeader.tsx | 41 ---------- .../datasets-list/file-card/FileCardHelper.ts | 48 ------------ .../datasets-list/file-card/FileCardIcon.tsx | 14 ---- .../datasets-list/file-card/FileCardInfo.tsx | 46 ------------ .../file-card/FileCardThumbnail.tsx | 28 ------- 7 files changed, 277 deletions(-) delete mode 100644 src/sections/collection/datasets-list/file-card/FileCard.module.scss delete mode 100644 src/sections/collection/datasets-list/file-card/FileCard.tsx delete mode 100644 src/sections/collection/datasets-list/file-card/FileCardHeader.tsx delete mode 100644 src/sections/collection/datasets-list/file-card/FileCardHelper.ts delete mode 100644 src/sections/collection/datasets-list/file-card/FileCardIcon.tsx delete mode 100644 src/sections/collection/datasets-list/file-card/FileCardInfo.tsx delete mode 100644 src/sections/collection/datasets-list/file-card/FileCardThumbnail.tsx diff --git a/src/sections/collection/datasets-list/file-card/FileCard.module.scss b/src/sections/collection/datasets-list/file-card/FileCard.module.scss deleted file mode 100644 index ff436a6f8..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCard.module.scss +++ /dev/null @@ -1,75 +0,0 @@ -@use 'sass:color'; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module"; -@import "node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/typography.module"; - -.container { - margin: 6px 0; - padding: 4px 10px; - border: 1px solid $dv-file-border-color; -} - -.header { - display: flex; - justify-content: space-between; -} - -.title { - display: flex;gap: 8px; -} - -.icon { - margin-top: 2px; - font-size: 1.3em; - line-height: 1.1; - - > div >span { - margin-right: 0; - } -} - -.thumbnail { - width: 48px; - margin: 8px 12px 6px 0; - font-size: 2.8em; - - img { - vertical-align: top; - } -} - -.info { - display: flex; - color: $dv-subtext-color; -} - -.card-info-container { - display: flex; - font-size: $dv-font-size-sm; -} - -.description { - display: -webkit-box; -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical; - flex-direction: column; - width: 100%; - overflow: hidden; - color: black; -} - -.date { - color: $dv-subtext-color; - -} - -.citation-box { - margin-top: 4px; - margin-bottom: .5em; - padding: 4px; - background-color: color.adjust($dv-primary-color, $lightness: 51%) ; -} - -.citation-box-deaccessioned { - margin-top: 4px; - margin-bottom: .5em; - padding: 4px; - background-color: color.adjust($dv-danger-box-color, $lightness: 6%); -} \ No newline at end of file diff --git a/src/sections/collection/datasets-list/file-card/FileCard.tsx b/src/sections/collection/datasets-list/file-card/FileCard.tsx deleted file mode 100644 index a1900e4d6..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCard.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import styles from './FileCard.module.scss' -import { FileCardHeader } from './FileCardHeader' -import { FileCardThumbnail } from './FileCardThumbnail' -import { FileCardInfo } from './FileCardInfo' -import { FilePreview } from '../../../../files/domain/models/FilePreview' -import { Stack } from '@iqss/dataverse-design-system' - -interface FileCardProps { - filePreview: FilePreview - persistentId: string -} - -export function FileCard({ filePreview, persistentId }: FileCardProps) { - return ( -
      - -
      - - - - -
      -
      - ) -} diff --git a/src/sections/collection/datasets-list/file-card/FileCardHeader.tsx b/src/sections/collection/datasets-list/file-card/FileCardHeader.tsx deleted file mode 100644 index a8a887920..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCardHeader.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import styles from './FileCard.module.scss' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { FilePreview } from '../../../../files/domain/models/FilePreview' -import { DatasetLabels } from '../../../dataset/dataset-labels/DatasetLabels' -import { FileCardIcon } from './FileCardIcon' -import { FileType } from '../../../../files/domain/models/FileMetadata' -import { FileCardHelper } from './FileCardHelper' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' - -interface FileCardHeaderProps { - filePreview: FilePreview -} - -export function FileCardHeader({ filePreview }: FileCardHeaderProps) { - const iconFileType = new FileType('text/tab-separated-values', 'Comma Separated Values') - return ( -
      -
      - - {filePreview.name} - - -
      -
      - -
      -
      - ) -} diff --git a/src/sections/collection/datasets-list/file-card/FileCardHelper.ts b/src/sections/collection/datasets-list/file-card/FileCardHelper.ts deleted file mode 100644 index ce506c1ce..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCardHelper.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { - DatasetLabel, - DatasetLabelSemanticMeaning, - DatasetLabelValue, - DatasetNonNumericVersionSearchParam, - DatasetPublishingStatus -} from '../../../../dataset/domain/models/Dataset' -export class FileCardHelper { - static getDatasetSearchParams( - persistentId: string, - publishingStatus: DatasetPublishingStatus - ): Record { - const params: Record = { persistentId: persistentId } - if (publishingStatus === DatasetPublishingStatus.DRAFT) { - params.version = DatasetNonNumericVersionSearchParam.DRAFT - } - return params - } - static getFileSearchParams( - id: number, - publishingStatus: DatasetPublishingStatus - ): Record { - const params: Record = { id: id.toString() } - if (publishingStatus === DatasetPublishingStatus.DRAFT) { - params.datasetVersion = DatasetNonNumericVersionSearchParam.DRAFT - } - return params - } - - static getDatasetLabels( - datasetPublishingStatus: DatasetPublishingStatus, - someDatasetVersionHasBeenReleased: boolean | undefined - ) { - const labels: DatasetLabel[] = [] - if (datasetPublishingStatus === DatasetPublishingStatus.DRAFT) { - labels.push(new DatasetLabel(DatasetLabelSemanticMeaning.DATASET, DatasetLabelValue.DRAFT)) - } - if ( - someDatasetVersionHasBeenReleased == undefined || - someDatasetVersionHasBeenReleased == false - ) { - labels.push( - new DatasetLabel(DatasetLabelSemanticMeaning.WARNING, DatasetLabelValue.UNPUBLISHED) - ) - } - return labels - } -} diff --git a/src/sections/collection/datasets-list/file-card/FileCardIcon.tsx b/src/sections/collection/datasets-list/file-card/FileCardIcon.tsx deleted file mode 100644 index c8f695c69..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCardIcon.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import styles from './FileCard.module.scss' -import { IconName } from '@iqss/dataverse-design-system' -import { FileType } from '../../../../files/domain/models/FileMetadata' -import { FileTypeToFileIconMap } from '../../../file/file-preview/FileTypeToFileIconMap' - -export function FileCardIcon({ type }: { type: FileType }) { - const icon = FileTypeToFileIconMap[type.value] || IconName.OTHER - - return ( - - {icon} - - ) -} diff --git a/src/sections/collection/datasets-list/file-card/FileCardInfo.tsx b/src/sections/collection/datasets-list/file-card/FileCardInfo.tsx deleted file mode 100644 index 2e11ea917..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCardInfo.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import styles from './FileCard.module.scss' -import { DateHelper } from '../../../../shared/helpers/DateHelper' -import { FilePreview } from '../../../../files/domain/models/FilePreview' -import { Stack } from '@iqss/dataverse-design-system' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { FileChecksum } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileChecksum' -import { FileTabularData } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileTabularData' -import { FileCardHelper } from './FileCardHelper' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { FileLabels } from '../../../file/file-labels/FileLabels' - -interface FileCardInfoProps { - filePreview: FilePreview - persistentId: string -} - -export function FileCardInfo({ filePreview, persistentId }: FileCardInfoProps) { - return ( -
      - - - {DateHelper.toDisplayFormat(filePreview.metadata.depositDate)} -{' '} - - {filePreview.datasetName} - - - - - {filePreview.metadata.type.toDisplayFormat()} - {filePreview.metadata.size.toString()} - - - - - -

      {filePreview.metadata.description}

      -
      -
      - ) -} diff --git a/src/sections/collection/datasets-list/file-card/FileCardThumbnail.tsx b/src/sections/collection/datasets-list/file-card/FileCardThumbnail.tsx deleted file mode 100644 index 95bbe515d..000000000 --- a/src/sections/collection/datasets-list/file-card/FileCardThumbnail.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import styles from './FileCard.module.scss' -import { LinkToPage } from '../../../shared/link-to-page/LinkToPage' -import { Route } from '../../../Route.enum' -import { FileThumbnail } from '../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/file-thumbnail/FileThumbnail' -import { FilePreview } from '../../../../files/domain/models/FilePreview' -import { FileCardHelper } from './FileCardHelper' -import { DvObjectType } from '../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' - -interface FileCardThumbnailProps { - filePreview: FilePreview - thumbnail?: string -} - -export function FileCardThumbnail({ filePreview }: FileCardThumbnailProps) { - return ( -
      - - - -
      - ) -} From 12f79a2ef1443cd2e8759774f9bf698be8744d34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Mon, 23 Sep 2024 17:10:56 -0300 Subject: [PATCH 23/42] feat: load items on back and forward browser navigation --- src/index.tsx | 18 +++---- src/sections/collection/Collection.tsx | 3 +- src/sections/collection/CollectionHelper.ts | 25 +++++++++ .../CollectionItemsPanel.module.scss | 1 - .../CollectionItemsPanel.tsx | 53 +++++++++---------- .../CollectionsItemsPanelHelper.ts | 7 --- .../search-panel/SearchPanel.tsx | 12 +++-- .../collection/useGetCollectionQueryParams.ts | 19 ++----- .../collection/useLoadMoreOnPopStateEvent.ts | 22 ++++++++ 9 files changed, 92 insertions(+), 68 deletions(-) create mode 100644 src/sections/collection/CollectionHelper.ts delete mode 100644 src/sections/collection/collection-items-panel/CollectionsItemsPanelHelper.ts create mode 100644 src/sections/collection/useLoadMoreOnPopStateEvent.ts diff --git a/src/index.tsx b/src/index.tsx index ba839fec8..9eabd9a17 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,13 +7,13 @@ import { ThemeProvider } from '@iqss/dataverse-design-system' const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( - - - - - - - - - + // + + + + + + + + // ) diff --git a/src/sections/collection/Collection.tsx b/src/sections/collection/Collection.tsx index 159191407..6edb35b25 100644 --- a/src/sections/collection/Collection.tsx +++ b/src/sections/collection/Collection.tsx @@ -26,8 +26,7 @@ export function Collection({ collectionId, collectionRepository, created, - collectionQueryParams, - infiniteScrollEnabled = false + collectionQueryParams }: CollectionProps) { useTranslation('collection') useScrollTop() diff --git a/src/sections/collection/CollectionHelper.ts b/src/sections/collection/CollectionHelper.ts new file mode 100644 index 000000000..d16273f05 --- /dev/null +++ b/src/sections/collection/CollectionHelper.ts @@ -0,0 +1,25 @@ +import { CollectionItemType } from '../../collection/domain/models/CollectionItemType' +import { QueryParamKey } from '../Route.enum' + +export class CollectionHelper { + static defineCollectionQueryParams(searchParams: URLSearchParams) { + const pageQuery = searchParams.get('page') + ? parseInt(searchParams.get('page') as string, 10) + : 1 + + const searchQuery = searchParams.get(QueryParamKey.QUERY) + ? decodeURIComponent(searchParams.get(QueryParamKey.QUERY) as string) + : undefined + + const typesParam = searchParams.get(QueryParamKey.COLLECTION_ITEM_TYPES) ?? undefined + + const typesQuery = typesParam + ?.split(',') + .map((type) => decodeURIComponent(type)) + .filter((type) => + Object.values(CollectionItemType).includes(type as CollectionItemType) + ) as CollectionItemType[] + + return { pageQuery, searchQuery, typesQuery } + } +} diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.module.scss b/src/sections/collection/collection-items-panel/CollectionItemsPanel.module.scss index 5bc22306e..f852e9d6c 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.module.scss +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.module.scss @@ -27,7 +27,6 @@ flex-direction: column; gap: 0.5rem; - // TODO:ME Add media queries in variables @media (min-width: 992px) { display: grid; grid-template-columns: 1fr 3fr; diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 96101aa76..6485aa07c 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -13,6 +13,8 @@ import { QueryParamKey } from '../../Route.enum' import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType' import { ItemTypeChange } from './filter-panel/type-filters/TypeFilters' import styles from './CollectionItemsPanel.module.scss' +import { useLoadMoreOnPopStateEvent } from '../useLoadMoreOnPopStateEvent' +import { CollectionHelper } from '../CollectionHelper' interface CollectionItemsPanelProps { collectionId: string @@ -30,16 +32,14 @@ export const CollectionItemsPanel = ({ const { setIsLoading } = useLoading() const [_, setSearchParams] = useSearchParams() + useLoadMoreOnPopStateEvent(loadItemsOnBackAndForwardNavigation) + // This object will update every time we update a query param in the URL with the setSearchParams setter const currentSearchCriteria = new CollectionSearchCriteria( collectionQueryParams.searchQuery, collectionQueryParams.typesQuery || [CollectionItemType.COLLECTION, CollectionItemType.DATASET] ) - /* - TODO:ME For now I think we really shouldnt care about setting an inital page based on the page query param - Items in specific page of a list could change while browsing at different times so is not so useful for url sharing. - */ const [paginationInfo, setPaginationInfo] = useState( new CollectionItemsPaginationInfo() ) @@ -60,27 +60,6 @@ export const CollectionItemsPanel = ({ collectionId }) - // TODO:ME Detect with popstate event when the user goes back or forward in the browser history to perform the search again. - /* - Could be good to move it to a custom hook that receives the currentSearchCriteria and the loadMore function - CurrentSearchCriteria will be outdated, due to change only when is set by setSearchParams - Could be good to instead of calling load more, call the setSearchParams with the new search params but reading them with URLSearchParams - And also create a reausable function that accepts searchParams from both useSearchParams and URLSearchParams - */ - useEffect(() => { - const handlePopState = (event: PopStateEvent) => { - console.log(currentSearchCriteria) - console.log('Navegación hacia atrás o adelante detectada', event) - } - - window.addEventListener('popstate', handlePopState) - - return () => { - console.log('Removing popstate event listener') - window.removeEventListener('popstate', handlePopState) - } - }, []) - async function handleLoadMoreOnBottomReach(currentPagination: CollectionItemsPaginationInfo) { let paginationInfoToSend = currentPagination if (totalAvailable !== undefined) { @@ -170,6 +149,24 @@ export const CollectionItemsPanel = ({ } } + async function loadItemsOnBackAndForwardNavigation() { + const searchParams = new URLSearchParams(window.location.search) + const collectionQueryParams = CollectionHelper.defineCollectionQueryParams(searchParams) + + const newCollectionSearchCriteria = new CollectionSearchCriteria( + collectionQueryParams.searchQuery, + collectionQueryParams.typesQuery + ) + + const newPaginationInfo = new CollectionItemsPaginationInfo() + const totalItemsCount = await loadMore(newPaginationInfo, newCollectionSearchCriteria, true) + + if (totalItemsCount !== undefined) { + const paginationInfoUpdated = newPaginationInfo.withTotal(totalItemsCount) + setPaginationInfo(paginationInfoUpdated) + } + } + useEffect(() => { setIsLoading(isLoadingItems) }, [isLoadingItems, setIsLoading]) @@ -179,7 +176,7 @@ export const CollectionItemsPanel = ({
      {addDataSlot}
      @@ -200,9 +197,7 @@ export const CollectionItemsPanel = ({ areItemsAvailable={areItemsAvailable} hasNextPage={hasNextPage} isEmptyItems={isEmptyItems} - hasSearchValue={new CollectionSearchCriteria( - collectionQueryParams.searchQuery - ).hasSearchText()} + hasSearchValue={currentSearchCriteria.hasSearchText()} paginationInfo={paginationInfo} onBottomReach={handleLoadMoreOnBottomReach} ref={itemsListContainerRef} diff --git a/src/sections/collection/collection-items-panel/CollectionsItemsPanelHelper.ts b/src/sections/collection/collection-items-panel/CollectionsItemsPanelHelper.ts deleted file mode 100644 index f9ad6dd2d..000000000 --- a/src/sections/collection/collection-items-panel/CollectionsItemsPanelHelper.ts +++ /dev/null @@ -1,7 +0,0 @@ -// TODO:ME Delete if not needed -export class CollectionItemsPanelHelper { - // static getURLQueryValueByKey(queryKey: string): string | null { - // const searchParams = new URLSearchParams(window.location.search) - // return searchParams.get(queryKey) - // } -} diff --git a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx index b482f26f1..5071f4c2c 100644 --- a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx +++ b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx @@ -1,20 +1,20 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import { Button, Form } from '@iqss/dataverse-design-system' import { Search } from 'react-bootstrap-icons' import styles from './SearchPanel.module.scss' interface SearchPanelProps { - initialSearchValue?: string + currentSearchValue?: string isLoadingCollectionItems: boolean onSubmitSearch: (searchValue: string) => void } export const SearchPanel = ({ - initialSearchValue = '', + currentSearchValue = '', isLoadingCollectionItems, onSubmitSearch }: SearchPanelProps) => { - const [searchValue, setSearchValue] = useState(initialSearchValue) + const [searchValue, setSearchValue] = useState(currentSearchValue) const handleSubmit = (event: React.FormEvent) => { event.preventDefault() @@ -30,6 +30,10 @@ export const SearchPanel = ({ setSearchValue(event.target.value) } + useEffect(() => { + setSearchValue(currentSearchValue) + }, [currentSearchValue]) + return (
      diff --git a/src/sections/collection/useGetCollectionQueryParams.ts b/src/sections/collection/useGetCollectionQueryParams.ts index 43c797326..2f92f516d 100644 --- a/src/sections/collection/useGetCollectionQueryParams.ts +++ b/src/sections/collection/useGetCollectionQueryParams.ts @@ -1,6 +1,6 @@ import { useSearchParams } from 'react-router-dom' -import { QueryParamKey } from '../Route.enum' import { CollectionItemType } from '../../collection/domain/models/CollectionItemType' +import { CollectionHelper } from './CollectionHelper' export interface UseCollectionQueryParamsReturnType { pageQuery: number @@ -11,20 +11,7 @@ export interface UseCollectionQueryParamsReturnType { export const useGetCollectionQueryParams = (): UseCollectionQueryParamsReturnType => { const [searchParams] = useSearchParams() - const pageQuery = searchParams.get('page') ? parseInt(searchParams.get('page') as string, 10) : 1 + const collectionQueryParams = CollectionHelper.defineCollectionQueryParams(searchParams) - const searchQuery = searchParams.get(QueryParamKey.QUERY) - ? decodeURIComponent(searchParams.get(QueryParamKey.QUERY) as string) - : undefined - - const typesParam = searchParams.get(QueryParamKey.COLLECTION_ITEM_TYPES) ?? undefined - - const typesQuery = typesParam - ?.split(',') - .map((type) => decodeURIComponent(type)) - .filter((type) => - Object.values(CollectionItemType).includes(type as CollectionItemType) - ) as CollectionItemType[] - - return { pageQuery, searchQuery, typesQuery } + return collectionQueryParams } diff --git a/src/sections/collection/useLoadMoreOnPopStateEvent.ts b/src/sections/collection/useLoadMoreOnPopStateEvent.ts new file mode 100644 index 000000000..5479fcd7d --- /dev/null +++ b/src/sections/collection/useLoadMoreOnPopStateEvent.ts @@ -0,0 +1,22 @@ +import { useEffect } from 'react' + +/** + * Hook that listens to the popstate event and calls the onPopStateEvent function. + * This is to load collection items when the user navigates back and forward in the browser history within the collection page. + * @param loadCollectionItems - Function to be called when the popstate event is triggered + */ + +export const useLoadMoreOnPopStateEvent = (onPopStateEvent: () => Promise) => { + useEffect(() => { + const handlePopState = (_e: PopStateEvent) => { + void onPopStateEvent() + } + + window.addEventListener('popstate', handlePopState) + + return () => { + window.removeEventListener('popstate', handlePopState) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) +} From 746a19f6a99e14c16b0a5e8e1c64e8c15e35375a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 24 Sep 2024 09:29:48 -0300 Subject: [PATCH 24/42] chore: add src folder alias to improve imports --- dev-env/vite.config.ts | 4 ++++ tsconfig.json | 6 +++++- vite.config.ts | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/dev-env/vite.config.ts b/dev-env/vite.config.ts index 972a2ce0c..17bf71d82 100644 --- a/dev-env/vite.config.ts +++ b/dev-env/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import istanbul from 'vite-plugin-istanbul' +import * as path from 'path' export default defineConfig({ plugins: [ @@ -20,5 +21,8 @@ export default defineConfig({ hmr: { clientPort: 8000 // nginx reverse proxy port } + }, + resolve: { + alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }] } }) diff --git a/tsconfig.json b/tsconfig.json index 0923b81c4..3b85af474 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,11 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + + "paths": { + "@/*": ["./src/*"] + } }, "include": [ "src", diff --git a/vite.config.ts b/vite.config.ts index 9281bbd1b..1330a9112 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import istanbul from 'vite-plugin-istanbul' +import * as path from 'path' export default defineConfig({ plugins: [ @@ -12,5 +13,8 @@ export default defineConfig({ ], preview: { port: 5173 + }, + resolve: { + alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }] } }) From dc025365953aad7f8e7a6c2a1ea272adc8d941e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 24 Sep 2024 09:30:12 -0300 Subject: [PATCH 25/42] chore: use alias in imports --- src/dataset/domain/models/DatasetPreview.ts | 21 ------------------- .../CollectionItemsPanel.tsx | 19 ++++++++++------- .../filter-panel/FilterPanel.tsx | 2 +- .../filter-panel/type-filters/TypeFilters.tsx | 2 +- .../items-list/ItemsList.tsx | 10 ++++----- .../items-list/NoItemsMessage.tsx | 4 ++-- .../collection-card/CollectionCard.tsx | 2 +- .../collection-card/CollectionCardHeader.tsx | 8 +++---- .../collection-card/CollectionCardHelper.ts | 2 +- .../collection-card/CollectionCardInfo.tsx | 12 +++++------ .../CollectionCardThumbnail.tsx | 8 +++---- .../items-list/dataset-card/DatasetCard.tsx | 2 +- .../dataset-card/DatasetCardHeader.tsx | 12 +++++------ .../dataset-card/DatasetCardHelper.ts | 2 +- .../dataset-card/DatasetCardInfo.tsx | 9 +++----- .../items-list/file-card/FileCard.tsx | 2 +- .../items-list/file-card/FileCardHeader.tsx | 16 +++++++------- .../items-list/file-card/FileCardHelper.ts | 4 ++-- .../items-list/file-card/FileCardInfo.tsx | 14 ++++++------- .../file-card/FileCardThumbnail.tsx | 14 ++++++------- .../useGetAccumulatedItems.tsx | 10 ++++----- .../collection/useGetCollectionQueryParams.ts | 2 +- .../collection/useLoadMoreOnPopStateEvent.ts | 2 +- 23 files changed, 79 insertions(+), 100 deletions(-) diff --git a/src/dataset/domain/models/DatasetPreview.ts b/src/dataset/domain/models/DatasetPreview.ts index 782b26260..22b0fa540 100644 --- a/src/dataset/domain/models/DatasetPreview.ts +++ b/src/dataset/domain/models/DatasetPreview.ts @@ -2,8 +2,6 @@ import { CollectionItemType } from '../../../collection/domain/models/Collection import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' import { DatasetVersion } from './Dataset' -// TODO:ME A class is not needed, we can change to an interface, we can ellipse the text with CSS as done in CollectionCard and FileCard - export interface DatasetPreview { type: CollectionItemType.DATASET persistentId: string @@ -15,22 +13,3 @@ export interface DatasetPreview { parentCollectionName: string parentCollectionAlias: string } - -/* -export class DatasetPreview { - constructor( - public persistentId: string, - public version: DatasetVersion, - public releaseOrCreateDate: Date, - public description: string, - public thumbnail?: string - ) {} - - get abbreviatedDescription(): string { - if (this.description.length > 280) { - return `${this.description.substring(0, 280)}...` - } - return this.description - } -} - */ diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 6485aa07c..8600b0970 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -1,20 +1,20 @@ import { useEffect, useRef, useState } from 'react' import { useSearchParams } from 'react-router-dom' -import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' -import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' -import { CollectionSearchCriteria } from '../../../collection/domain/models/CollectionSearchCriteria' +import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import { useGetAccumulatedItems } from './useGetAccumulatedItems' import { UseCollectionQueryParamsReturnType } from '../useGetCollectionQueryParams' -import { useLoading } from '../../loading/LoadingContext' +import { useLoadMoreOnPopStateEvent } from '../useLoadMoreOnPopStateEvent' +import { useLoading } from '@/sections/loading/LoadingContext' +import { QueryParamKey } from '../../Route.enum' +import { CollectionHelper } from '../CollectionHelper' import { FilterPanel } from './filter-panel/FilterPanel' import { ItemsList } from './items-list/ItemsList' import { SearchPanel } from './search-panel/SearchPanel' -import { QueryParamKey } from '../../Route.enum' -import { CollectionItemType } from '../../../collection/domain/models/CollectionItemType' import { ItemTypeChange } from './filter-panel/type-filters/TypeFilters' import styles from './CollectionItemsPanel.module.scss' -import { useLoadMoreOnPopStateEvent } from '../useLoadMoreOnPopStateEvent' -import { CollectionHelper } from '../CollectionHelper' interface CollectionItemsPanelProps { collectionId: string @@ -23,6 +23,9 @@ interface CollectionItemsPanelProps { addDataSlot: JSX.Element | null } +// TODO:ME Fix Stories and Mother Mocks objects +// TODO:ME New Tests + export const CollectionItemsPanel = ({ collectionId, collectionRepository, diff --git a/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.tsx b/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.tsx index 0d88f6c36..03fe63218 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.tsx +++ b/src/sections/collection/collection-items-panel/filter-panel/FilterPanel.tsx @@ -1,8 +1,8 @@ import { useState } from 'react' import { Button, Offcanvas } from '@iqss/dataverse-design-system' import { FunnelFill } from 'react-bootstrap-icons' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import { ItemTypeChange, TypeFilters } from './type-filters/TypeFilters' -import { CollectionItemType } from '../../../../collection/domain/models/CollectionItemType' import styles from './FilterPanel.module.scss' interface FilterPanelProps { diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx index 1cf3ab2bd..ec221ba12 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -1,6 +1,6 @@ import { ChangeEvent } from 'react' import { Form, Icon, IconName, Stack } from '@iqss/dataverse-design-system' -import { CollectionItemType } from '../../../../../collection/domain/models/CollectionItemType' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import styles from './TypeFilters.module.scss' interface TypeFiltersProps { diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 19a79cca8..55bd6457c 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -2,14 +2,14 @@ import { ForwardedRef, forwardRef } from 'react' import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' import useInfiniteScroll from 'react-infinite-scroll-hook' import cn from 'classnames' -import { type CollectionItem } from '../../../../collection/domain/models/CollectionItemSubset' -import { CollectionItemsPaginationInfo } from '../../../../collection/domain/models/CollectionItemsPaginationInfo' -import { PaginationResultsInfo } from '../../../shared/pagination/PaginationResultsInfo' +import { type CollectionItem } from '@/collection/domain/models/CollectionItemSubset' +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' +import { PaginationResultsInfo } from '@/sections/shared/pagination/PaginationResultsInfo' +import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' import { ErrorItemsMessage } from './ErrorItemsMessage' import { NoItemsMessage } from './NoItemsMessage' import { NoSearchMatchesMessage } from './NoSearchMatchesMessage' -import { NO_COLLECTION_ITEMS } from '../useGetAccumulatedItems' -import { CollectionItemType } from '../../../../collection/domain/models/CollectionItemType' import { CollectionCard } from './collection-card/CollectionCard' import { DatasetCard } from './dataset-card/DatasetCard' import { FileCard } from './file-card/FileCard' diff --git a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx index 0a497f438..a35be5798 100644 --- a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx +++ b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx @@ -1,6 +1,6 @@ import { Trans, useTranslation } from 'react-i18next' -import { useSession } from '../../../session/SessionContext' -import { Route } from '../../../Route.enum' +import { useSession } from '@/sections/session/SessionContext' +import { Route } from '@/sections/Route.enum' import styles from './ItemsList.module.scss' export function NoItemsMessage() { diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx index 0492c056f..2a60af1d6 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.tsx @@ -1,7 +1,7 @@ import { CollectionCardHeader } from './CollectionCardHeader' import { CollectionCardThumbnail } from './CollectionCardThumbnail' import { CollectionCardInfo } from './CollectionCardInfo' -import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' +import { CollectionItemTypePreview } from '@/collection/domain/models/CollectionItemTypePreview' import styles from './CollectionCard.module.scss' interface CollectionCardProps { diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx index 49af8c024..6dffc3655 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHeader.tsx @@ -1,8 +1,8 @@ import { Badge, Icon, IconName } from '@iqss/dataverse-design-system' -import { Route } from '../../../../Route.enum' -import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { Route } from '@/sections/Route.enum' +import { CollectionItemTypePreview } from '@/collection/domain/models/CollectionItemTypePreview' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' import styles from './CollectionCard.module.scss' interface CollectionCardHeaderProps { diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts index 1bc0c9f24..4d6c52ee9 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts @@ -2,7 +2,7 @@ import { DatasetLabel, DatasetLabelSemanticMeaning, DatasetLabelValue -} from '../../../../../dataset/domain/models/Dataset' +} from '@/dataset/domain/models/Dataset' export class CollectionCardHelper { static getLabel(isReleased: boolean) { diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx index 41084ab26..5c182b285 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardInfo.tsx @@ -1,11 +1,11 @@ import { useParams } from 'react-router-dom' import { Stack } from '@iqss/dataverse-design-system' -import { Route } from '../../../../Route.enum' -import { ROOT_COLLECTION_ALIAS } from '../../../../../collection/domain/models/Collection' -import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' -import { DateHelper } from '../../../../../shared/helpers/DateHelper' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { ROOT_COLLECTION_ALIAS } from '@/collection/domain/models/Collection' +import { CollectionItemTypePreview } from '@/collection/domain/models/CollectionItemTypePreview' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { DateHelper } from '@/shared/helpers/DateHelper' +import { Route } from '@/sections/Route.enum' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' import styles from './CollectionCard.module.scss' interface CollectionCardInfoProps { diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx index d0759f190..88c56bb6e 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardThumbnail.tsx @@ -1,8 +1,8 @@ import { Icon, IconName } from '@iqss/dataverse-design-system' -import { Route } from '../../../../Route.enum' -import { CollectionItemTypePreview } from '../../../../../collection/domain/models/CollectionItemTypePreview' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { CollectionItemTypePreview } from '@/collection/domain/models/CollectionItemTypePreview' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { Route } from '@/sections/Route.enum' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' import styles from './CollectionCard.module.scss' interface CollectionCardCardThumbnailProps { diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx index edf9573e0..3db6f49b1 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx @@ -1,4 +1,4 @@ -import { DatasetPreview } from '../../../../../dataset/domain/models/DatasetPreview' +import { DatasetPreview } from '@/dataset/domain/models/DatasetPreview' import { DatasetCardHeader } from './DatasetCardHeader' import { DatasetCardThumbnail } from './DatasetCardThumbnail' import { DatasetCardInfo } from './DatasetCardInfo' diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx index 484dc4b9b..857a0c7a3 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader.tsx @@ -1,10 +1,10 @@ -import { Route } from '../../../../Route.enum' +import { Route } from '@/sections/Route.enum' import { DatasetCardHelper } from './DatasetCardHelper' -import { DatasetVersion } from '../../../../../dataset/domain/models/Dataset' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { DatasetIcon } from '../../../../dataset/dataset-icon/DatasetIcon' -import { DatasetLabels } from '../../../../dataset/dataset-labels/DatasetLabels' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { DatasetVersion } from '@/dataset/domain/models/Dataset' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { DatasetIcon } from '@/sections/dataset/dataset-icon/DatasetIcon' +import { DatasetLabels } from '@/sections/dataset/dataset-labels/DatasetLabels' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' import styles from './DatasetCard.module.scss' interface DatasetCardHeaderProps { diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts index 484641bb2..bfde0ba9c 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHelper.ts @@ -1,7 +1,7 @@ import { DatasetNonNumericVersionSearchParam, DatasetPublishingStatus -} from '../../../../../dataset/domain/models/Dataset' +} from '@/dataset/domain/models/Dataset' export class DatasetCardHelper { static getDatasetSearchParams( diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx index 2e0bc330f..194ac622d 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo.tsx @@ -1,10 +1,7 @@ import cn from 'classnames' -import { - DatasetPublishingStatus, - DatasetVersion -} from '../../../../../dataset/domain/models/Dataset' -import { DateHelper } from '../../../../../shared/helpers/DateHelper' -import { CitationDescription } from '../../../../shared/citation/CitationDescription' +import { DatasetPublishingStatus, DatasetVersion } from '@/dataset/domain/models/Dataset' +import { DateHelper } from '@/shared/helpers/DateHelper' +import { CitationDescription } from '@/sections/shared/citation/CitationDescription' import styles from './DatasetCard.module.scss' interface DatasetCardInfoProps { diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx index 479549b81..5c0a6b979 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCard.tsx @@ -1,4 +1,4 @@ -import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' +import { FileItemTypePreview } from '@/files/domain/models/FileItemTypePreview' import { FileCardHeader } from './FileCardHeader' import { FileCardThumbnail } from './FileCardThumbnail' import { FileCardInfo } from './FileCardInfo' diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx index aa8bbc30c..43f5f0c0b 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHeader.tsx @@ -1,13 +1,13 @@ import { Icon, IconName } from '@iqss/dataverse-design-system' -import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' -import { FileType } from '../../../../../files/domain/models/FileMetadata' -import { FileTypeToFileIconMap } from '../../../../file/file-preview/FileTypeToFileIconMap' +import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { FileItemTypePreview } from '@/files/domain/models/FileItemTypePreview' +import { FileType } from '@/files/domain/models/FileMetadata' +import { FileTypeToFileIconMap } from '@/sections/file/file-preview/FileTypeToFileIconMap' +import { Route } from '@/sections/Route.enum' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' +import { DatasetLabels } from '@/sections/dataset/dataset-labels/DatasetLabels' import { FileCardHelper } from './FileCardHelper' -import { Route } from '../../../../Route.enum' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' -import { DatasetLabels } from '../../../../dataset/dataset-labels/DatasetLabels' import styles from './FileCard.module.scss' interface FileCardHeaderProps { diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts index 85590c8e1..c55eebff1 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts @@ -3,8 +3,8 @@ import { DatasetLabelSemanticMeaning, DatasetLabelValue, DatasetNonNumericVersionSearchParam -} from '../../../../../dataset/domain/models/Dataset' -import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' +} from '@/dataset/domain/models/Dataset' +import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus' export class FileCardHelper { static getDatasetSearchParams(persistentId: string, isDraft: boolean): Record { diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx index ce9507e4f..977212bb6 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardInfo.tsx @@ -1,12 +1,12 @@ import { Stack } from '@iqss/dataverse-design-system' -import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' -import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' -import { DateHelper } from '../../../../../shared/helpers/DateHelper' +import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus' +import { FileItemTypePreview } from '@/files/domain/models/FileItemTypePreview' +import { DateHelper } from '@/shared/helpers/DateHelper' import { FileCardHelper } from './FileCardHelper' -import { Route } from '../../../../Route.enum' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' -import { CopyToClipboardButton } from '../../../../dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboardButton' +import { Route } from '@/sections/Route.enum' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' +import { CopyToClipboardButton } from '@/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/copy-to-clipboard-button/CopyToClipboardButton' import styles from './FileCard.module.scss' interface FileCardInfoProps { diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx index 6f859f648..8a4a35948 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardThumbnail.tsx @@ -1,12 +1,12 @@ import { Icon, IconName, Tooltip } from '@iqss/dataverse-design-system' -import { FileItemTypePreview } from '../../../../../files/domain/models/FileItemTypePreview' -import { PublicationStatus } from '../../../../../shared/core/domain/models/PublicationStatus' -import { DvObjectType } from '../../../../../shared/hierarchy/domain/models/UpwardHierarchyNode' -import { FileType } from '../../../../../files/domain/models/FileMetadata' -import { FileTypeToFileIconMap } from '../../../../file/file-preview/FileTypeToFileIconMap' +import { FileItemTypePreview } from '@/files/domain/models/FileItemTypePreview' +import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus' +import { DvObjectType } from '@/shared/hierarchy/domain/models/UpwardHierarchyNode' +import { FileType } from '@/files/domain/models/FileMetadata' +import { FileTypeToFileIconMap } from '@/sections/file/file-preview/FileTypeToFileIconMap' import { FileCardHelper } from './FileCardHelper' -import { Route } from '../../../../Route.enum' -import { LinkToPage } from '../../../../shared/link-to-page/LinkToPage' +import { Route } from '@/sections/Route.enum' +import { LinkToPage } from '@/sections/shared/link-to-page/LinkToPage' import styles from './FileCard.module.scss' interface FileCardThumbnailProps { diff --git a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx index fb6fc57e8..8179cc2a1 100644 --- a/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx +++ b/src/sections/collection/collection-items-panel/useGetAccumulatedItems.tsx @@ -1,12 +1,12 @@ import { useMemo, useState } from 'react' -import { getCollectionItems } from '../../../collection/domain/useCases/getCollectionItems' -import { CollectionRepository } from '../../../collection/domain/repositories/CollectionRepository' +import { getCollectionItems } from '@/collection/domain/useCases/getCollectionItems' +import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { CollectionItem, CollectionItemSubset -} from '../../../collection/domain/models/CollectionItemSubset' -import { CollectionItemsPaginationInfo } from '../../../collection/domain/models/CollectionItemsPaginationInfo' -import { CollectionSearchCriteria } from '../../../collection/domain/models/CollectionSearchCriteria' +} from '@/collection/domain/models/CollectionItemSubset' +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria' export const NO_COLLECTION_ITEMS = 0 diff --git a/src/sections/collection/useGetCollectionQueryParams.ts b/src/sections/collection/useGetCollectionQueryParams.ts index 2f92f516d..292db143f 100644 --- a/src/sections/collection/useGetCollectionQueryParams.ts +++ b/src/sections/collection/useGetCollectionQueryParams.ts @@ -1,5 +1,5 @@ import { useSearchParams } from 'react-router-dom' -import { CollectionItemType } from '../../collection/domain/models/CollectionItemType' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import { CollectionHelper } from './CollectionHelper' export interface UseCollectionQueryParamsReturnType { diff --git a/src/sections/collection/useLoadMoreOnPopStateEvent.ts b/src/sections/collection/useLoadMoreOnPopStateEvent.ts index 5479fcd7d..9de66b2c9 100644 --- a/src/sections/collection/useLoadMoreOnPopStateEvent.ts +++ b/src/sections/collection/useLoadMoreOnPopStateEvent.ts @@ -3,7 +3,7 @@ import { useEffect } from 'react' /** * Hook that listens to the popstate event and calls the onPopStateEvent function. * This is to load collection items when the user navigates back and forward in the browser history within the collection page. - * @param loadCollectionItems - Function to be called when the popstate event is triggered + * @param onPopStateEvent - Function to be called when the popstate event is triggered */ export const useLoadMoreOnPopStateEvent = (onPopStateEvent: () => Promise) => { From 491826be1b0bbcba5805ba32deb54851cf118871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 24 Sep 2024 17:16:06 -0300 Subject: [PATCH 26/42] feat: working on stories and mocks --- dev-env/docker-compose-dev.yml | 4 +- .../CollectionItemsPanel.tsx | 2 +- .../CollectionCard.module.scss | 2 +- .../dataset-card/DatasetCard.module.scss | 17 +++- .../useLoadMoreOnPopStateEvent.ts | 0 .../shared/hierarchy/BreadcrumbsSkeleton.tsx | 2 +- src/stories/collection/Collection.stories.tsx | 51 +++--------- .../CollectionLoadingMockRepository.ts | 10 +++ .../collection/CollectionMockRepository.ts | 35 +++++++++ .../collection/NoCollectionMockRepository.ts | 18 +++++ .../CollectionCard.stories.tsx | 17 ++-- .../CollectionItemsPanel.stories.tsx | 34 ++++++++ .../DatasetCard.stories.tsx | 6 +- .../FileCard.stories.tsx | 36 +++++++++ .../datasets-list/DatasetsList.stories.tsx | 35 --------- ...DatasetsListWithInfiniteScroll.stories.tsx | 52 ------------- .../datasets-list/FileCard.stories.tsx | 65 ---------------- ....ts => CollectionItemTypePreviewMother.ts} | 21 +++-- .../domain/models/CollectionItemsMother.ts | 24 ++++++ .../domain/models/DatasetPreviewMother.ts | 29 +++---- .../models/FileItemTypePreviewMother.ts | 77 +++++++++++++++++++ 21 files changed, 307 insertions(+), 230 deletions(-) rename src/sections/collection/{ => collection-items-panel}/useLoadMoreOnPopStateEvent.ts (100%) rename src/stories/collection/{datasets-list => collection-items-panel}/CollectionCard.stories.tsx (52%) create mode 100644 src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx rename src/stories/collection/{datasets-list => collection-items-panel}/DatasetCard.stories.tsx (73%) create mode 100644 src/stories/collection/collection-items-panel/FileCard.stories.tsx delete mode 100644 src/stories/collection/datasets-list/DatasetsList.stories.tsx delete mode 100644 src/stories/collection/datasets-list/DatasetsListWithInfiniteScroll.stories.tsx delete mode 100644 src/stories/collection/datasets-list/FileCard.stories.tsx rename tests/component/collection/domain/models/{CollectionPreviewMother.ts => CollectionItemTypePreviewMother.ts} (80%) create mode 100644 tests/component/collection/domain/models/CollectionItemsMother.ts create mode 100644 tests/component/files/domain/models/FileItemTypePreviewMother.ts diff --git a/dev-env/docker-compose-dev.yml b/dev-env/docker-compose-dev.yml index cdefa5ea0..7f6f21c26 100644 --- a/dev-env/docker-compose-dev.yml +++ b/dev-env/docker-compose-dev.yml @@ -66,8 +66,8 @@ services: -Ddataverse.files.s3.connection-pool-size=2048 -Ddataverse.files.s3.custom-endpoint-region=us-east-1 -Ddataverse.files.s3.custom-endpoint-url=https://s3.us-east-1.amazonaws.com - expose: - - '8080' + ports: + - '8080:8080' networks: - dataverse depends_on: diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 8600b0970..af305f16b 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -6,7 +6,7 @@ import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionS import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import { useGetAccumulatedItems } from './useGetAccumulatedItems' import { UseCollectionQueryParamsReturnType } from '../useGetCollectionQueryParams' -import { useLoadMoreOnPopStateEvent } from '../useLoadMoreOnPopStateEvent' +import { useLoadMoreOnPopStateEvent } from './useLoadMoreOnPopStateEvent' import { useLoading } from '@/sections/loading/LoadingContext' import { QueryParamKey } from '../../Route.enum' import { CollectionHelper } from '../CollectionHelper' diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss index d40e87775..9bbcc80ff 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss @@ -18,7 +18,7 @@ .left-side-content { display: flex; flex-wrap: wrap; - column-gap: 8px; + gap: 8px; .affiliation { margin: 0; diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss index c1aa46400..523641e1c 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss @@ -24,7 +24,7 @@ .left-side-content { display: flex; flex-wrap: wrap; - column-gap: 8px; + gap: 8px; } .top-right-icon { @@ -41,7 +41,9 @@ padding-block: 0.25rem; img { - max-width: 40px; + width: 64px; + max-width: 64px; + height: 48px; max-height: 48px; vertical-align: top; } @@ -66,11 +68,18 @@ } .citation-box { + display: -webkit-box; padding: 4px; + overflow: hidden; background-color: color.adjust($dv-primary-color, $lightness: 48%); border-radius: 4px; + -webkit-line-clamp: 5; + line-clamp: 5; + -webkit-box-orient: vertical; - // TODO:ME Could be good to apply line-clamp here also to avoid long citations, we will need to create a selector to match + a { + word-break: break-all; + } &.deaccesioned { background-color: color.adjust($dv-danger-box-color, $lightness: 6%); @@ -88,6 +97,6 @@ -webkit-box-orient: vertical; width: 100%; overflow: hidden; - color: black; + color: #000; } } diff --git a/src/sections/collection/useLoadMoreOnPopStateEvent.ts b/src/sections/collection/collection-items-panel/useLoadMoreOnPopStateEvent.ts similarity index 100% rename from src/sections/collection/useLoadMoreOnPopStateEvent.ts rename to src/sections/collection/collection-items-panel/useLoadMoreOnPopStateEvent.ts diff --git a/src/sections/shared/hierarchy/BreadcrumbsSkeleton.tsx b/src/sections/shared/hierarchy/BreadcrumbsSkeleton.tsx index 42582bee5..b87775d7b 100644 --- a/src/sections/shared/hierarchy/BreadcrumbsSkeleton.tsx +++ b/src/sections/shared/hierarchy/BreadcrumbsSkeleton.tsx @@ -3,7 +3,7 @@ import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' export function BreadcrumbsSkeleton() { return ( - + ) } diff --git a/src/stories/collection/Collection.stories.tsx b/src/stories/collection/Collection.stories.tsx index 92d541d54..4b30b62b1 100644 --- a/src/stories/collection/Collection.stories.tsx +++ b/src/stories/collection/Collection.stories.tsx @@ -2,13 +2,9 @@ import type { Meta, StoryObj } from '@storybook/react' import { Collection } from '../../sections/collection/Collection' import { WithI18next } from '../WithI18next' import { WithLayout } from '../WithLayout' -import { DatasetMockRepository } from '../dataset/DatasetMockRepository' -import { DatasetLoadingMockRepository } from '../dataset/DatasetLoadingMockRepository' -import { NoDatasetsMockRepository } from '../dataset/NoDatasetsMockRepository' import { WithLoggedInUser } from '../WithLoggedInUser' import { CollectionMockRepository } from './CollectionMockRepository' import { CollectionLoadingMockRepository } from './CollectionLoadingMockRepository' -import { NoCollectionMockRepository } from './NoCollectionMockRepository' const meta: Meta = { title: 'Pages/Collection', @@ -26,22 +22,10 @@ type Story = StoryObj export const Default: Story = { render: () => ( - ) -} - -export const InfiniteScrollingEnabled: Story = { - render: () => ( - ) } @@ -49,21 +33,10 @@ export const InfiniteScrollingEnabled: Story = { export const Loading: Story = { render: () => ( - ) -} - -export const NoResults: Story = { - render: () => ( - ) } @@ -72,10 +45,10 @@ export const LoggedIn: Story = { decorators: [WithLoggedInUser], render: () => ( ) } @@ -84,10 +57,10 @@ export const Created: Story = { decorators: [WithLoggedInUser], render: () => ( ) } diff --git a/src/stories/collection/CollectionLoadingMockRepository.ts b/src/stories/collection/CollectionLoadingMockRepository.ts index bb41133e8..cf48f565e 100644 --- a/src/stories/collection/CollectionLoadingMockRepository.ts +++ b/src/stories/collection/CollectionLoadingMockRepository.ts @@ -2,6 +2,9 @@ import { CollectionDTO, CollectionUserPermissions } from '@iqss/dataverse-client import { Collection } from '../../collection/domain/models/Collection' import { CollectionMockRepository } from './CollectionMockRepository' import { CollectionFacet } from '../../collection/domain/models/CollectionFacet' +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '@/collection/domain/models/CollectionItemSubset' +import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria' export class CollectionLoadingMockRepository extends CollectionMockRepository { getById(_id: string): Promise { @@ -16,4 +19,11 @@ export class CollectionLoadingMockRepository extends CollectionMockRepository { getUserPermissions(_collectionIdOrAlias: number | string): Promise { return new Promise(() => {}) } + getItems( + _collectionId: string, + _paginationInfo: CollectionItemsPaginationInfo, + _searchCriteria?: CollectionSearchCriteria + ): Promise { + return new Promise(() => {}) + } } diff --git a/src/stories/collection/CollectionMockRepository.ts b/src/stories/collection/CollectionMockRepository.ts index 6d2030b30..6881605f8 100644 --- a/src/stories/collection/CollectionMockRepository.ts +++ b/src/stories/collection/CollectionMockRepository.ts @@ -6,6 +6,10 @@ import { CollectionDTO } from '../../collection/domain/useCases/DTOs/CollectionD import { CollectionFacet } from '../../collection/domain/models/CollectionFacet' import { CollectionFacetMother } from '../../../tests/component/collection/domain/models/CollectionFacetMother' import { CollectionUserPermissions } from '../../collection/domain/models/CollectionUserPermissions' +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '@/collection/domain/models/CollectionItemSubset' +import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria' +import { CollectionItemsMother } from '../../../tests/component/collection/domain/models/CollectionItemsMother' export class CollectionMockRepository implements CollectionRepository { getById(_id: string): Promise { @@ -38,4 +42,35 @@ export class CollectionMockRepository implements CollectionRepository { }, FakerHelper.loadingTimout()) }) } + + getItems( + _collectionId: string, + paginationInfo: CollectionItemsPaginationInfo, + searchCriteria?: CollectionSearchCriteria + ): Promise { + // TODO:ME Filter based on search criteria now + + const numberOfCollections = Math.floor(paginationInfo.pageSize / 3) + const numberOfDatasets = Math.floor(paginationInfo.pageSize / 3) + const numberOfFiles = paginationInfo.pageSize - numberOfCollections - numberOfDatasets + + const items = CollectionItemsMother.createItems({ + numberOfCollections, + numberOfDatasets, + numberOfFiles + }) + + const filteredByTypeItems = items.filter((item) => + searchCriteria?.itemTypes?.includes(item.type) + ) + + return new Promise((resolve) => { + setTimeout(() => { + resolve({ + items: filteredByTypeItems, + totalItemCount: 200 // This is a fake number, its big so we can always scroll to load more items for the story + }) + }, FakerHelper.loadingTimout()) + }) + } } diff --git a/src/stories/collection/NoCollectionMockRepository.ts b/src/stories/collection/NoCollectionMockRepository.ts index c082d5c82..17cb063a0 100644 --- a/src/stories/collection/NoCollectionMockRepository.ts +++ b/src/stories/collection/NoCollectionMockRepository.ts @@ -1,3 +1,6 @@ +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '@/collection/domain/models/CollectionItemSubset' +import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria' import { FakerHelper } from '../../../tests/component/shared/FakerHelper' import { Collection } from '../../collection/domain/models/Collection' import { CollectionFacet } from '../../collection/domain/models/CollectionFacet' @@ -19,4 +22,19 @@ export class NoCollectionMockRepository extends CollectionMockRepository { }, FakerHelper.loadingTimout()) }) } + + getItems( + _collectionId: string, + _paginationInfo: CollectionItemsPaginationInfo, + _searchCriteria?: CollectionSearchCriteria + ): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve({ + items: [], + totalItemCount: 0 + }) + }, FakerHelper.loadingTimout()) + }) + } } diff --git a/src/stories/collection/datasets-list/CollectionCard.stories.tsx b/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx similarity index 52% rename from src/stories/collection/datasets-list/CollectionCard.stories.tsx rename to src/stories/collection/collection-items-panel/CollectionCard.stories.tsx index ddb26a65a..184c7d6af 100644 --- a/src/stories/collection/datasets-list/CollectionCard.stories.tsx +++ b/src/stories/collection/collection-items-panel/CollectionCard.stories.tsx @@ -1,7 +1,7 @@ import { Meta, StoryObj } from '@storybook/react' import { WithI18next } from '../../WithI18next' -import { CollectionCard } from '../../../sections/collection/datasets-list/collection-card/CollectionCard' -import { CollectionPreviewMother } from '../../../../tests/component/collection/domain/models/CollectionPreviewMother' +import { CollectionCard } from '@/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard' +import { CollectionItemTypePreviewMother } from '../../../../tests/component/collection/domain/models/CollectionItemTypePreviewMother' import { FakerHelper } from '../../../../tests/component/shared/FakerHelper' const meta: Meta = { @@ -14,17 +14,14 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => -} - -export const RequiredOnly: Story = { render: () => ( - + ) } + export const WithLongDescription: Story = { render: () => { - const collectionPreview = CollectionPreviewMother.create({ + const collectionPreview = CollectionItemTypePreviewMother.create({ description: FakerHelper.paragraph(20) }) @@ -33,5 +30,7 @@ export const WithLongDescription: Story = { } export const Unpublished: Story = { - render: () => + render: () => ( + + ) } diff --git a/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx b/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx new file mode 100644 index 000000000..22d95322e --- /dev/null +++ b/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, StoryObj } from '@storybook/react' +import { CollectionItemsPanel } from '@/sections/collection/collection-items-panel/CollectionItemsPanel' +import { WithI18next } from '@/stories/WithI18next' +import { CollectionMockRepository } from '../CollectionMockRepository' + +const meta: Meta = { + title: 'Sections/Collection Page/CollectionItemsPanel', + component: CollectionItemsPanel, + decorators: [WithI18next], + parameters: { + // Sets the delay for all stories. + chromatic: { delay: 15000, pauseAnimationAtEnd: true } + } +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + render: () => ( + + ) +} + +// With Add Data Buttons + +//Loading, NoResults, Error, Also stories for messages + +// diff --git a/src/stories/collection/datasets-list/DatasetCard.stories.tsx b/src/stories/collection/collection-items-panel/DatasetCard.stories.tsx similarity index 73% rename from src/stories/collection/datasets-list/DatasetCard.stories.tsx rename to src/stories/collection/collection-items-panel/DatasetCard.stories.tsx index 90910b822..4ecac85b1 100644 --- a/src/stories/collection/datasets-list/DatasetCard.stories.tsx +++ b/src/stories/collection/collection-items-panel/DatasetCard.stories.tsx @@ -1,6 +1,6 @@ import { Meta, StoryObj } from '@storybook/react' +import { DatasetCard } from '@/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard' import { WithI18next } from '../../WithI18next' -import { DatasetCard } from '../../../sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard' import { DatasetPreviewMother } from '../../../../tests/component/dataset/domain/models/DatasetPreviewMother' const meta: Meta = { @@ -19,3 +19,7 @@ export const Default: Story = { export const Deaccessioned: Story = { render: () => } + +export const WithThumbnail: Story = { + render: () => +} diff --git a/src/stories/collection/collection-items-panel/FileCard.stories.tsx b/src/stories/collection/collection-items-panel/FileCard.stories.tsx new file mode 100644 index 000000000..05a17fea4 --- /dev/null +++ b/src/stories/collection/collection-items-panel/FileCard.stories.tsx @@ -0,0 +1,36 @@ +import { Meta, StoryObj } from '@storybook/react' +import { FileCard } from '@/sections/collection/collection-items-panel/items-list/file-card/FileCard' +import { WithI18next } from '../../WithI18next' +import { FileItemTypePreviewMother } from '../../../../tests/component/files/domain/models/FileItemTypePreviewMother' +import { FakerHelper } from '../../../../tests/component/shared/FakerHelper' + +const meta: Meta = { + title: 'Sections/Collection Page/FileCard', + component: FileCard, + decorators: [WithI18next] +} + +export default meta +type Story = StoryObj + +export const Default: Story = { + render: () => +} + +export const WithLongDescription: Story = { + render: () => { + const filePreview = FileItemTypePreviewMother.create({ + description: FakerHelper.paragraph(20) + }) + + return + } +} + +export const WithDraft: Story = { + render: () => +} + +export const UnpublishedWithDraft: Story = { + render: () => +} diff --git a/src/stories/collection/datasets-list/DatasetsList.stories.tsx b/src/stories/collection/datasets-list/DatasetsList.stories.tsx deleted file mode 100644 index 108a54a90..000000000 --- a/src/stories/collection/datasets-list/DatasetsList.stories.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react' -import { WithI18next } from '../../WithI18next' -import { DatasetMockRepository } from '../../dataset/DatasetMockRepository' -import { DatasetsList } from '../../../sections/collection/datasets-list/DatasetsList' -import { DatasetLoadingMockRepository } from '../../dataset/DatasetLoadingMockRepository' -import { NoDatasetsMockRepository } from '../../dataset/NoDatasetsMockRepository' - -const meta: Meta = { - title: 'Sections/Collection Page/DatasetsList', - component: DatasetsList, - decorators: [WithI18next], - parameters: { - // Sets the delay for all stories. - chromatic: { delay: 15000, pauseAnimationAtEnd: true } - } -} - -export default meta -type Story = StoryObj - -export const Default: Story = { - render: () => -} - -export const Loading: Story = { - render: () => ( - - ) -} - -export const NoResults: Story = { - render: () => ( - - ) -} diff --git a/src/stories/collection/datasets-list/DatasetsListWithInfiniteScroll.stories.tsx b/src/stories/collection/datasets-list/DatasetsListWithInfiniteScroll.stories.tsx deleted file mode 100644 index 88980694a..000000000 --- a/src/stories/collection/datasets-list/DatasetsListWithInfiniteScroll.stories.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react' -import { WithI18next } from '../../WithI18next' -import { DatasetMockRepository } from '../../dataset/DatasetMockRepository' -import { DatasetsListWithInfiniteScroll } from '../../../sections/collection/datasets-list/DatasetsListWithInfiniteScroll' -import { DatasetLoadingMockRepository } from '../../dataset/DatasetLoadingMockRepository' -import { NoDatasetsMockRepository } from '../../dataset/NoDatasetsMockRepository' -import { DatasetErrorMockRepository } from '../../dataset/DatasetErrorMockRepository' - -const meta: Meta = { - title: 'Sections/Collection/DatasetsListWithInfiniteScroll', - component: DatasetsListWithInfiniteScroll, - decorators: [WithI18next] -} - -export default meta -type Story = StoryObj - -export const Default: Story = { - render: () => ( - - ) -} - -export const Loading: Story = { - render: () => ( - - ) -} - -export const NoResults: Story = { - render: () => ( - - ) -} - -export const Error: Story = { - render: () => ( - - ) -} diff --git a/src/stories/collection/datasets-list/FileCard.stories.tsx b/src/stories/collection/datasets-list/FileCard.stories.tsx deleted file mode 100644 index 4c832cb30..000000000 --- a/src/stories/collection/datasets-list/FileCard.stories.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react' -import { WithI18next } from '../../WithI18next' -import { FileCard } from '../../../sections/collection/datasets-list/file-card/FileCard' -import { FilePreviewMother } from '../../../../tests/component/files/domain/models/FilePreviewMother' -import { - FileLabelMother, - FileMetadataMother -} from '../../../../tests/component/files/domain/models/FileMetadataMother' -import { FakerHelper } from '../../../../tests/component/shared/FakerHelper' -import { DatasetPublishingStatus } from '../../../dataset/domain/models/Dataset' - -const meta: Meta = { - title: 'Sections/Collection Page/FileCard', - component: FileCard, - decorators: [WithI18next] -} - -export default meta -type Story = StoryObj - -export const Default: Story = { - render: () => -} -export const TabDelimited: Story = { - render: () => -} -export const WithLongDescription: Story = { - render: () => { - const filePreview = FilePreviewMother.createDefault({ - metadata: FileMetadataMother.createDefault({ - description: FakerHelper.paragraph(20) - }) - }) - return - } -} -export const WithChecksum: Story = { - render: () => ( - - ) -} -export const WithDraft: Story = { - render: () => ( - - ) -} - -export const ReleasedWithDraft: Story = { - render: () => ( - - ) -} -export const WithAllLabels: Story = { - render: () => { - const filePreview = FilePreviewMother.createDefault({ - datasetPublishingStatus: DatasetPublishingStatus.DRAFT, - someDatasetVersionHasBeenReleased: false, - metadata: FileMetadataMother.createDefault({ - description: FakerHelper.paragraph(5), - labels: FileLabelMother.createMany(4) - }) - }) - return - } -} diff --git a/tests/component/collection/domain/models/CollectionPreviewMother.ts b/tests/component/collection/domain/models/CollectionItemTypePreviewMother.ts similarity index 80% rename from tests/component/collection/domain/models/CollectionPreviewMother.ts rename to tests/component/collection/domain/models/CollectionItemTypePreviewMother.ts index 7135d972b..765b0b850 100644 --- a/tests/component/collection/domain/models/CollectionPreviewMother.ts +++ b/tests/component/collection/domain/models/CollectionItemTypePreviewMother.ts @@ -3,7 +3,7 @@ import { FakerHelper } from '../../../shared/FakerHelper' import { CollectionItemTypePreview } from '../../../../../src/collection/domain/models/CollectionItemTypePreview' import { CollectionItemType } from '../../../../../src/collection/domain/models/CollectionItemType' -export class CollectionPreviewMother { +export class CollectionItemTypePreviewMother { static create(props?: Partial): CollectionItemTypePreview { return { type: CollectionItemType.COLLECTION, @@ -21,8 +21,15 @@ export class CollectionPreviewMother { } } + static createMany( + amount: number, + props?: Partial + ): CollectionItemTypePreview[] { + return Array.from({ length: amount }).map(() => this.create(props)) + } + static createRealistic(): CollectionItemTypePreview { - return CollectionPreviewMother.create({ + return CollectionItemTypePreviewMother.create({ isReleased: true, name: 'Scientific Research Collection', alias: 'scientific-research-collection', @@ -37,7 +44,7 @@ export class CollectionPreviewMother { static createWithOnlyRequiredFields( props?: Partial ): CollectionItemTypePreview { - return CollectionPreviewMother.create({ + return CollectionItemTypePreviewMother.create({ name: FakerHelper.collectionName(), isReleased: faker.datatype.boolean(), affiliation: undefined, @@ -47,7 +54,7 @@ export class CollectionPreviewMother { } static createComplete(): CollectionItemTypePreview { - return CollectionPreviewMother.create({ + return CollectionItemTypePreviewMother.create({ isReleased: faker.datatype.boolean(), name: FakerHelper.collectionName(), parentCollectionAlias: faker.datatype.string(10), @@ -58,19 +65,19 @@ export class CollectionPreviewMother { }) } static createUnpublished(): CollectionItemTypePreview { - return CollectionPreviewMother.createWithOnlyRequiredFields({ + return CollectionItemTypePreviewMother.createWithOnlyRequiredFields({ isReleased: false, affiliation: FakerHelper.affiliation() }) } static createWithDescription(): CollectionItemTypePreview { - return CollectionPreviewMother.createWithOnlyRequiredFields({ + return CollectionItemTypePreviewMother.createWithOnlyRequiredFields({ description: FakerHelper.paragraph() }) } static createWithAffiliation(): CollectionItemTypePreview { - return CollectionPreviewMother.createWithOnlyRequiredFields({ + return CollectionItemTypePreviewMother.createWithOnlyRequiredFields({ affiliation: FakerHelper.affiliation() }) } diff --git a/tests/component/collection/domain/models/CollectionItemsMother.ts b/tests/component/collection/domain/models/CollectionItemsMother.ts new file mode 100644 index 000000000..270723cf6 --- /dev/null +++ b/tests/component/collection/domain/models/CollectionItemsMother.ts @@ -0,0 +1,24 @@ +import { CollectionItem } from '@/collection/domain/models/CollectionItemSubset' +import { FileItemTypePreviewMother } from '../../../files/domain/models/FileItemTypePreviewMother' +import { CollectionItemTypePreviewMother } from './CollectionItemTypePreviewMother' +import { DatasetPreviewMother } from '../../../dataset/domain/models/DatasetPreviewMother' + +interface CreateItemsProps { + numberOfCollections?: number + numberOfDatasets?: number + numberOfFiles?: number +} + +export class CollectionItemsMother { + static createItems({ + numberOfCollections = 1, + numberOfDatasets = 1, + numberOfFiles = 1 + }: CreateItemsProps): CollectionItem[] { + const collections = CollectionItemTypePreviewMother.createMany(numberOfCollections) + const datasets = DatasetPreviewMother.createMany(numberOfDatasets) + const files = FileItemTypePreviewMother.createMany(numberOfFiles) + + return [...collections, ...datasets, ...files] + } +} diff --git a/tests/component/dataset/domain/models/DatasetPreviewMother.ts b/tests/component/dataset/domain/models/DatasetPreviewMother.ts index 18f740298..72000ec3c 100644 --- a/tests/component/dataset/domain/models/DatasetPreviewMother.ts +++ b/tests/component/dataset/domain/models/DatasetPreviewMother.ts @@ -21,20 +21,22 @@ export class DatasetPreviewMother { releaseOrCreateDate: FakerHelper.pastDate(), description: faker.lorem.paragraph(), thumbnail: faker.datatype.boolean() ? FakerHelper.getImageUrl() : undefined, + publicationStatuses: [PublicationStatus.Published], + parentCollectionName: faker.lorem.word(), + parentCollectionAlias: faker.lorem.slug(), ...props } - - return new DatasetPreview( - CollectionItemType.DATASET, - datasetPreview.persistentId, - datasetPreview.version, - datasetPreview.releaseOrCreateDate, - datasetPreview.description, - [PublicationStatus.Published], - faker.lorem.word(), - faker.lorem.word(), - datasetPreview.thumbnail - ) + return { + type: CollectionItemType.DATASET, + persistentId: datasetPreview.persistentId, + version: datasetPreview.version, + releaseOrCreateDate: datasetPreview.releaseOrCreateDate, + description: datasetPreview.description, + publicationStatuses: datasetPreview.publicationStatuses, + parentCollectionName: datasetPreview.parentCollectionName, + parentCollectionAlias: datasetPreview.parentCollectionAlias, + thumbnail: datasetPreview.thumbnail + } } static createRealistic(): DatasetPreview { @@ -43,7 +45,8 @@ export class DatasetPreviewMother { static createDraft(): DatasetPreview { return this.create({ - version: DatasetVersionMother.createDraft() + version: DatasetVersionMother.createDraft(), + publicationStatuses: [PublicationStatus.Draft] }) } diff --git a/tests/component/files/domain/models/FileItemTypePreviewMother.ts b/tests/component/files/domain/models/FileItemTypePreviewMother.ts new file mode 100644 index 000000000..eef5b2009 --- /dev/null +++ b/tests/component/files/domain/models/FileItemTypePreviewMother.ts @@ -0,0 +1,77 @@ +import { faker } from '@faker-js/faker' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' +import { FileItemTypePreview } from '@/files/domain/models/FileItemTypePreview' +import { PublicationStatus } from '@/shared/core/domain/models/PublicationStatus' +import { FakerHelper } from '../../../shared/FakerHelper' + +export class FileItemTypePreviewMother { + static create(props?: Partial): FileItemTypePreview { + return { + type: CollectionItemType.FILE, + id: faker.datatype.number(), + name: faker.lorem.words(3), + persistentId: faker.datatype.uuid(), + url: faker.internet.url(), + thumbnail: FakerHelper.getImageUrl(), + description: faker.lorem.paragraph(), + fileType: faker.system.fileType(), + fileContentType: faker.system.mimeType(), + sizeInBytes: faker.datatype.number(), + md5: faker.datatype.uuid(), + checksum: { + type: faker.lorem.word(), + value: faker.datatype.uuid() + }, + unf: faker.datatype.uuid(), + datasetName: faker.lorem.words(3), + datasetId: faker.datatype.number(), + datasetPersistentId: faker.datatype.uuid(), + datasetCitation: faker.lorem.paragraph(), + publicationStatuses: [PublicationStatus.Published], + releaseOrCreateDate: faker.date.past(), + ...props + } + } + + static createMany(amount: number, props?: Partial): FileItemTypePreview[] { + return Array.from({ length: amount }).map(() => this.create(props)) + } + + static createRealistic(): FileItemTypePreview { + return this.create({ + id: 2, + name: 'test file', + persistentId: 'test pid2', + url: 'http://dataverse.com', + thumbnail: 'http://dataverseimage.com', + description: 'test description', + fileType: 'testtype', + fileContentType: 'test/type', + sizeInBytes: 10, + md5: 'testmd5', + checksum: { + type: 'md5', + value: 'testmd5' + }, + unf: 'testunf', + datasetName: 'test dataset', + datasetId: 1, + datasetPersistentId: 'test pid1', + datasetCitation: 'test citation', + publicationStatuses: [PublicationStatus.Published], + releaseOrCreateDate: new Date('2023-05-15T08:21:01Z') + }) + } + + static createWithDraft(): FileItemTypePreview { + return this.create({ + publicationStatuses: [PublicationStatus.Draft] + }) + } + + static createUnpublishedWithDraft(): FileItemTypePreview { + return this.create({ + publicationStatuses: [PublicationStatus.Draft, PublicationStatus.Unpublished] + }) + } +} From 7b0b8955f8b444e3aea2e5d93c6fd9cb46d1ccdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 25 Sep 2024 08:54:00 -0300 Subject: [PATCH 27/42] feat: add stories --- src/sections/collection/CollectionInfo.tsx | 4 +- .../CollectionItemsPanel.tsx | 1 - .../datasets-list/DatasetsList.module.scss | 50 -------- .../collection/datasets-list/DatasetsList.tsx | 71 ------------ .../datasets-list/DatasetsListSkeleton.tsx | 26 ----- .../DatasetsListWithInfiniteScroll.tsx | 109 ------------------ ...atasetsListWithInfiniteScrollSkeletons.tsx | 27 ----- .../datasets-list/ErrorDatasetsMessage.tsx | 11 -- .../datasets-list/NoDatasetsMessage.tsx | 24 ---- .../datasets-list/PageNumberNotFound.tsx | 12 -- .../collection/datasets-list/useDatasets.tsx | 58 ---------- .../datasets-list/useLoadDatasets.tsx | 77 ------------- .../CollectionErrorMockRepository.ts | 37 ++++++ .../CollectionItemsPanel.stories.tsx | 96 ++++++++++++++- .../NoDatasetsMessage.stories.tsx | 23 ---- 15 files changed, 133 insertions(+), 493 deletions(-) delete mode 100644 src/sections/collection/datasets-list/DatasetsList.module.scss delete mode 100644 src/sections/collection/datasets-list/DatasetsList.tsx delete mode 100644 src/sections/collection/datasets-list/DatasetsListSkeleton.tsx delete mode 100644 src/sections/collection/datasets-list/DatasetsListWithInfiniteScroll.tsx delete mode 100644 src/sections/collection/datasets-list/DatasetsListWithInfiniteScrollSkeletons.tsx delete mode 100644 src/sections/collection/datasets-list/ErrorDatasetsMessage.tsx delete mode 100644 src/sections/collection/datasets-list/NoDatasetsMessage.tsx delete mode 100644 src/sections/collection/datasets-list/PageNumberNotFound.tsx delete mode 100644 src/sections/collection/datasets-list/useDatasets.tsx delete mode 100644 src/sections/collection/datasets-list/useLoadDatasets.tsx create mode 100644 src/stories/collection/CollectionErrorMockRepository.ts delete mode 100644 src/stories/collection/datasets-list/NoDatasetsMessage.stories.tsx diff --git a/src/sections/collection/CollectionInfo.tsx b/src/sections/collection/CollectionInfo.tsx index 8c2d2ee45..82c6ef0ae 100644 --- a/src/sections/collection/CollectionInfo.tsx +++ b/src/sections/collection/CollectionInfo.tsx @@ -18,7 +18,9 @@ export function CollectionInfo({ collection }: CollectionInfoProps) { ({collection.affiliation}) )} {!collection.isReleased && ( - Unpublished +
      + Unpublished +
      )}
      diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index af305f16b..eb9a8d129 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -23,7 +23,6 @@ interface CollectionItemsPanelProps { addDataSlot: JSX.Element | null } -// TODO:ME Fix Stories and Mother Mocks objects // TODO:ME New Tests export const CollectionItemsPanel = ({ diff --git a/src/sections/collection/datasets-list/DatasetsList.module.scss b/src/sections/collection/datasets-list/DatasetsList.module.scss deleted file mode 100644 index 3bf17dfb6..000000000 --- a/src/sections/collection/datasets-list/DatasetsList.module.scss +++ /dev/null @@ -1,50 +0,0 @@ -@import 'node_modules/@iqss/dataverse-design-system/src/lib/assets/styles/design-tokens/colors.module'; - -.container { - min-height: calc(100vh + 100px); - padding: 15px; - border: 1px solid #ddd; - border-radius: 4px; -} - -.empty-message-container { - padding: 0.5em 1em; - background: $dv-warning-box-color; -} - -// Infinite scroll enabled -.scrollable-container { - --inline-padding: 1rem; - - height: 650px; - max-height: 650px; - padding-inline: var(--inline-padding); - overflow-x: hidden; - overflow-y: auto; - border: 1px solid #ddd; - border-radius: 4px; - - @media screen and (max-width: 768px) { - --inline-padding: 8px; - } - - @media screen and (min-width: 1280px) { - height: 60vh; - max-height: 60vh; - } - - &--empty-or-error { - padding-block: 1rem; - } - - .sticky-pagination-results { - position: sticky; - top: 0; - z-index: 10; - width: calc(100% + (var(--inline-padding) * 2)); - padding: 1rem var(--inline-padding) 0.5rem; - background-color: var(--bs-white); - box-shadow: 0 0 10px 0 rgba(0 0 0 / 30%); - transform: translateX(calc(var(--inline-padding) * -1)); - } -} diff --git a/src/sections/collection/datasets-list/DatasetsList.tsx b/src/sections/collection/datasets-list/DatasetsList.tsx deleted file mode 100644 index a0240a9cd..000000000 --- a/src/sections/collection/datasets-list/DatasetsList.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useDatasets } from './useDatasets' -import styles from './DatasetsList.module.scss' -import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' -import { useEffect, useState } from 'react' -import { PaginationResultsInfo } from '../../shared/pagination/PaginationResultsInfo' -import { PaginationControls } from '../../shared/pagination/PaginationControls' -import { DatasetPaginationInfo } from '../../../dataset/domain/models/DatasetPaginationInfo' -import { useLoading } from '../../loading/LoadingContext' -import { DatasetsListSkeleton } from './DatasetsListSkeleton' -import { NoDatasetsMessage } from './NoDatasetsMessage' -import { DatasetCard } from './dataset-card/DatasetCard' -import { PageNumberNotFound } from './PageNumberNotFound' - -interface DatasetsListProps { - datasetRepository: DatasetRepository - collectionId: string - page?: number -} - -const NO_DATASETS = 0 -export function DatasetsList({ datasetRepository, page, collectionId }: DatasetsListProps) { - const { setIsLoading } = useLoading() - const [paginationInfo, setPaginationInfo] = useState( - new DatasetPaginationInfo(page) - ) - const { datasets, isLoading, pageNumberNotFound } = useDatasets( - datasetRepository, - collectionId, - setPaginationInfo, - paginationInfo - ) - - useEffect(() => { - setIsLoading(isLoading) - }, [isLoading, setIsLoading]) - - if (isLoading) { - return - } - - if (pageNumberNotFound) { - return ( -
      - -
      - ) - } - - return ( -
      - {datasets.length === NO_DATASETS ? ( - - ) : ( - <> -
      - -
      - {datasets.map((dataset) => ( - - ))} - - - )} -
      - ) -} diff --git a/src/sections/collection/datasets-list/DatasetsListSkeleton.tsx b/src/sections/collection/datasets-list/DatasetsListSkeleton.tsx deleted file mode 100644 index b07bc39d3..000000000 --- a/src/sections/collection/datasets-list/DatasetsListSkeleton.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import styles from './DatasetsList.module.scss' -import Skeleton, { SkeletonTheme } from 'react-loading-skeleton' - -export function DatasetsListSkeleton() { - return ( - -
      -
      - -
      -
      - - - - - - - - - - -
      -
      -
      - ) -} diff --git a/src/sections/collection/datasets-list/DatasetsListWithInfiniteScroll.tsx b/src/sections/collection/datasets-list/DatasetsListWithInfiniteScroll.tsx deleted file mode 100644 index 5372afd92..000000000 --- a/src/sections/collection/datasets-list/DatasetsListWithInfiniteScroll.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { useEffect, useState } from 'react' -import useInfiniteScroll from 'react-infinite-scroll-hook' -import cn from 'classnames' -import { SkeletonTheme } from 'react-loading-skeleton' -import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' -import { PaginationResultsInfo } from '../../shared/pagination/PaginationResultsInfo' -import { DatasetPaginationInfo } from '../../../dataset/domain/models/DatasetPaginationInfo' -import { useLoading } from '../../loading/LoadingContext' -import { NoDatasetsMessage } from './NoDatasetsMessage' -import { DatasetCard } from './dataset-card/DatasetCard' -import { InitialLoadingSkeleton, LoadingSkeleton } from './DatasetsListWithInfiniteScrollSkeletons' -import { ErrorDatasetsMessage } from './ErrorDatasetsMessage' -import { NO_DATASETS, useLoadDatasets } from './useLoadDatasets' -import styles from './DatasetsList.module.scss' - -interface DatasetsListWithInfiniteScrollProps { - datasetRepository: DatasetRepository - collectionId: string -} - -const PAGE_SIZE = 10 -const INITIAL_PAGE = 1 - -export function DatasetsListWithInfiniteScroll({ - datasetRepository, - collectionId -}: DatasetsListWithInfiniteScrollProps) { - const { setIsLoading } = useLoading() - const [paginationInfo, setPaginationInfo] = useState( - new DatasetPaginationInfo(INITIAL_PAGE) - ) - const { - isLoading, - accumulatedDatasets, - totalAvailable, - hasNextPage, - error, - loadMore, - isEmptyDatasets, - areDatasetsAvailable, - accumulatedCount - } = useLoadDatasets(datasetRepository, collectionId, paginationInfo) - - const [sentryRef, { rootRef }] = useInfiniteScroll({ - loading: isLoading, - hasNextPage: hasNextPage, - onLoadMore: loadMore as VoidFunction, - disabled: !!error, - rootMargin: '0px 0px 250px 0px' - }) - - useEffect(() => { - setIsLoading(isLoading) - }, [isLoading, setIsLoading]) - - useEffect(() => { - const updatePaginationTotalItems = () => { - if (totalAvailable && totalAvailable !== paginationInfo.totalItems) { - setPaginationInfo(paginationInfo.withTotal(totalAvailable)) - } - } - - updatePaginationTotalItems() - }, [totalAvailable, paginationInfo]) - - useEffect(() => { - const updatePaginationPageNumber = () => { - setPaginationInfo((currentPagination) => - currentPagination.goToPage(accumulatedCount / PAGE_SIZE + 1) - ) - } - - updatePaginationPageNumber() - }, [accumulatedCount]) - - return ( -
      - {isEmptyDatasets && } - - {error && } - - {areDatasetsAvailable && ( - <> -
      - -
      - {accumulatedDatasets.map((dataset) => ( - - ))} - - )} - - {hasNextPage && !error && !isEmptyDatasets && ( -
      - - {accumulatedCount === NO_DATASETS && } - - -
      - )} -
      - ) -} diff --git a/src/sections/collection/datasets-list/DatasetsListWithInfiniteScrollSkeletons.tsx b/src/sections/collection/datasets-list/DatasetsListWithInfiniteScrollSkeletons.tsx deleted file mode 100644 index 56a97bec6..000000000 --- a/src/sections/collection/datasets-list/DatasetsListWithInfiniteScrollSkeletons.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import styles from './DatasetsList.module.scss' -import Skeleton from 'react-loading-skeleton' - -export const InitialLoadingSkeleton = () => ( - <> -
      - -
      - - - - - - - - -) - -export const LoadingSkeleton = () => ( - <> - - - - -) diff --git a/src/sections/collection/datasets-list/ErrorDatasetsMessage.tsx b/src/sections/collection/datasets-list/ErrorDatasetsMessage.tsx deleted file mode 100644 index a12bb88a5..000000000 --- a/src/sections/collection/datasets-list/ErrorDatasetsMessage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Alert } from '@iqss/dataverse-design-system' - -interface ErrorDatasetsMessageProps { - errorMessage: string -} - -export const ErrorDatasetsMessage = ({ errorMessage }: ErrorDatasetsMessageProps) => ( - - {errorMessage} - -) diff --git a/src/sections/collection/datasets-list/NoDatasetsMessage.tsx b/src/sections/collection/datasets-list/NoDatasetsMessage.tsx deleted file mode 100644 index 345c56e14..000000000 --- a/src/sections/collection/datasets-list/NoDatasetsMessage.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import styles from './DatasetsList.module.scss' -import { Trans, useTranslation } from 'react-i18next' -import { useSession } from '../../session/SessionContext' -import { Route } from '../../Route.enum' - -export function NoDatasetsMessage() { - const { t } = useTranslation('collection') - const { user } = useSession() - - return ( -
      - {user ? ( -

      {t('noDatasetsMessage.authenticated')}

      - ) : ( - -

      - This collection currently has no datasets. Please log in to - see if you are able to add to it. -

      -
      - )} -
      - ) -} diff --git a/src/sections/collection/datasets-list/PageNumberNotFound.tsx b/src/sections/collection/datasets-list/PageNumberNotFound.tsx deleted file mode 100644 index b3feb1cb3..000000000 --- a/src/sections/collection/datasets-list/PageNumberNotFound.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { Alert } from '@iqss/dataverse-design-system' -import { useTranslation } from 'react-i18next' - -export function PageNumberNotFound() { - const { t } = useTranslation('pageNumberNotFound') - - return ( - - {t('message')} - - ) -} diff --git a/src/sections/collection/datasets-list/useDatasets.tsx b/src/sections/collection/datasets-list/useDatasets.tsx deleted file mode 100644 index 2aa14a52b..000000000 --- a/src/sections/collection/datasets-list/useDatasets.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useEffect, useState } from 'react' -import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' -import { getDatasetsWithCount } from '../../../dataset/domain/useCases/getDatasetsWithCount' -import { TotalDatasetsCount } from '../../../dataset/domain/models/TotalDatasetsCount' -import { DatasetPaginationInfo } from '../../../dataset/domain/models/DatasetPaginationInfo' -import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' -import { DatasetsWithCount } from '../../../dataset/domain/models/DatasetsWithCount' - -export function useDatasets( - datasetRepository: DatasetRepository, - collectionId: string, - onPaginationInfoChange: (paginationInfo: DatasetPaginationInfo) => void, - paginationInfo: DatasetPaginationInfo -) { - const [pageNumberNotFound, setPageNumberNotFound] = useState(false) - const [datasets, setDatasets] = useState([]) - const [isLoading, setIsLoading] = useState(true) - const [totalDatasetsCount, setTotalDatasetsCount] = useState() - - const fetchDatasetsWithCount = () => { - return getDatasetsWithCount(datasetRepository, collectionId, paginationInfo).then( - (datasetsWithCount: DatasetsWithCount) => { - setTotalDatasetsCount(totalDatasetsCount) - if (datasetsWithCount.totalCount === 0) { - setIsLoading(false) - return Promise.resolve() - } - if (totalDatasetsCount !== paginationInfo.totalItems) { - paginationInfo = paginationInfo.withTotal(datasetsWithCount.totalCount) - onPaginationInfoChange(paginationInfo) - - if (paginationInfo.page > paginationInfo.totalPages) { - setPageNumberNotFound(true) - setIsLoading(false) - return Promise.resolve() - } - setDatasets(datasetsWithCount.datasetPreviews) - setIsLoading(false) - } - } - ) - } - - useEffect(() => { - setIsLoading(true) - fetchDatasetsWithCount().catch(() => { - console.error('There was an error getting the datasets') - setIsLoading(false) - }) - }, [datasetRepository, paginationInfo.page]) - - return { - datasets, - totalDatasetsCount, - isLoading, - pageNumberNotFound - } -} diff --git a/src/sections/collection/datasets-list/useLoadDatasets.tsx b/src/sections/collection/datasets-list/useLoadDatasets.tsx deleted file mode 100644 index 314fb999f..000000000 --- a/src/sections/collection/datasets-list/useLoadDatasets.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useMemo, useState } from 'react' -import { getDatasetsWithCount } from '../../../dataset/domain/useCases/getDatasetsWithCount' -import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' -import { DatasetRepository } from '../../../dataset/domain/repositories/DatasetRepository' -import { DatasetPaginationInfo } from '../../../dataset/domain/models/DatasetPaginationInfo' -import { DatasetsWithCount } from '../../../dataset/domain/models/DatasetsWithCount' - -export const NO_DATASETS = 0 - -async function loadNextDatasets( - datasetRepository: DatasetRepository, - collectionId: string, - paginationInfo: DatasetPaginationInfo -): Promise { - return getDatasetsWithCount(datasetRepository, collectionId, paginationInfo).catch((_err) => { - throw new Error('Something went wrong getting the datasets') - }) -} - -export function useLoadDatasets( - datasetRepository: DatasetRepository, - collectionId: string, - paginationInfo: DatasetPaginationInfo -) { - const [isLoading, setLoading] = useState(false) - const [accumulatedDatasets, setAccumulatedDatasets] = useState([]) - const [hasNextPage, setHasNextPage] = useState(true) - const [totalAvailable, setTotalAvailable] = useState(undefined) - const [error, setError] = useState(null) - - const isEmptyDatasets = useMemo(() => totalAvailable === NO_DATASETS, [totalAvailable]) - const areDatasetsAvailable = useMemo(() => { - return typeof totalAvailable === 'number' && totalAvailable > NO_DATASETS && !error - }, [totalAvailable, error]) - const accumulatedCount = useMemo(() => accumulatedDatasets.length, [accumulatedDatasets]) - - const loadMore = async () => { - setLoading(true) - try { - const { datasetPreviews, totalCount } = await loadNextDatasets( - datasetRepository, - collectionId, - paginationInfo - ) - const isNextPage = paginationInfo.page * paginationInfo.pageSize < totalCount - - setAccumulatedDatasets((current) => [...current, ...datasetPreviews]) - setTotalAvailable(totalCount) - - setHasNextPage(isNextPage) - - if (!isNextPage) { - setLoading(false) - } - } catch (err) { - const errorMessage = - err instanceof Error && err.message - ? err.message - : 'Something went wrong getting the datasets' - setError(errorMessage) - } finally { - setLoading(false) - } - } - - return { - isLoading, - accumulatedDatasets, - totalAvailable, - hasNextPage, - error, - loadMore, - isEmptyDatasets, - areDatasetsAvailable, - accumulatedCount - } -} diff --git a/src/stories/collection/CollectionErrorMockRepository.ts b/src/stories/collection/CollectionErrorMockRepository.ts new file mode 100644 index 000000000..2d5e8b4e7 --- /dev/null +++ b/src/stories/collection/CollectionErrorMockRepository.ts @@ -0,0 +1,37 @@ +import { CollectionItemsPaginationInfo } from '@/collection/domain/models/CollectionItemsPaginationInfo' +import { CollectionItemSubset } from '@/collection/domain/models/CollectionItemSubset' +import { CollectionSearchCriteria } from '@/collection/domain/models/CollectionSearchCriteria' +import { FakerHelper } from '../../../tests/component/shared/FakerHelper' +import { Collection } from '../../collection/domain/models/Collection' +import { CollectionFacet } from '../../collection/domain/models/CollectionFacet' +import { CollectionMockRepository } from './CollectionMockRepository' + +export class CollectionErrorMockRepository extends CollectionMockRepository { + getById(_id: string): Promise { + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject('Something went wrong') + }, FakerHelper.loadingTimout()) + }) + } + + getFacets(_collectionIdOrAlias: number | string): Promise { + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject('Something went wrong') + }, FakerHelper.loadingTimout()) + }) + } + + getItems( + _collectionId: string, + _paginationInfo: CollectionItemsPaginationInfo, + _searchCriteria?: CollectionSearchCriteria + ): Promise { + return new Promise((_resolve, reject) => { + setTimeout(() => { + reject('Something went wrong') + }, FakerHelper.loadingTimout()) + }) + } +} diff --git a/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx b/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx index 22d95322e..b6da69258 100644 --- a/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx +++ b/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx @@ -1,7 +1,13 @@ import { Meta, StoryObj } from '@storybook/react' import { CollectionItemsPanel } from '@/sections/collection/collection-items-panel/CollectionItemsPanel' +import AddDataActionsButton from '@/sections/shared/add-data-actions/AddDataActionsButton' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import { WithI18next } from '@/stories/WithI18next' +import { WithLoggedInUser } from '@/stories/WithLoggedInUser' import { CollectionMockRepository } from '../CollectionMockRepository' +import { CollectionLoadingMockRepository } from '../CollectionLoadingMockRepository' +import { NoCollectionMockRepository } from '../NoCollectionMockRepository' +import { CollectionErrorMockRepository } from '../CollectionErrorMockRepository' const meta: Meta = { title: 'Sections/Collection Page/CollectionItemsPanel', @@ -27,8 +33,92 @@ export const Default: Story = { ) } -// With Add Data Buttons +export const WithAllFiltersAndSearchValue: Story = { + render: () => ( + + ) +} + +export const WithAddDataButtons: Story = { + render: () => ( + + } + /> + ) +} + +export const LoadingItems: Story = { + render: () => ( + + ) +} + +export const NoSearchMatches: Story = { + render: () => ( + + ) +} -//Loading, NoResults, Error, Also stories for messages +export const NoCollectionDatasetsOrFilesAnonymousUser: Story = { + render: () => ( + + ) +} -// +export const NoCollectionDatasetsOrFilesAuthenticatedUser: Story = { + decorators: [WithLoggedInUser], + render: () => ( + + } + /> + ) +} + +export const WithErrorLoadingItems: Story = { + render: () => ( + + ) +} diff --git a/src/stories/collection/datasets-list/NoDatasetsMessage.stories.tsx b/src/stories/collection/datasets-list/NoDatasetsMessage.stories.tsx deleted file mode 100644 index e4fcbd133..000000000 --- a/src/stories/collection/datasets-list/NoDatasetsMessage.stories.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react' -import { Collection } from '../../../sections/collection/Collection' -import { WithI18next } from '../../WithI18next' -import { NoDatasetsMessage } from '../../../sections/collection/datasets-list/NoDatasetsMessage' -import { WithLoggedInUser } from '../../WithLoggedInUser' - -const meta: Meta = { - title: 'Sections/Collection Page/NoDatasetsMessage', - component: Collection, - decorators: [WithI18next] -} - -export default meta -type Story = StoryObj - -export const AnonymousUser: Story = { - render: () => -} - -export const AuthenticatedUser: Story = { - decorators: [WithLoggedInUser], - render: () => -} From 7a5f3cbf4d051bb00504a608d1695c81c08d966d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 25 Sep 2024 10:10:51 -0300 Subject: [PATCH 28/42] chore: add alias for test folder --- dev-env/vite.config.ts | 5 ++++- tsconfig.json | 3 ++- vite.config.ts | 5 ++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/dev-env/vite.config.ts b/dev-env/vite.config.ts index 17bf71d82..1effd4727 100644 --- a/dev-env/vite.config.ts +++ b/dev-env/vite.config.ts @@ -23,6 +23,9 @@ export default defineConfig({ } }, resolve: { - alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }] + alias: { + '@': path.resolve(__dirname, 'src'), + '@tests': path.resolve(__dirname, 'tests') + } } }) diff --git a/tsconfig.json b/tsconfig.json index 3b85af474..7a25b67d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,8 @@ "jsx": "react-jsx", "paths": { - "@/*": ["./src/*"] + "@/*": ["./src/*"], + "@tests/*": ["./tests/*"] } }, "include": [ diff --git a/vite.config.ts b/vite.config.ts index 1330a9112..c255fb8af 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -15,6 +15,9 @@ export default defineConfig({ port: 5173 }, resolve: { - alias: [{ find: '@', replacement: path.resolve(__dirname, 'src') }] + alias: { + '@': path.resolve(__dirname, 'src'), + '@tests': path.resolve(__dirname, 'tests') + } } }) From ed212dee694de03fe2ca65eb7707c58f9c4e7db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Wed, 25 Sep 2024 17:17:50 -0300 Subject: [PATCH 29/42] test: unit test in progress --- .../CollectionItemsPanel.tsx | 6 +- .../items-list/ItemsList.tsx | 2 +- .../CollectionCard.module.scss | 4 +- .../collection-card/CollectionCardHelper.ts | 18 -- .../items-list/file-card/FileCardHelper.ts | 7 +- .../search-panel/SearchPanel.tsx | 1 + .../CollectionCard.stories.tsx | 6 + .../models/CollectionItemTypePreviewMother.ts | 6 + .../models/FileItemTypePreviewMother.ts | 5 +- .../sections/collection/Collection.spec.tsx | 126 ++++-------- .../CollectionItemsPanel.spec.tsx | 191 ++++++++++++++++++ .../SearchPanel.spec.tsx | 49 +++++ .../TypeFilters.spec.tsx | 124 ++++++++++++ .../collection-card/CollectionCard.spec.tsx | 26 +++ .../dataset-card/DatasetCard.spec.tsx | 17 ++ .../dataset-card/DatasetCardHeader.spec.tsx | 4 +- .../dataset-card/DatasetCardInfo.spec.tsx | 8 +- .../DatasetCardThumbnail.spec.tsx | 4 +- .../file-card/FileCard.spec.tsx | 18 ++ .../file-card/FileCardHeader.spec.tsx | 62 ++++++ .../file-card/FileCardThumbnail.spec.tsx | 37 ++++ .../datasets-list/DatasetsList.spec.tsx | 81 -------- .../DatasetsListWithInfiniteScroll.spec.tsx | 97 --------- .../datasets-list/NoDatasetsMessage.spec.tsx | 20 -- .../collection-card/CollectionCard.spec.tsx | 18 -- .../dataset-card/DatasetCard.spec.tsx | 22 -- .../datasets-list/file-card/FileCard.spec.tsx | 19 -- 27 files changed, 597 insertions(+), 381 deletions(-) delete mode 100644 src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts create mode 100644 tests/component/sections/collection/collection-items-panel/CollectionItemsPanel.spec.tsx create mode 100644 tests/component/sections/collection/collection-items-panel/SearchPanel.spec.tsx create mode 100644 tests/component/sections/collection/collection-items-panel/TypeFilters.spec.tsx create mode 100644 tests/component/sections/collection/collection-items-panel/collection-card/CollectionCard.spec.tsx create mode 100644 tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCard.spec.tsx rename tests/component/sections/collection/{datasets-list => collection-items-panel}/dataset-card/DatasetCardHeader.spec.tsx (80%) rename tests/component/sections/collection/{datasets-list => collection-items-panel}/dataset-card/DatasetCardInfo.spec.tsx (75%) rename tests/component/sections/collection/{datasets-list => collection-items-panel}/dataset-card/DatasetCardThumbnail.spec.tsx (81%) create mode 100644 tests/component/sections/collection/collection-items-panel/file-card/FileCard.spec.tsx create mode 100644 tests/component/sections/collection/collection-items-panel/file-card/FileCardHeader.spec.tsx create mode 100644 tests/component/sections/collection/collection-items-panel/file-card/FileCardThumbnail.spec.tsx delete mode 100644 tests/component/sections/collection/datasets-list/DatasetsList.spec.tsx delete mode 100644 tests/component/sections/collection/datasets-list/DatasetsListWithInfiniteScroll.spec.tsx delete mode 100644 tests/component/sections/collection/datasets-list/NoDatasetsMessage.spec.tsx delete mode 100644 tests/component/sections/collection/datasets-list/collection-card/CollectionCard.spec.tsx delete mode 100644 tests/component/sections/collection/datasets-list/dataset-card/DatasetCard.spec.tsx delete mode 100644 tests/component/sections/collection/datasets-list/file-card/FileCard.spec.tsx diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index eb9a8d129..bffbbc0be 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -24,6 +24,8 @@ interface CollectionItemsPanelProps { } // TODO:ME New Tests +// TODO:ME Add all locales in translation files +// TODO:ME Add documentation about how it works and link to some react router dom docs export const CollectionItemsPanel = ({ collectionId, @@ -76,6 +78,7 @@ export const CollectionItemsPanel = ({ } } + // TODO:ME This code will be test with an e2e test because it updates the URL const handleSearchSubmit = async (searchValue: string) => { itemsListContainerRef.current?.scrollTo({ top: 0 }) @@ -115,7 +118,7 @@ export const CollectionItemsPanel = ({ } } - // WHEN APPLYING FILTERS, WE RESET THE PAGINATION INFO AND IF SEARCH VALUE EXISTS, WE KEEP IT!! + // TODO:ME This code will be test with an e2e test because it updates the URL const handleItemsTypeChange = async (itemTypeChange: ItemTypeChange) => { const { type, checked } = itemTypeChange @@ -151,6 +154,7 @@ export const CollectionItemsPanel = ({ } } + // TODO:ME This code will MAYBE? be test with an e2e test because it updates the URL async function loadItemsOnBackAndForwardNavigation() { const searchParams = new URLSearchParams(window.location.search) const collectionQueryParams = CollectionHelper.defineCollectionQueryParams(searchParams) diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index 55bd6457c..c8fe907b5 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -87,7 +87,7 @@ export const ItemsList = forwardRef( )} -
        +
          {items.map((collectionItem, index) => (
        • {collectionItem?.type === CollectionItemType.COLLECTION && ( diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss index 9bbcc80ff..a3eabca8f 100644 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss +++ b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCard.module.scss @@ -47,7 +47,9 @@ padding-block: 0.25rem; img { - max-width: 40px; + width: 64px; + max-width: 64px; + height: 48px; max-height: 48px; vertical-align: top; } diff --git a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts deleted file mode 100644 index 4d6c52ee9..000000000 --- a/src/sections/collection/collection-items-panel/items-list/collection-card/CollectionCardHelper.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - DatasetLabel, - DatasetLabelSemanticMeaning, - DatasetLabelValue -} from '@/dataset/domain/models/Dataset' - -export class CollectionCardHelper { - static getLabel(isReleased: boolean) { - const labels: DatasetLabel[] = [] - - if (!isReleased) { - labels.push( - new DatasetLabel(DatasetLabelSemanticMeaning.WARNING, DatasetLabelValue.UNPUBLISHED) - ) - } - return labels - } -} diff --git a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts index c55eebff1..cdd60c36b 100644 --- a/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts +++ b/src/sections/collection/collection-items-panel/items-list/file-card/FileCardHelper.ts @@ -24,16 +24,13 @@ export class FileCardHelper { static getDatasetLabels( publicationStatuses: PublicationStatus[], - someDatasetVersionHasBeenReleased: boolean | undefined + someDatasetVersionHasBeenReleased: boolean ) { const labels: DatasetLabel[] = [] if (publicationStatuses.includes(PublicationStatus.Draft)) { labels.push(new DatasetLabel(DatasetLabelSemanticMeaning.DATASET, DatasetLabelValue.DRAFT)) } - if ( - someDatasetVersionHasBeenReleased == undefined || - someDatasetVersionHasBeenReleased == false - ) { + if (!someDatasetVersionHasBeenReleased) { labels.push( new DatasetLabel(DatasetLabelSemanticMeaning.WARNING, DatasetLabelValue.UNPUBLISHED) ) diff --git a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx index 5071f4c2c..e701dd21c 100644 --- a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx +++ b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx @@ -47,6 +47,7 @@ export const SearchPanel = ({ /> - Filter Results + {t('filterResults')}
          diff --git a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx index ec221ba12..e5d381cb4 100644 --- a/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx +++ b/src/sections/collection/collection-items-panel/filter-panel/type-filters/TypeFilters.tsx @@ -1,4 +1,5 @@ import { ChangeEvent } from 'react' +import { useTranslation } from 'react-i18next' import { Form, Icon, IconName, Stack } from '@iqss/dataverse-design-system' import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import styles from './TypeFilters.module.scss' @@ -19,6 +20,8 @@ export const TypeFilters = ({ onItemTypesChange, isLoadingCollectionItems }: TypeFiltersProps) => { + const { t } = useTranslation('collection') + const handleItemTypeChange = (type: CollectionItemType, checked: boolean) => { onItemTypesChange({ type, checked }) } @@ -45,7 +48,7 @@ export const TypeFilters = ({ label={ <> - Collections + {t('collectionFilterTypeLabel')} } checked={Boolean(currentItemTypes?.includes(CollectionItemType.COLLECTION))} @@ -59,7 +62,7 @@ export const TypeFilters = ({ label={ <> - Datasets + {t('datasetFilterTypeLabel')} } checked={Boolean(currentItemTypes?.includes(CollectionItemType.DATASET))} @@ -73,7 +76,7 @@ export const TypeFilters = ({ label={ <> - Files + {t('fileFilterTypeLabel')} } checked={Boolean(currentItemTypes?.includes(CollectionItemType.FILE))} diff --git a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx index e701dd21c..47c08db60 100644 --- a/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx +++ b/src/sections/collection/collection-items-panel/search-panel/SearchPanel.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' import { Button, Form } from '@iqss/dataverse-design-system' import { Search } from 'react-bootstrap-icons' import styles from './SearchPanel.module.scss' @@ -14,6 +15,8 @@ export const SearchPanel = ({ isLoadingCollectionItems, onSubmitSearch }: SearchPanelProps) => { + const { t } = useTranslation('collection') + const [searchValue, setSearchValue] = useState(currentSearchValue) const handleSubmit = (event: React.FormEvent) => { @@ -40,7 +43,7 @@ export const SearchPanel = ({ } - aria-label="Search submit" + aria-label={t('searchSubmitButtonLabel')} disabled={isLoadingCollectionItems} /> From 0cdbd6b13e5aff31504702d41e1ee6548d682916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 26 Sep 2024 08:52:05 -0300 Subject: [PATCH 34/42] test: fix e2e test --- .../collection-items-panel/CollectionItemsPanel.tsx | 2 +- .../sections/collection/CollectionItemsPanel.spec.ts | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 835ded0ad..276a2aba2 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -23,7 +23,7 @@ interface CollectionItemsPanelProps { addDataSlot: JSX.Element | null } -// TODO:ME Add all locales in translation files +// TODO:ME Add tests for off canvas component /** * HOW IT WORKS: diff --git a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts index 7a963dcad..c4c667ad1 100644 --- a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts +++ b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts @@ -91,16 +91,15 @@ describe('Collection Items Panel', () => { cy.findByRole('checkbox', { name: /Collections/ }).click() cy.findByRole('checkbox', { name: /Datasets/ }).click() - cy.findByText('8 results').should('exist') - const fourthExpectedURL = new URLSearchParams({ [QueryParamKey.COLLECTION_ITEM_TYPES]: [CollectionItemType.FILE].join(',') }).toString() cy.url().should('include', `/collections?${fourthExpectedURL}`) + cy.findByText('8 results').should('exist') cy.findByTestId('items-list').should('exist').children().should('have.length', 8) - // 5 - Now check all to get 16 total items, scroll to the bottom and assert that 16 of 16 results displayed + // 5 - Now check all to get 16 total items by scroll to the bottom and assert that 16 of 16 results displayed cy.findByRole('checkbox', { name: /Collections/ }).click() cy.findByRole('checkbox', { name: /Datasets/ }).click() @@ -122,5 +121,12 @@ describe('Collection Items Panel', () => { cy.findByText('16 of 16 results displayed').should('exist') cy.findByTestId('items-list').should('exist').children().should('have.length', 16) + + // 6 - Navigate baack with the browser and assert that the url is updated correctly and the items are displayed correctly as in step 4 + cy.go('back') + + cy.url().should('include', `/collections?${fourthExpectedURL}`) + cy.findByText('8 results').should('exist') + cy.findByTestId('items-list').should('exist').children().should('have.length', 8) }) }) From 7b9c3e50fe9628df3d3c854d932b24e42db12beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 26 Sep 2024 09:36:28 -0300 Subject: [PATCH 35/42] test: unit test for new offcanvas --- .../lib/components/offcanvas/Offcanvas.tsx | 2 +- .../component/off-canvas/Offcanvas.spec.tsx | 79 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 packages/design-system/tests/component/off-canvas/Offcanvas.spec.tsx diff --git a/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx b/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx index 5454be337..5b53bedea 100644 --- a/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx +++ b/packages/design-system/src/lib/components/offcanvas/Offcanvas.tsx @@ -15,7 +15,7 @@ interface OffcanvasProps { } const Offcanvas = ({ - show = false, + show, placement = 'start', responsive, onHide, diff --git a/packages/design-system/tests/component/off-canvas/Offcanvas.spec.tsx b/packages/design-system/tests/component/off-canvas/Offcanvas.spec.tsx new file mode 100644 index 000000000..7ae621832 --- /dev/null +++ b/packages/design-system/tests/component/off-canvas/Offcanvas.spec.tsx @@ -0,0 +1,79 @@ +import { useState } from 'react' +import { Offcanvas } from '../../../src/lib/components/offcanvas/Offcanvas' + +const OffcanvasWithTrigger = ({ + placement, + responsive, + withCloseButton +}: { + placement?: 'start' | 'end' | 'top' | 'bottom' + responsive?: 'sm' | 'md' | 'lg' | 'xl' | 'xxl' + withCloseButton?: boolean +}) => { + const [show, setShow] = useState(false) + + const handleClose = () => setShow(false) + const handleShow = () => setShow(true) + + return ( +
          + + + + + Offcanvas Title + + + {responsive ? ( +
          +

          Resize your browser to show the responsive offcanvas toggle.

          +

          + Responsive offcanvas classes hide content outside the viewport from a specified + breakpoint and down. Above that breakpoint, the contents within will behave as + usual. +

          +
          + ) : ( +

          All the content goes here

          + )} +
          +
          +
          + ) +} + +describe('Offcanvas', () => { + it('opens and close correctly by a button', () => { + cy.viewport(375, 700) + + cy.mount() + + cy.findByTestId('off-canvas-body').should('not.be.visible') + + cy.findByRole('button', { name: /Open Offcanvas/i }).click() + cy.findByTestId('off-canvas-body').should('be.visible') + + cy.findByLabelText(/Close/).click() + cy.findByTestId('off-canvas-body').should('not.be.visible') + }) + + it('is shown automatically after lg screens (992px)', () => { + cy.viewport(1200, 700) + + cy.mount() + + cy.findByTestId('off-canvas-body').should('be.visible') + }) + + it('is not show an placement start as default props if not passed', () => { + cy.viewport(375, 700) + + cy.mount() + + cy.findByTestId('off-canvas-body').should('not.be.visible') + + cy.findByRole('button', { name: /Open Offcanvas/i }).click() + + cy.findByRole('dialog').should('have.class', 'offcanvas-start') + }) +}) From 5b52f8a1de0346fc5410b842f614c4543edc6e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 26 Sep 2024 11:22:20 -0300 Subject: [PATCH 36/42] test: more robust e2e test --- .../collection/CollectionItemsPanel.spec.ts | 273 +++++++++++++----- 1 file changed, 207 insertions(+), 66 deletions(-) diff --git a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts index c4c667ad1..4fe65a57d 100644 --- a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts +++ b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts @@ -1,11 +1,37 @@ +import { CollectionItem } from '@/collection/domain/models/CollectionItemSubset' import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import { QueryParamKey } from '@/sections/Route.enum' import { DatasetHelper } from '@tests/e2e-integration/shared/datasets/DatasetHelper' import { FileHelper } from '@tests/e2e-integration/shared/files/FileHelper' import { TestsUtils } from '@tests/e2e-integration/shared/TestsUtils' +import { Interception } from 'cypress/types/net-stubbing' const numbersOfDatasetsToCreate = [1, 2, 3, 4, 5, 6, 7, 8] +const SEARCH_ENDPOINT_REGEX = /^\/api\/v1\/search(\?.*)?$/ + +function extractInfoFromInterceptedResponse(interception: Interception) { + const totalCount = interception?.response?.body?.data.total_count as number + const totalItemsInResponse = interception?.response?.body?.data.items.length as number + const collectionsInResponse = ( + interception?.response?.body?.data.items as CollectionItem[] + ).filter((item: CollectionItem) => item.type === CollectionItemType.COLLECTION) + const datasetsInResponse = (interception?.response?.body?.data.items as CollectionItem[]).filter( + (item: CollectionItem) => item.type === CollectionItemType.DATASET + ) + const filesInResponse = (interception?.response?.body?.data.items as CollectionItem[]).filter( + (item: CollectionItem) => item.type === CollectionItemType.FILE + ) + + return { + totalCount, + totalItemsInResponse, + collectionsInResponse, + datasetsInResponse, + filesInResponse + } +} + describe('Collection Items Panel', () => { before(() => { TestsUtils.setup() @@ -13,6 +39,8 @@ describe('Collection Items Panel', () => { }) beforeEach(async () => { + cy.intercept(SEARCH_ENDPOINT_REGEX).as('getCollectionItems') + // Creates 8 datasets with 1 file each for (const _number of numbersOfDatasetsToCreate) { await DatasetHelper.createWithFile(FileHelper.create()) @@ -25,108 +53,221 @@ describe('Collection Items Panel', () => { }) }) - //TODO: I cant call CollectionHelper with a destroy method because it gives me an error, so only testing dataset and filter items - it.only('performs different search, filtering and respond to back and forward navigation', () => { cy.visit(`/spa/collections`) - cy.wait(4_000) + cy.wait('@getCollectionItems').then((interception) => { + console.log('interception', interception) + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) + + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) + + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) - // Should show 8 items: 8 Datasets only because by default if no query params are present in the URL, the item types by default are COLLECTION and DATASET. - cy.findByTestId('items-list').should('exist').children().should('have.length', 8) - cy.findByText('8 results').should('exist') + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) - // 1 - Now select checkbox file, assert that 10 of 16 results displayed and url is updated correctly + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) + }) + + // 1 - Now select the Files checkbox cy.findByRole('checkbox', { name: /Files/ }).click() - const firstExpectedURL = new URLSearchParams({ - [QueryParamKey.COLLECTION_ITEM_TYPES]: [ - CollectionItemType.COLLECTION, - CollectionItemType.DATASET, - CollectionItemType.FILE - ].join(',') - }).toString() + cy.wait('@getCollectionItems').then((interception) => { + console.log('interception', interception) + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) + + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) + + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) + + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) - cy.url().should('include', `/collections?${firstExpectedURL}`) + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) - cy.findByText('10 of 16 results displayed').should('exist') - cy.findByTestId('items-list').should('exist').children().should('have.length', 10) + const firstExpectedURL = new URLSearchParams({ + [QueryParamKey.COLLECTION_ITEM_TYPES]: [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ].join(',') + }).toString() - // 2 - Now perform a search in the input and validate that the search is performed correctly and the url is updated correctly + cy.url().should('include', `/collections?${firstExpectedURL}`) + }) + + // 2 - Now perform a search in the input cy.findByPlaceholderText('Search this collection...').type('Darwin{enter}') - const secondExpectedURL = new URLSearchParams({ - [QueryParamKey.COLLECTION_ITEM_TYPES]: [ - CollectionItemType.COLLECTION, - CollectionItemType.DATASET, - CollectionItemType.FILE - ].join(','), - [QueryParamKey.QUERY]: 'Darwin' - }).toString() + cy.wait('@getCollectionItems').then((interception) => { + console.log('interception', interception) + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) + + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) + + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) + + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) - cy.url().should('include', `/collections?${secondExpectedURL}`) + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) - cy.findByText('8 results').should('exist') - cy.findByTestId('items-list').should('exist').children().should('have.length', 8) + const secondExpectedURL = new URLSearchParams({ + [QueryParamKey.COLLECTION_ITEM_TYPES]: [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ].join(','), + [QueryParamKey.QUERY]: 'Darwin' + }).toString() + + cy.url().should('include', `/collections?${secondExpectedURL}`) + }) // 3 - Clear the search and assert that the search is performed correctly and the url is updated correctly cy.findByPlaceholderText('Search this collection...').clear() cy.findByRole('button', { name: /Search submit/ }).click() - const thirdExpectedURL = new URLSearchParams({ - [QueryParamKey.COLLECTION_ITEM_TYPES]: [ - CollectionItemType.COLLECTION, - CollectionItemType.DATASET, - CollectionItemType.FILE - ].join(',') - }).toString() + cy.wait('@getCollectionItems').then((interception) => { + console.log('interception', interception) + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) - cy.url().should('include', `/collections?${thirdExpectedURL}`) + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) - cy.findByText('10 of 16 results displayed').should('exist') - cy.findByTestId('items-list').should('exist').children().should('have.length', 10) + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) - // 4 - Uncheck all and check only Files, assert that 8 results displayed and url is updated correctly + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) - cy.findByRole('checkbox', { name: /Collections/ }).click() - cy.findByRole('checkbox', { name: /Datasets/ }).click() + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) - const fourthExpectedURL = new URLSearchParams({ - [QueryParamKey.COLLECTION_ITEM_TYPES]: [CollectionItemType.FILE].join(',') - }).toString() + const thirdExpectedURL = new URLSearchParams({ + [QueryParamKey.COLLECTION_ITEM_TYPES]: [ + CollectionItemType.COLLECTION, + CollectionItemType.DATASET, + CollectionItemType.FILE + ].join(',') + }).toString() - cy.url().should('include', `/collections?${fourthExpectedURL}`) - cy.findByText('8 results').should('exist') - cy.findByTestId('items-list').should('exist').children().should('have.length', 8) + cy.url().should('include', `/collections?${thirdExpectedURL}`) + }) + + // 4 - Uncheck the Collections checkbox - // 5 - Now check all to get 16 total items by scroll to the bottom and assert that 16 of 16 results displayed cy.findByRole('checkbox', { name: /Collections/ }).click() + + cy.wait('@getCollectionItems').then((interception) => { + console.log('interception', interception) + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) + + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) + + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) + + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) + + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) + + const fourthExpectedURL = new URLSearchParams({ + [QueryParamKey.COLLECTION_ITEM_TYPES]: [ + CollectionItemType.DATASET, + CollectionItemType.FILE + ].join(',') + }).toString() + + cy.url().should('include', `/collections?${fourthExpectedURL}`) + }) + + // 5 - Uncheck the Dataset checkbox cy.findByRole('checkbox', { name: /Datasets/ }).click() - cy.findByText('10 of 16 results displayed').should('exist') - cy.findByTestId('items-list').should('exist').children().should('have.length', 10) + cy.wait('@getCollectionItems').then((interception) => { + console.log('interception', interception) + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) - cy.findByTestId('items-list-scrollable-container').scrollTo('bottom') + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) - const fifthExpectedURL = new URLSearchParams({ - [QueryParamKey.COLLECTION_ITEM_TYPES]: [ - CollectionItemType.FILE, - CollectionItemType.COLLECTION, - CollectionItemType.DATASET - ].join(',') - }).toString() + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) - cy.url().should('include', `/collections?${fifthExpectedURL}`) + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) - cy.findByText('16 of 16 results displayed').should('exist') + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) - cy.findByTestId('items-list').should('exist').children().should('have.length', 16) + const fifthExpectedURL = new URLSearchParams({ + [QueryParamKey.COLLECTION_ITEM_TYPES]: [CollectionItemType.FILE].join(',') + }).toString() - // 6 - Navigate baack with the browser and assert that the url is updated correctly and the items are displayed correctly as in step 4 + cy.url().should('include', `/collections?${fifthExpectedURL}`) + }) + + //6 - Navigate back with the browser and assert that the url is updated correctly and the items are displayed correctly as in step 4 cy.go('back') - cy.url().should('include', `/collections?${fourthExpectedURL}`) - cy.findByText('8 results').should('exist') - cy.findByTestId('items-list').should('exist').children().should('have.length', 8) + cy.wait('@getCollectionItems').then((interception) => { + const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = + extractInfoFromInterceptedResponse(interception) + + cy.findByTestId('items-list') + .should('exist') + .children() + .should('have.length', totalItemsInResponse) + + collectionsInResponse.length > 0 && + cy.findAllByTestId('collection-card').should('have.length', collectionsInResponse.length) + + datasetsInResponse.length > 0 && + cy.findAllByTestId('dataset-card').should('have.length', datasetsInResponse.length) + + filesInResponse.length > 0 && + cy.findAllByTestId('file-card').should('have.length', filesInResponse.length) + + const fourthExpectedURL = new URLSearchParams({ + [QueryParamKey.COLLECTION_ITEM_TYPES]: [ + CollectionItemType.DATASET, + CollectionItemType.FILE + ].join(',') + }).toString() + + cy.url().should('include', `/collections?${fourthExpectedURL}`) + }) }) }) From e35605cb64c94a0384b19034edc556cc3ee44fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Thu, 26 Sep 2024 15:56:18 -0300 Subject: [PATCH 37/42] feat: update no items message to show correct message based on type of filters applied and update test --- public/locales/en/collection.json | 13 +- .../CollectionItemsPanel.tsx | 1 + .../items-list/ItemsList.tsx | 7 +- .../items-list/NoItemsMessage.tsx | 66 ++++++- .../CollectionItemsPanel.spec.tsx | 171 ++++++++++++++++-- .../collection/CollectionItemsPanel.spec.ts | 8 +- 6 files changed, 228 insertions(+), 38 deletions(-) diff --git a/public/locales/en/collection.json b/public/locales/en/collection.json index 24c83b1e5..d45c4b482 100644 --- a/public/locales/en/collection.json +++ b/public/locales/en/collection.json @@ -4,8 +4,17 @@ "anonymous": "This collection currently has no datasets. Please <1>log in to see if you are able to add to it." }, "noItemsMessage": { - "authenticated": "This collection currently has no collections, datasets or files. You can add to it by using the Add Data button on this page.", - "anonymous": "This collection currently has no collections, datasets or files. Please <1>log in to see if you are able to add to it." + "authenticated": "This collection currently has no {{typeOfEmptyItems}}. You can add to it by using the Add Data button on this page.", + "anonymous": "This collection currently has no {{typeOfEmptyItems}}. Please <1>log in to see if you are able to add to it.", + "itemTypeMessage": { + "all": "collections, datasets or files", + "collection": "collections", + "dataset": "datasets", + "file": "files", + "collectionAndDataset": "collections or datasets", + "collectionAndFile": "collections or files", + "datasetAndFile": "datasets or files" + } }, "noSearchMatches": "There are no collections, datasets, or files that match your search. Please try a new search by using other or broader terms. You can also check out the search guide for tips.", "createdAlert": "You have successfully created your collection! To learn more about what you can do with your collection, check out the User Guide.", diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index 276a2aba2..df031c29a 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -216,6 +216,7 @@ export const CollectionItemsPanel = ({ hasNextPage={hasNextPage} isEmptyItems={isEmptyItems} hasSearchValue={currentSearchCriteria.hasSearchText()} + itemsTypesSelected={currentSearchCriteria.itemTypes as CollectionItemType[]} paginationInfo={paginationInfo} onBottomReach={handleLoadMoreOnBottomReach} ref={itemsListContainerRef} diff --git a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx index bd3e9ef30..99bed2d79 100644 --- a/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx +++ b/src/sections/collection/collection-items-panel/items-list/ItemsList.tsx @@ -26,6 +26,7 @@ interface ItemsListProps { hasSearchValue: boolean paginationInfo: CollectionItemsPaginationInfo onBottomReach: (paginationInfo: CollectionItemsPaginationInfo) => void + itemsTypesSelected: CollectionItemType[] } export const ItemsList = forwardRef( @@ -40,7 +41,8 @@ export const ItemsList = forwardRef( isEmptyItems, hasSearchValue, paginationInfo, - onBottomReach + onBottomReach, + itemsTypesSelected }: ItemsListProps, ref ) => { @@ -67,7 +69,8 @@ export const ItemsList = forwardRef( tabIndex={0} ref={ref as ForwardedRef} data-testid="items-list-scrollable-container"> - {showNoItemsMessage && } + {showNoItemsMessage && } + {showNoSearchMatchesMessage && } {error && } diff --git a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx index a35be5798..6b568040f 100644 --- a/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx +++ b/src/sections/collection/collection-items-panel/items-list/NoItemsMessage.tsx @@ -1,23 +1,73 @@ import { Trans, useTranslation } from 'react-i18next' import { useSession } from '@/sections/session/SessionContext' import { Route } from '@/sections/Route.enum' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' import styles from './ItemsList.module.scss' -export function NoItemsMessage() { +interface NoItemsMessageProps { + itemsTypesSelected: CollectionItemType[] +} + +export function NoItemsMessage({ itemsTypesSelected }: NoItemsMessageProps) { const { t } = useTranslation('collection') const { user } = useSession() + const itemTypeMessages = { + all: t('noItemsMessage.itemTypeMessage.all'), + [CollectionItemType.COLLECTION]: t('noItemsMessage.itemTypeMessage.collection'), + [CollectionItemType.DATASET]: t('noItemsMessage.itemTypeMessage.dataset'), + [CollectionItemType.FILE]: t('noItemsMessage.itemTypeMessage.file'), + collectionAndDataset: t('noItemsMessage.itemTypeMessage.collectionAndDataset'), + collectionAndFile: t('noItemsMessage.itemTypeMessage.collectionAndFile'), + datasetAndFile: t('noItemsMessage.itemTypeMessage.datasetAndFile') + } + + const getMessageKey = (itemsTypesSelected: CollectionItemType[]) => { + const itemCount = itemsTypesSelected.length + + if (itemCount === 3) return itemTypeMessages.all + if (itemCount === 1) { + const itemType = itemsTypesSelected[0] + return itemTypeMessages[itemType] + } + + if (itemCount === 2) { + if ( + itemsTypesSelected.includes(CollectionItemType.COLLECTION) && + itemsTypesSelected.includes(CollectionItemType.DATASET) + ) { + return itemTypeMessages.collectionAndDataset + } + if ( + itemsTypesSelected.includes(CollectionItemType.COLLECTION) && + itemsTypesSelected.includes(CollectionItemType.FILE) + ) { + return itemTypeMessages.collectionAndFile + } + if ( + itemsTypesSelected.includes(CollectionItemType.DATASET) && + itemsTypesSelected.includes(CollectionItemType.FILE) + ) { + return itemTypeMessages.datasetAndFile + } + } + } + + const messageKey = getMessageKey(itemsTypesSelected) + return (
          {user ? ( -

          {t('noItemsMessage.authenticated')}

          +

          {t('noItemsMessage.authenticated', { typeOfEmptyItems: messageKey })}

          ) : ( - -

          - This collection currently has no collections, datasets or files. Please{' '} - log in to see if you are able to add to it. -

          -
          + log in + }} + /> )}
          ) diff --git a/tests/component/sections/collection/collection-items-panel/CollectionItemsPanel.spec.tsx b/tests/component/sections/collection/collection-items-panel/CollectionItemsPanel.spec.tsx index e1f9788a1..4ea332bfa 100644 --- a/tests/component/sections/collection/collection-items-panel/CollectionItemsPanel.spec.tsx +++ b/tests/component/sections/collection/collection-items-panel/CollectionItemsPanel.spec.tsx @@ -6,6 +6,7 @@ import { } from '@/collection/domain/models/CollectionItemSubset' import { CollectionRepository } from '@/collection/domain/repositories/CollectionRepository' import { CollectionItemsMother } from '@tests/component/collection/domain/models/CollectionItemsMother' +import { CollectionItemType } from '@/collection/domain/models/CollectionItemType' const collectionRepository: CollectionRepository = {} as CollectionRepository @@ -42,27 +43,159 @@ describe('CollectionItemsPanel', () => { cy.findByTestId('collection-items-list-infinite-scroll-skeleton').should('exist') }) - it('renders no items message when there are no collection, dataset or files', () => { - const emptyItems: CollectionItem[] = [] - const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } - collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + describe('NoItemsMessage', () => { + it('renders correct no items message when there are no collection, dataset or files', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) - cy.customMount( - - ) + cy.customMount( + + ) - cy.findByText(/This collection currently has no collections, datasets or files./).should( - 'exist' - ) + cy.findByText(/This collection currently has no collections, datasets or files./).should( + 'exist' + ) + }) + + it('renders correct no items message when there are no collections', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + + cy.customMount( + + ) + + cy.findByText(/This collection currently has no collections./).should('exist') + }) + + it('renders correct no items message when there are no datasets', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + + cy.customMount( + + ) + + cy.findByText(/This collection currently has no datasets./).should('exist') + }) + + it('renders correct no items message when there are no files', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + + cy.customMount( + + ) + + cy.findByText(/This collection currently has no files./).should('exist') + }) + + it('renders correct no items message when there are no collections and datasets', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + + cy.customMount( + + ) + + cy.findByText(/This collection currently has no collections or datasets./).should('exist') + }) + + it('renders correct no items message when there are no collections and files', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + + cy.customMount( + + ) + + cy.findByText(/This collection currently has no collections or files./).should('exist') + }) + + it('renders correct no items message when there are no datasets and files', () => { + const emptyItems: CollectionItem[] = [] + const emptyItemsWithCount: CollectionItemSubset = { items: emptyItems, totalItemCount: 0 } + collectionRepository.getItems = cy.stub().resolves(emptyItemsWithCount) + + cy.customMount( + + ) + + cy.findByText(/This collection currently has no datasets or files./).should('exist') + }) }) it('renders the no search results message when there are no items matching the search query', () => { diff --git a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts index 4fe65a57d..37e33b7e2 100644 --- a/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts +++ b/tests/e2e-integration/e2e/sections/collection/CollectionItemsPanel.spec.ts @@ -53,11 +53,10 @@ describe('Collection Items Panel', () => { }) }) - it.only('performs different search, filtering and respond to back and forward navigation', () => { + it('performs different search, filtering and respond to back and forward navigation', () => { cy.visit(`/spa/collections`) cy.wait('@getCollectionItems').then((interception) => { - console.log('interception', interception) const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = extractInfoFromInterceptedResponse(interception) @@ -80,7 +79,6 @@ describe('Collection Items Panel', () => { cy.findByRole('checkbox', { name: /Files/ }).click() cy.wait('@getCollectionItems').then((interception) => { - console.log('interception', interception) const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = extractInfoFromInterceptedResponse(interception) @@ -113,7 +111,6 @@ describe('Collection Items Panel', () => { cy.findByPlaceholderText('Search this collection...').type('Darwin{enter}') cy.wait('@getCollectionItems').then((interception) => { - console.log('interception', interception) const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = extractInfoFromInterceptedResponse(interception) @@ -148,7 +145,6 @@ describe('Collection Items Panel', () => { cy.findByRole('button', { name: /Search submit/ }).click() cy.wait('@getCollectionItems').then((interception) => { - console.log('interception', interception) const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = extractInfoFromInterceptedResponse(interception) @@ -182,7 +178,6 @@ describe('Collection Items Panel', () => { cy.findByRole('checkbox', { name: /Collections/ }).click() cy.wait('@getCollectionItems').then((interception) => { - console.log('interception', interception) const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = extractInfoFromInterceptedResponse(interception) @@ -214,7 +209,6 @@ describe('Collection Items Panel', () => { cy.findByRole('checkbox', { name: /Datasets/ }).click() cy.wait('@getCollectionItems').then((interception) => { - console.log('interception', interception) const { totalItemsInResponse, collectionsInResponse, datasetsInResponse, filesInResponse } = extractInfoFromInterceptedResponse(interception) From 7dcead424d678e5b768085d988d29caafe480b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Fri, 27 Sep 2024 10:38:18 -0300 Subject: [PATCH 38/42] feat: add few more stories and remove comments --- src/index.tsx | 18 ++--- .../CollectionItemsPanel.tsx | 2 - .../CollectionItemsPanel.stories.tsx | 68 +++++++++++++++++-- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 9eabd9a17..ba839fec8 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -7,13 +7,13 @@ import { ThemeProvider } from '@iqss/dataverse-design-system' const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( - // - - - - - - - - // + + + + + + + + + ) diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index df031c29a..bc96d59cf 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -23,8 +23,6 @@ interface CollectionItemsPanelProps { addDataSlot: JSX.Element | null } -// TODO:ME Add tests for off canvas component - /** * HOW IT WORKS: * This component loads items on 5 different scenarios: diff --git a/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx b/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx index b6da69258..4d0e23056 100644 --- a/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx +++ b/src/stories/collection/collection-items-panel/CollectionItemsPanel.stories.tsx @@ -87,23 +87,38 @@ export const NoSearchMatches: Story = { ) } -export const NoCollectionDatasetsOrFilesAnonymousUser: Story = { +export const NoCollectionDatasetsOrFiles: Story = { render: () => ( ) } - export const NoCollectionDatasetsOrFilesAuthenticatedUser: Story = { decorators: [WithLoggedInUser], render: () => ( @@ -112,6 +127,51 @@ export const NoCollectionDatasetsOrFilesAuthenticatedUser: Story = { ) } +export const NoCollections: Story = { + render: () => ( + + ) +} + +export const NoDatasets: Story = { + render: () => ( + + ) +} + +export const NoFiles: Story = { + render: () => ( + + ) +} + export const WithErrorLoadingItems: Story = { render: () => ( Date: Tue, 1 Oct 2024 08:36:11 -0300 Subject: [PATCH 39/42] reset word correction --- .../CollectionItemsPanel.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx index bc96d59cf..cb21400c3 100644 --- a/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx +++ b/src/sections/collection/collection-items-panel/CollectionItemsPanel.tsx @@ -91,8 +91,8 @@ export const CollectionItemsPanel = ({ const handleSearchSubmit = async (searchValue: string) => { itemsListContainerRef.current?.scrollTo({ top: 0 }) - const resetedPaginationInfo = new CollectionItemsPaginationInfo() - setPaginationInfo(resetedPaginationInfo) + const resetPaginationInfo = new CollectionItemsPaginationInfo() + setPaginationInfo(resetPaginationInfo) if (searchValue === '') { // Update the URL without the search value, keep other querys @@ -119,10 +119,10 @@ export const CollectionItemsPanel = ({ [CollectionItemType.COLLECTION, CollectionItemType.DATASET, CollectionItemType.FILE] ) - const totalItemsCount = await loadMore(resetedPaginationInfo, newCollectionSearchCriteria, true) + const totalItemsCount = await loadMore(resetPaginationInfo, newCollectionSearchCriteria, true) if (totalItemsCount !== undefined) { - const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalItemsCount) + const paginationInfoUpdated = resetPaginationInfo.withTotal(totalItemsCount) setPaginationInfo(paginationInfoUpdated) } } @@ -140,8 +140,8 @@ export const CollectionItemsPanel = ({ // KEEP SEARCH VALUE IF EXISTS itemsListContainerRef.current?.scrollTo({ top: 0 }) - const resetedPaginationInfo = new CollectionItemsPaginationInfo() - setPaginationInfo(resetedPaginationInfo) + const resetPaginationInfo = new CollectionItemsPaginationInfo() + setPaginationInfo(resetPaginationInfo) // Update the URL with the new item types, keep other querys and include the search value if exists setSearchParams((currentSearchParams) => ({ @@ -157,10 +157,10 @@ export const CollectionItemsPanel = ({ newItemsTypes ) - const totalItemsCount = await loadMore(resetedPaginationInfo, newCollectionSearchCriteria, true) + const totalItemsCount = await loadMore(resetPaginationInfo, newCollectionSearchCriteria, true) if (totalItemsCount !== undefined) { - const paginationInfoUpdated = resetedPaginationInfo.withTotal(totalItemsCount) + const paginationInfoUpdated = resetPaginationInfo.withTotal(totalItemsCount) setPaginationInfo(paginationInfoUpdated) } } From 8ad3f1a492a52a0ae964380b2b21ca1d84dca3df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 1 Oct 2024 08:45:48 -0300 Subject: [PATCH 40/42] added TODOs for using time tag for displaying dates --- .../file-info/file-info-cell/file-info-data/FileDate.tsx | 2 ++ src/sections/file/file-embargo/FileEmbargoDate.tsx | 2 ++ src/sections/file/file-metadata/FileMetadata.tsx | 3 +++ 3 files changed, 7 insertions(+) diff --git a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx index 682ca39cc..b160cfe86 100644 --- a/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx +++ b/src/sections/dataset/dataset-files/files-table/file-info/file-info-cell/file-info-data/FileDate.tsx @@ -2,6 +2,8 @@ import { FileDate as FileDateModel } from '../../../../../../../files/domain/mod import { useTranslation } from 'react-i18next' import { DateHelper } from '../../../../../../../shared/helpers/DateHelper' +// TODO: use time tag with dateTime attr https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time + export function FileDate({ date }: { date: FileDateModel }) { const { t } = useTranslation('files') return ( diff --git a/src/sections/file/file-embargo/FileEmbargoDate.tsx b/src/sections/file/file-embargo/FileEmbargoDate.tsx index 0735b7de3..6a238851c 100644 --- a/src/sections/file/file-embargo/FileEmbargoDate.tsx +++ b/src/sections/file/file-embargo/FileEmbargoDate.tsx @@ -20,6 +20,8 @@ export function FileEmbargoDate({ return <> } + // TODO: use time tag with dateTime attr https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time + return (
          diff --git a/src/sections/file/file-metadata/FileMetadata.tsx b/src/sections/file/file-metadata/FileMetadata.tsx index 2655d2d12..88f079926 100644 --- a/src/sections/file/file-metadata/FileMetadata.tsx +++ b/src/sections/file/file-metadata/FileMetadata.tsx @@ -97,6 +97,7 @@ export function FileMetadata({ {t('metadata.fields.depositDate')} + {/* TODO: use time tag with dateTime attr https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time */} {DateHelper.toDisplayFormatYYYYMMDD(metadata.depositDate)} {metadata.publicationDate && ( @@ -104,6 +105,7 @@ export function FileMetadata({ {t('metadata.fields.metadataReleaseDate')} + {/* TODO: use time tag with dateTime attr https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time */} {DateHelper.toDisplayFormatYYYYMMDD(metadata.publicationDate)} )} @@ -112,6 +114,7 @@ export function FileMetadata({ {t('metadata.fields.publicationDate')} + {/* TODO: use time tag with dateTime attr https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time */} {metadata.embargo ? ( Date: Tue, 1 Oct 2024 09:16:07 -0300 Subject: [PATCH 41/42] change DatasetPreview to DatasetItemTypePreview --- DEVELOPER_GUIDE.md | 6 ++--- .../domain/models/CollectionItemSubset.ts | 7 ++++-- .../mappers/JSCollectionItemsMapper.ts | 24 +++++++++---------- ...etPreview.ts => DatasetItemTypePreview.ts} | 2 +- .../domain/models/DatasetsWithCount.ts | 4 ++-- .../mappers/JSDatasetPreviewMapper.ts | 4 ++-- .../DatasetJSDataverseRepository.ts | 2 +- .../items-list/dataset-card/DatasetCard.tsx | 4 ++-- .../DatasetCard.stories.tsx | 8 +++---- src/stories/dataset/DatasetMockRepository.ts | 6 +++-- .../domain/models/CollectionItemsMother.ts | 4 ++-- ...her.ts => DatasetItemTypePreviewMother.ts} | 20 ++++++++-------- .../dataset-card/DatasetCard.spec.tsx | 4 ++-- .../dataset-card/DatasetCardHeader.spec.tsx | 6 ++--- .../dataset-card/DatasetCardInfo.spec.tsx | 6 ++--- .../DatasetCardThumbnail.spec.tsx | 6 ++--- .../DatasetJSDataverseRepository.spec.ts | 2 +- 17 files changed, 60 insertions(+), 55 deletions(-) rename src/dataset/domain/models/{DatasetPreview.ts => DatasetItemTypePreview.ts} (92%) rename tests/component/dataset/domain/models/{DatasetPreviewMother.ts => DatasetItemTypePreviewMother.ts} (74%) diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md index eb0d503da..d37e500b9 100644 --- a/DEVELOPER_GUIDE.md +++ b/DEVELOPER_GUIDE.md @@ -328,7 +328,7 @@ dataset/ │ ├── Dataset.ts │ ├── DatasetFormFields.ts │ ├── DatasetPaginationInfo.ts - │ ├── DatasetPreview.ts + │ ├── DatasetItemTypePreview.ts │ ├── DatasetValidationResponse.ts │ └── TotalDatasetsCount.ts └── repositories/ @@ -655,11 +655,11 @@ This means: import { Home } from '../../../../src/sections/home/Home' import { DatasetRepository } from '../../../../src/dataset/domain/repositories/DatasetRepository' -import { DatasetPreviewMother } from '../../dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '../../dataset/domain/models/DatasetItemTypePreviewMother' const datasetRepository: DatasetRepository = {} as DatasetRepository const totalDatasetsCount = 10 -const datasets = DatasetPreviewMother.createMany(totalDatasetsCount) +const datasets = DatasetItemTypePreviewMother.createMany(totalDatasetsCount) describe('Home page', () => { beforeEach(() => { datasetRepository.getAll = cy.stub().resolves(datasets) diff --git a/src/collection/domain/models/CollectionItemSubset.ts b/src/collection/domain/models/CollectionItemSubset.ts index 4e8b0541f..3fbc3b0b7 100644 --- a/src/collection/domain/models/CollectionItemSubset.ts +++ b/src/collection/domain/models/CollectionItemSubset.ts @@ -1,5 +1,5 @@ import { CollectionItemTypePreview } from './CollectionItemTypePreview' -import { DatasetPreview } from '../../../dataset/domain/models/DatasetPreview' +import { DatasetItemTypePreview } from '../../../dataset/domain/models/DatasetItemTypePreview' import { FileItemTypePreview } from '../../../files/domain/models/FileItemTypePreview' export interface CollectionItemSubset { @@ -7,4 +7,7 @@ export interface CollectionItemSubset { totalItemCount: number } -export type CollectionItem = CollectionItemTypePreview | DatasetPreview | FileItemTypePreview +export type CollectionItem = + | CollectionItemTypePreview + | DatasetItemTypePreview + | FileItemTypePreview diff --git a/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts b/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts index 82e4d8ba8..10076bee8 100644 --- a/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts +++ b/src/collection/infrastructure/mappers/JSCollectionItemsMapper.ts @@ -22,7 +22,7 @@ export class JSCollectionItemsMapper { } if (item.type === JSCollectionItemType.DATASET) { - items.push(JSDatasetPreviewMapper.toDatasetPreview(item)) + items.push(JSDatasetPreviewMapper.toDatasetItemTypePreview(item)) } if (item.type === JSCollectionItemType.FILE) { @@ -34,21 +34,21 @@ export class JSCollectionItemsMapper { } static toCollectionItemTypePreview( - jsDatasetPreview: JSCollectionPreview + jsCollectionPreview: JSCollectionPreview ): CollectionItemTypePreview { return { - type: jsDatasetPreview.type, + type: jsCollectionPreview.type, isReleased: JSCollectionItemsMapper.toIsRelasedCollection( - jsDatasetPreview.publicationStatuses + jsCollectionPreview.publicationStatuses ), - name: jsDatasetPreview.name, - alias: jsDatasetPreview.alias, - description: jsDatasetPreview.description, - affiliation: jsDatasetPreview.affiliation, - releaseOrCreateDate: jsDatasetPreview.releaseOrCreateDate, - thumbnail: jsDatasetPreview.imageUrl, - parentCollectionName: jsDatasetPreview.parentName, - parentCollectionAlias: jsDatasetPreview.parentAlias + name: jsCollectionPreview.name, + alias: jsCollectionPreview.alias, + description: jsCollectionPreview.description, + affiliation: jsCollectionPreview.affiliation, + releaseOrCreateDate: jsCollectionPreview.releaseOrCreateDate, + thumbnail: jsCollectionPreview.imageUrl, + parentCollectionName: jsCollectionPreview.parentName, + parentCollectionAlias: jsCollectionPreview.parentAlias } } diff --git a/src/dataset/domain/models/DatasetPreview.ts b/src/dataset/domain/models/DatasetItemTypePreview.ts similarity index 92% rename from src/dataset/domain/models/DatasetPreview.ts rename to src/dataset/domain/models/DatasetItemTypePreview.ts index 22b0fa540..dd37a1e00 100644 --- a/src/dataset/domain/models/DatasetPreview.ts +++ b/src/dataset/domain/models/DatasetItemTypePreview.ts @@ -2,7 +2,7 @@ import { CollectionItemType } from '../../../collection/domain/models/Collection import { PublicationStatus } from '../../../shared/core/domain/models/PublicationStatus' import { DatasetVersion } from './Dataset' -export interface DatasetPreview { +export interface DatasetItemTypePreview { type: CollectionItemType.DATASET persistentId: string version: DatasetVersion diff --git a/src/dataset/domain/models/DatasetsWithCount.ts b/src/dataset/domain/models/DatasetsWithCount.ts index 9194f4b62..7b217a5b7 100644 --- a/src/dataset/domain/models/DatasetsWithCount.ts +++ b/src/dataset/domain/models/DatasetsWithCount.ts @@ -1,7 +1,7 @@ -import { DatasetPreview } from '../../domain/models/DatasetPreview' +import { DatasetItemTypePreview } from './DatasetItemTypePreview' import { TotalDatasetsCount } from './TotalDatasetsCount' export interface DatasetsWithCount { - datasetPreviews: DatasetPreview[] + datasetPreviews: DatasetItemTypePreview[] totalCount: TotalDatasetsCount } diff --git a/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts b/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts index 5dcd67c51..c832be823 100644 --- a/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts +++ b/src/dataset/infrastructure/mappers/JSDatasetPreviewMapper.ts @@ -1,10 +1,10 @@ import { DatasetPreview as JSDatasetPreview } from '@iqss/dataverse-client-javascript/dist/datasets/domain/models/DatasetPreview' -import { DatasetPreview } from '../../domain/models/DatasetPreview' +import { DatasetItemTypePreview } from '../../domain/models/DatasetItemTypePreview' import { DatasetVersionInfo as JSDatasetVersionInfo } from '@iqss/dataverse-client-javascript/dist/datasets/domain/models/Dataset' import { JSDatasetVersionMapper } from './JSDatasetVersionMapper' export class JSDatasetPreviewMapper { - static toDatasetPreview(jsDatasetPreview: JSDatasetPreview): DatasetPreview { + static toDatasetItemTypePreview(jsDatasetPreview: JSDatasetPreview): DatasetItemTypePreview { return { type: jsDatasetPreview.type, persistentId: jsDatasetPreview.persistentId, diff --git a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts index ef68c1c5d..cb352631a 100644 --- a/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts +++ b/src/dataset/infrastructure/repositories/DatasetJSDataverseRepository.ts @@ -59,7 +59,7 @@ export class DatasetJSDataverseRepository implements DatasetRepository { .then((subset: DatasetPreviewSubset) => { const datasetPreviewsMapped = subset.datasetPreviews.map( (datasetPreview: JSDatasetPreview) => - JSDatasetPreviewMapper.toDatasetPreview(datasetPreview) + JSDatasetPreviewMapper.toDatasetItemTypePreview(datasetPreview) ) return { datasetPreviews: datasetPreviewsMapped, diff --git a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx index 3bcb71817..12bf17bf6 100644 --- a/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx +++ b/src/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.tsx @@ -1,11 +1,11 @@ -import { DatasetPreview } from '@/dataset/domain/models/DatasetPreview' +import { DatasetItemTypePreview } from '@/dataset/domain/models/DatasetItemTypePreview' import { DatasetCardHeader } from './DatasetCardHeader' import { DatasetCardThumbnail } from './DatasetCardThumbnail' import { DatasetCardInfo } from './DatasetCardInfo' import styles from './DatasetCard.module.scss' interface DatasetCardProps { - datasetPreview: DatasetPreview + datasetPreview: DatasetItemTypePreview } export function DatasetCard({ datasetPreview }: DatasetCardProps) { diff --git a/src/stories/collection/collection-items-panel/DatasetCard.stories.tsx b/src/stories/collection/collection-items-panel/DatasetCard.stories.tsx index 4ecac85b1..4edb1f002 100644 --- a/src/stories/collection/collection-items-panel/DatasetCard.stories.tsx +++ b/src/stories/collection/collection-items-panel/DatasetCard.stories.tsx @@ -1,7 +1,7 @@ import { Meta, StoryObj } from '@storybook/react' import { DatasetCard } from '@/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard' import { WithI18next } from '../../WithI18next' -import { DatasetPreviewMother } from '../../../../tests/component/dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '../../../../tests/component/dataset/domain/models/DatasetItemTypePreviewMother' const meta: Meta = { title: 'Sections/Collection Page/DatasetCard', @@ -13,13 +13,13 @@ export default meta type Story = StoryObj export const Default: Story = { - render: () => + render: () => } export const Deaccessioned: Story = { - render: () => + render: () => } export const WithThumbnail: Story = { - render: () => + render: () => } diff --git a/src/stories/dataset/DatasetMockRepository.ts b/src/stories/dataset/DatasetMockRepository.ts index 5a68c8c13..446216e55 100644 --- a/src/stories/dataset/DatasetMockRepository.ts +++ b/src/stories/dataset/DatasetMockRepository.ts @@ -2,7 +2,7 @@ import { Dataset, DatasetLock } from '../../dataset/domain/models/Dataset' import { DatasetRepository } from '../../dataset/domain/repositories/DatasetRepository' import { DatasetMother } from '../../../tests/component/dataset/domain/models/DatasetMother' import { DatasetPaginationInfo } from '../../dataset/domain/models/DatasetPaginationInfo' -import { DatasetPreviewMother } from '../../../tests/component/dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '../../../tests/component/dataset/domain/models/DatasetItemTypePreviewMother' import { DatasetDTO } from '../../dataset/domain/useCases/DTOs/DatasetDTO' import { DatasetsWithCount } from '../../dataset/domain/models/DatasetsWithCount' import { FakerHelper } from '../../../tests/component/shared/FakerHelper' @@ -18,7 +18,9 @@ export class DatasetMockRepository implements DatasetRepository { return new Promise((resolve) => { setTimeout(() => { resolve({ - datasetPreviews: DatasetPreviewMother.createManyRealistic(paginationInfo.pageSize), + datasetPreviews: DatasetItemTypePreviewMother.createManyRealistic( + paginationInfo.pageSize + ), totalCount: 200 }) }, FakerHelper.loadingTimout()) diff --git a/tests/component/collection/domain/models/CollectionItemsMother.ts b/tests/component/collection/domain/models/CollectionItemsMother.ts index 270723cf6..6e9dc819b 100644 --- a/tests/component/collection/domain/models/CollectionItemsMother.ts +++ b/tests/component/collection/domain/models/CollectionItemsMother.ts @@ -1,7 +1,7 @@ import { CollectionItem } from '@/collection/domain/models/CollectionItemSubset' import { FileItemTypePreviewMother } from '../../../files/domain/models/FileItemTypePreviewMother' import { CollectionItemTypePreviewMother } from './CollectionItemTypePreviewMother' -import { DatasetPreviewMother } from '../../../dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '../../../dataset/domain/models/DatasetItemTypePreviewMother' interface CreateItemsProps { numberOfCollections?: number @@ -16,7 +16,7 @@ export class CollectionItemsMother { numberOfFiles = 1 }: CreateItemsProps): CollectionItem[] { const collections = CollectionItemTypePreviewMother.createMany(numberOfCollections) - const datasets = DatasetPreviewMother.createMany(numberOfDatasets) + const datasets = DatasetItemTypePreviewMother.createMany(numberOfDatasets) const files = FileItemTypePreviewMother.createMany(numberOfFiles) return [...collections, ...datasets, ...files] diff --git a/tests/component/dataset/domain/models/DatasetPreviewMother.ts b/tests/component/dataset/domain/models/DatasetItemTypePreviewMother.ts similarity index 74% rename from tests/component/dataset/domain/models/DatasetPreviewMother.ts rename to tests/component/dataset/domain/models/DatasetItemTypePreviewMother.ts index 72000ec3c..a71f34fa6 100644 --- a/tests/component/dataset/domain/models/DatasetPreviewMother.ts +++ b/tests/component/dataset/domain/models/DatasetItemTypePreviewMother.ts @@ -1,20 +1,20 @@ import { faker } from '@faker-js/faker' -import { DatasetPreview } from '../../../../../src/dataset/domain/models/DatasetPreview' +import { DatasetItemTypePreview } from '../../../../../src/dataset/domain/models/DatasetItemTypePreview' import { DatasetVersionMother } from './DatasetMother' import { FakerHelper } from '../../../shared/FakerHelper' import { CollectionItemType } from '../../../../../src/collection/domain/models/CollectionItemType' import { PublicationStatus } from '../../../../../src/shared/core/domain/models/PublicationStatus' -export class DatasetPreviewMother { - static createMany(count: number): DatasetPreview[] { +export class DatasetItemTypePreviewMother { + static createMany(count: number): DatasetItemTypePreview[] { return Array.from({ length: count }, () => this.create()) } - static createManyRealistic(count: number): DatasetPreview[] { + static createManyRealistic(count: number): DatasetItemTypePreview[] { return Array.from({ length: count }, () => this.createRealistic()) } - static create(props?: Partial): DatasetPreview { + static create(props?: Partial): DatasetItemTypePreview { const datasetPreview = { persistentId: faker.datatype.uuid(), version: DatasetVersionMother.create(), @@ -39,26 +39,26 @@ export class DatasetPreviewMother { } } - static createRealistic(): DatasetPreview { + static createRealistic(): DatasetItemTypePreview { return faker.datatype.boolean() ? this.createDraft() : this.createDeaccessioned() } - static createDraft(): DatasetPreview { + static createDraft(): DatasetItemTypePreview { return this.create({ version: DatasetVersionMother.createDraft(), publicationStatuses: [PublicationStatus.Draft] }) } - static createWithThumbnail(): DatasetPreview { + static createWithThumbnail(): DatasetItemTypePreview { return this.create({ thumbnail: FakerHelper.getImageUrl() }) } - static createWithNoThumbnail(): DatasetPreview { + static createWithNoThumbnail(): DatasetItemTypePreview { return this.create({ thumbnail: undefined }) } - static createDeaccessioned(): DatasetPreview { + static createDeaccessioned(): DatasetItemTypePreview { return this.create({ version: DatasetVersionMother.createDeaccessioned() }) diff --git a/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCard.spec.tsx b/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCard.spec.tsx index a58ec1b93..61819c94f 100644 --- a/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCard.spec.tsx +++ b/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCard.spec.tsx @@ -1,10 +1,10 @@ import { DatasetCard } from '@/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard' -import { DatasetPreviewMother } from '@tests/component/dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '@tests/component/dataset/domain/models/DatasetItemTypePreviewMother' import { DateHelper } from '@/shared/helpers/DateHelper' describe('DatasetCard', () => { it('should render the card', () => { - const dataset = DatasetPreviewMother.createWithThumbnail() + const dataset = DatasetItemTypePreviewMother.createWithThumbnail() cy.customMount() diff --git a/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardHeader.spec.tsx b/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardHeader.spec.tsx index 8dd26530c..4ae8ec92d 100644 --- a/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardHeader.spec.tsx +++ b/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardHeader.spec.tsx @@ -1,9 +1,9 @@ import { DatasetCardHeader } from '@/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardHeader' -import { DatasetPreviewMother } from '@tests/component/dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '@tests/component/dataset/domain/models/DatasetItemTypePreviewMother' describe('DatasetCardHeader', () => { it('should render the header', () => { - const dataset = DatasetPreviewMother.create() + const dataset = DatasetItemTypePreviewMother.create() cy.customMount( ) @@ -17,7 +17,7 @@ describe('DatasetCardHeader', () => { cy.findByLabelText('icon-dataset').should('exist') }) it('should render the correct search param for draft version', () => { - const dataset = DatasetPreviewMother.createDraft() + const dataset = DatasetItemTypePreviewMother.createDraft() cy.customMount( ) diff --git a/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardInfo.spec.tsx b/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardInfo.spec.tsx index 7b0e85f1d..9ef0f054f 100644 --- a/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardInfo.spec.tsx +++ b/tests/component/sections/collection/collection-items-panel/dataset-card/DatasetCardInfo.spec.tsx @@ -1,11 +1,11 @@ import { DatasetCardInfo } from '@/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCardInfo' -import { DatasetPreviewMother } from '../../../../dataset/domain/models/DatasetPreviewMother' +import { DatasetItemTypePreviewMother } from '../../../../dataset/domain/models/DatasetItemTypePreviewMother' import { DateHelper } from '@/shared/helpers/DateHelper' import styles from '@/sections/collection/collection-items-panel/items-list/dataset-card/DatasetCard.module.scss' describe('DatasetCardInfo', () => { it('should render the dataset info', () => { - const dataset = DatasetPreviewMother.createDraft() + const dataset = DatasetItemTypePreviewMother.createDraft() cy.customMount( { }) it('should render the citation with the deaccessioned background if the dataset is deaccessioned', () => { - const dataset = DatasetPreviewMother.createDeaccessioned() + const dataset = DatasetItemTypePreviewMother.createDeaccessioned() cy.customMount( { it('should render the thumbnail', () => { - const dataset = DatasetPreviewMother.createWithThumbnail() + const dataset = DatasetItemTypePreviewMother.createWithThumbnail() cy.customMount( { }) it('should render the placeholder if the dataset has no thumbnail', () => { - const dataset = DatasetPreviewMother.createWithNoThumbnail() + const dataset = DatasetItemTypePreviewMother.createWithNoThumbnail() cy.customMount( { }) }) - it('gets the DatasetPreview', () => { + it('gets the DatasetItemTypePreview', () => { const previewCollectionId = 'DatasetJSDataverseRepositoryPreview' + Date.now().toString() cy.wrap(CollectionHelper.createAndPublish(previewCollectionId)).then(() => { From f035fc88bbaab30c696e409ff1cb1361d2fba6de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Germ=C3=A1n=20Saracca?= Date: Tue, 1 Oct 2024 09:22:01 -0300 Subject: [PATCH 42/42] remove comment and unf optional --- src/collection/domain/models/CollectionItemTypePreview.ts | 4 ++-- src/files/domain/models/FileItemTypePreview.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/collection/domain/models/CollectionItemTypePreview.ts b/src/collection/domain/models/CollectionItemTypePreview.ts index c2a24a3e4..383224cec 100644 --- a/src/collection/domain/models/CollectionItemTypePreview.ts +++ b/src/collection/domain/models/CollectionItemTypePreview.ts @@ -5,8 +5,8 @@ export interface CollectionItemTypePreview { isReleased: boolean name: string alias: string - description?: string // it could be undefined before ? - affiliation?: string // it could be undefined before ? + description?: string + affiliation?: string releaseOrCreateDate: Date thumbnail?: string parentCollectionName: string diff --git a/src/files/domain/models/FileItemTypePreview.ts b/src/files/domain/models/FileItemTypePreview.ts index 0e7a5659c..3c129bfdf 100644 --- a/src/files/domain/models/FileItemTypePreview.ts +++ b/src/files/domain/models/FileItemTypePreview.ts @@ -14,7 +14,7 @@ export interface FileItemTypePreview { sizeInBytes: number md5?: string checksum?: FilePreviewChecksum - unf: string + unf?: string datasetName: string datasetId: number datasetPersistentId: string