Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

137 - Files table UI [2/2] - Filters #147

Merged
merged 21 commits into from
Aug 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e149420
feat(FilesTable)
MellyGray Jul 11, 2023
d706827
feat(FilesTableSort): refactor to its own component
MellyGray Jul 11, 2023
4d8bd1c
feat(FilesTableFilters): add UI component for the Filter By Type
MellyGray Jul 11, 2023
41da10c
feat(FilesTableFilters): create FilesCountInfo interface
MellyGray Jul 12, 2023
839449b
feat(FilesTableFilters): add font weight bold to the selected filter …
MellyGray Jul 12, 2023
b4ce154
feat(DropdownSeparator): add DropdownSeparator to the design system
MellyGray Jul 12, 2023
2032072
refactor FilesTableFilters
MellyGray Jul 13, 2023
6d929df
feat(FilesTableFilters): add FilterByAccess component
MellyGray Jul 13, 2023
b1778a4
feat(FilesTableFilters): add FilterByTag component
MellyGray Jul 13, 2023
0e5432d
fix: keep the state of the rest of the filters when changing one
MellyGray Jul 13, 2023
837f9f9
feat(FilesTableFilters): separate filters from the table reload
MellyGray Jul 13, 2023
e4d7bfd
feat(FilesTableFilters): add internationalization
MellyGray Jul 13, 2023
6286892
feat(FilesTableFilters): show filters only if they can be applied
MellyGray Jul 13, 2023
94261b7
feat(filesCountInfo): add TODOs
MellyGray Jul 13, 2023
a8d5a4a
Merge branch 'feature/138-file-table-sorting-ui' of https://github.co…
MellyGray Jul 14, 2023
3d39be0
fix: create file mock data
MellyGray Jul 17, 2023
4872d61
Merge branch 'feature/138-file-table-sorting-ui' of https://github.co…
MellyGray Jul 31, 2023
ae96d9d
fix: remove lerna from dev-env npm install
MellyGray Aug 1, 2023
6e7f7a2
fix: refactor FilesTable component to get files as prop
MellyGray Aug 1, 2023
974f6e8
fix: move filesCount check inside FileCriteria component
MellyGray Aug 1, 2023
0510baa
feat(getFilesCountInfo): implement use case for getFilesCountInfo
MellyGray Aug 1, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dev.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ RUN npm run build

WORKDIR /usr/src/app
COPY package.json ./
COPY package-lock.json ./
COPY .npmrc ./
RUN npm uninstall --save-dev lerna
RUN npm install

FROM node:19.6.1-alpine
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Dropdown as DropdownBS } from 'react-bootstrap'
import { ReactNode } from 'react'
import React, { ReactNode } from 'react'

interface DropdownItemProps {
interface DropdownItemProps extends React.HTMLAttributes<HTMLElement> {
href?: string
eventKey?: string
children: ReactNode
}

export function DropdownButtonItem({ href, eventKey, children }: DropdownItemProps) {
export function DropdownButtonItem({ href, eventKey, children, ...props }: DropdownItemProps) {
return (
<DropdownBS.Item href={href} eventKey={eventKey}>
<DropdownBS.Item href={href} eventKey={eventKey} {...props}>
{children}
</DropdownBS.Item>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Dropdown } from 'react-bootstrap'
MellyGray marked this conversation as resolved.
Show resolved Hide resolved

export function DropdownSeparator() {
return <Dropdown.Divider />
}
1 change: 1 addition & 0 deletions packages/design-system/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { Badge } from './components/badge/Badge'
export { Button } from './components/button/Button'
export { DropdownButton } from './components/dropdown-button/DropdownButton'
export { DropdownButtonItem } from './components/dropdown-button/dropdown-button-item/DropdownButtonItem'
export { DropdownSeparator } from './components/dropdown-button/dropdown-separator/DropdownSeparator'
export { Col } from './components/grid/Col'
export { Container } from './components/grid/Container'
export { Row } from './components/grid/Row'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { DropdownButtonItem } from '../../components/dropdown-button/dropdown-bu
import { DropdownButton } from '../../components/dropdown-button/DropdownButton'
import { IconName } from '../../components/icon/IconName'
import { CanvasFixedHeight } from '../CanvasFixedHeight'
import { DropdownSeparator } from '../../components/dropdown-button/dropdown-separator/DropdownSeparator'

/**
* ## Description
Expand Down Expand Up @@ -117,6 +118,19 @@ export const WithIcon: Story = {
)
}

export const WithSeparatorBetweenOptions: Story = {
render: () => (
<CanvasFixedHeight height={150}>
<DropdownButton withSpacing title="Dropdown Button" id="dropdown-1" variant="primary">
<DropdownButtonItem href="/item-1">Item 1</DropdownButtonItem>
<DropdownButtonItem href="/item-2">Item 2</DropdownButtonItem>
<DropdownSeparator />
<DropdownButtonItem href="/item-3">Item 3</DropdownButtonItem>
</DropdownButton>
</CanvasFixedHeight>
)
}

/**
* This is an example use case for a navigation dropdown button.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { IconName } from '../../../src/lib/components/icon/IconName'
import styles from '../../../src/lib/components/dropdown-button/DropdownButton.module.scss'
import { ArrowClockwise } from 'react-bootstrap-icons'
import DropdownItem from 'react-bootstrap/DropdownItem'
import { DropdownSeparator } from '../../../src/lib'

const titleText = 'My Dropdown Button'

Expand Down Expand Up @@ -96,4 +97,18 @@ describe('DropdownButton', () => {

cy.wrap(onSelect).should('be.calledWith', '1')
})

it('renders with separator', () => {
cy.mount(
<DropdownButton id="dropdown-button" title={titleText}>
<DropdownItem eventKey="1">Item 1</DropdownItem>
<DropdownItem eventKey="2">Item 2</DropdownItem>
<DropdownSeparator />
<DropdownItem eventKey="3">Item 3</DropdownItem>
</DropdownButton>
)
cy.findByText(titleText).click()

cy.findByRole('separator').should('exist')
})
})
19 changes: 19 additions & 0 deletions public/locales/en/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,25 @@
"size": "Size",
"type": "Type"
}
},
"filters": {
"title": "Filter by"
},
"filterByType": {
"title": "Filter Type"
},
"filterByTag": {
"title": "Filter Tag"
},
"filterByAccess": {
"title": "Access",
"options": {
"all": "All",
"public": "Public",
"restricted": "Restricted",
"embargoed_public": "Embargoed then Public",
"embargoed_restricted": "Embargoed then Restricted"
}
}
}
}
15 changes: 14 additions & 1 deletion src/files/domain/models/File.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,26 @@ export interface FileLabel {
value: string
}

export class FileType {
constructor(readonly value: string) {}

toDisplayFormat(): string {
const words = this.value.split(' ')
return words
.map((word) => {
return word[0].toUpperCase() + word.substring(1)
})
.join(' ')
}
}

export class File {
constructor(
readonly id: string,
readonly version: FileVersion,
readonly name: string,
readonly access: FileAccess,
readonly type: string,
readonly type: FileType,
readonly size: FileSize,
readonly date: FileDate,
readonly downloads: number,
Expand Down
46 changes: 44 additions & 2 deletions src/files/domain/models/FileCriteria.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
export interface FileCriteria {
sortBy?: FileSortByOption
import { FileType } from './File'

export class FileCriteria {
constructor(
public readonly sortBy: FileSortByOption = FileSortByOption.NAME_AZ,
public readonly filterByType?: FileType,
public readonly filterByAccess?: FileAccessOption,
public readonly filterByTag?: FileTag
) {}

withSortBy(sortBy: FileSortByOption): FileCriteria {
return new FileCriteria(sortBy, this.filterByType, this.filterByAccess, this.filterByTag)
}

withFilterByType(filterByType: string | undefined): FileCriteria {
const newFilterByType = filterByType === undefined ? undefined : new FileType(filterByType)

return new FileCriteria(this.sortBy, newFilterByType, this.filterByAccess, this.filterByTag)
}

withFilterByAccess(filterByAccess: FileAccessOption | undefined): FileCriteria {
return new FileCriteria(this.sortBy, this.filterByType, filterByAccess, this.filterByTag)
}

withFilterByTag(filterByTag: string | undefined): FileCriteria {
const newFilterByTag = filterByTag === undefined ? undefined : new FileTag(filterByTag)

return new FileCriteria(this.sortBy, this.filterByType, this.filterByAccess, newFilterByTag)
}
}

export enum FileSortByOption {
Expand All @@ -10,3 +37,18 @@ export enum FileSortByOption {
SIZE = 'size',
TYPE = 'type'
}

export enum FileAccessOption {
PUBLIC = 'public',
RESTRICTED = 'restricted',
EMBARGOED = 'embargoed_public',
EMBARGOED_RESTRICTED = 'embargoed_restricted'
}

export class FileTag {
constructor(readonly value: string) {}

toDisplayFormat(): string {
return this.value[0].toUpperCase() + this.value.substring(1)
}
}
24 changes: 24 additions & 0 deletions src/files/domain/models/FilesCountInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FileType } from './File'
import { FileAccessOption, FileTag } from './FileCriteria'

export interface FilesCountInfo {
total: number
perFileType: FileTypeCount[]
perAccess: FileAccessCount[]
perFileTag: FileTagCount[]
}

export interface FileTypeCount {
type: FileType
count: number
}

export interface FileAccessCount {
access: FileAccessOption
count: number
}

export interface FileTagCount {
tag: FileTag
count: number
}
5 changes: 5 additions & 0 deletions src/files/domain/repositories/FileRepository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { File } from '../models/File'
import { FileCriteria } from '../models/FileCriteria'
import { FilesCountInfo } from '../models/FilesCountInfo'

export interface FileRepository {
getAllByDatasetPersistentId: (
datasetPersistentId: string,
version?: string,
criteria?: FileCriteria
) => Promise<File[]>
getCountInfoByDatasetPersistentId: (
datasetPersistentId: string,
version?: string
) => Promise<FilesCountInfo>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FileRepository } from '../repositories/FileRepository'
import { FileVersionNotNumber } from '../models/File'
import { FilesCountInfo } from '../models/FilesCountInfo'

export async function getFilesCountInfoByDatasetPersistentId(
fileRepository: FileRepository,
persistentId: string,
version: string = FileVersionNotNumber.LATEST
): Promise<FilesCountInfo> {
return fileRepository
.getCountInfoByDatasetPersistentId(persistentId, version)
.catch((error: Error) => {
throw new Error(error.message)
})
}
14 changes: 14 additions & 0 deletions src/files/infrastructure/FileJSDataverseRepository.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { FileRepository } from '../domain/repositories/FileRepository'
import { File } from '../domain/models/File'
import { FilesMockData } from '../../stories/files/FileMockData'
import { FilesCountInfo } from '../domain/models/FilesCountInfo'
import { FilesCountInfoMother } from '../../../tests/component/files/domain/models/FilesCountInfoMother'

export class FileJSDataverseRepository implements FileRepository {
// eslint-disable-next-line unused-imports/no-unused-vars
Expand All @@ -12,4 +14,16 @@
}, 1000)
})
}
// eslint-disable-next-line unused-imports/no-unused-vars
getCountInfoByDatasetPersistentId(
persistentId: string,

Check warning on line 19 in src/files/infrastructure/FileJSDataverseRepository.ts

View workflow job for this annotation

GitHub Actions / lint

'persistentId' is defined but never used. Allowed unused args must match /^_/u
version?: string

Check warning on line 20 in src/files/infrastructure/FileJSDataverseRepository.ts

View workflow job for this annotation

GitHub Actions / lint

'version' is defined but never used. Allowed unused args must match /^_/u
): Promise<FilesCountInfo> {
// TODO - implement using js-dataverse
return new Promise((resolve) => {
setTimeout(() => {
resolve(FilesCountInfoMother.create())
}, 1000)
})
}
}
31 changes: 12 additions & 19 deletions src/sections/dataset/dataset-files/DatasetFiles.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { useFilesTable } from './files-table/useFilesTable'
import { FileRepository } from '../../../files/domain/repositories/FileRepository'
import { useFiles } from './useFiles'
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { FilesTable } from './files-table/FilesTable'
import { SpinnerSymbol } from './files-table/spinner-symbol/SpinnerSymbol'
import { FileCriteriaInputs } from './file-criteria-inputs/FileCriteriaInputs'
import { FileCriteriaControls } from './file-criteria-controls/FileCriteriaControls'
import { FileCriteria } from '../../../files/domain/models/FileCriteria'
import { useFiles } from './useFiles'

interface DatasetFilesProps {
filesRepository: FileRepository
Expand All @@ -18,30 +16,25 @@ export function DatasetFiles({
datasetPersistentId,
datasetVersion
}: DatasetFilesProps) {
const [criteria, setCriteria] = useState<FileCriteria>()
const { files, isLoading } = useFiles(
const [criteria, setCriteria] = useState<FileCriteria>(new FileCriteria())
const { files, isLoading, filesCountInfo } = useFiles(
filesRepository,
datasetPersistentId,
datasetVersion,
criteria
)
const { table, setFilesTableData } = useFilesTable()
const handleCriteriaChange = (newCriteria: FileCriteria) => {
setCriteria((criteria) => ({ ...criteria, ...newCriteria }))
}

useEffect(() => {
setFilesTableData(files)
}, [files])

if (isLoading) {
return <SpinnerSymbol />
setCriteria(newCriteria)
}

return (
<>
{files.length !== 0 && <FileCriteriaInputs onCriteriaChange={handleCriteriaChange} />}
<FilesTable table={table} />
<FileCriteriaControls
criteria={criteria}
onCriteriaChange={handleCriteriaChange}
filesCountInfo={filesCountInfo}
/>
<FilesTable files={files} isLoading={isLoading} />
</>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
@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";

.criteria-section {
margin-bottom: 1em;
}

.sort-container {
display: flex;
align-items: end;
justify-content: end;
}

.icon {
margin-right: 0.3em;
margin-bottom: 0.2em;
}

.text-filter-by {
margin-left: 7px;
color: $dv-subtext-color;
font-size: $dv-font-size-sm;
}

.selected-option {
font-weight: $dv-font-weight-bold;
}

.filters-container {
display: flex;
align-items: center;
}
Loading