Skip to content

Commit

Permalink
feat: Refactor FileList component to use children prop and add FileLi…
Browse files Browse the repository at this point in the history
…stItem subcomponent
  • Loading branch information
dlnr committed Dec 13, 2024
1 parent c3d4684 commit 49c7ba4
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 59 deletions.
1 change: 1 addition & 0 deletions packages/css/src/components/file-list/file-list.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@use "../../common/text-rendering" as *;

@mixin reset-ul {
list-style: none;
margin-block: 0;
padding-inline: 0;
}
Expand Down
14 changes: 7 additions & 7 deletions packages/react/src/FileList/FileList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { createRef } from 'react'
import { FileList } from './FileList'
import '@testing-library/jest-dom'

var files = [
new File(['sample1'], 'sample1.txt', { type: 'text/plain', lastModified: Date.now() }),
] as unknown as FileList // This is a workaround because jest-dom does not support DataTransfer
// var files = [
// new File(['sample1'], 'sample1.txt', { type: 'text/plain', lastModified: Date.now() }),
// ] as unknown as FileList // This is a workaround because jest-dom does not support DataTransfer

describe('FileList', () => {
it('renders', () => {
const { container } = render(<FileList files={files} />)
const { container } = render(<FileList />)

const component = container.querySelector(':only-child')

Expand All @@ -18,15 +18,15 @@ describe('FileList', () => {
})

it('renders a design system BEM class name', () => {
const { container } = render(<FileList files={files} />)
const { container } = render(<FileList />)

const component = container.querySelector(':only-child')

expect(component).toHaveClass('ams-file-list')
})

it('renders an additional class name', () => {
const { container } = render(<FileList files={files} className="extra" />)
const { container } = render(<FileList className="extra" />)

const component = container.querySelector(':only-child')

Expand All @@ -36,7 +36,7 @@ describe('FileList', () => {
it('supports ForwardRef in React', () => {
const ref = createRef<HTMLOListElement>()

const { container } = render(<FileList files={files} ref={ref} />)
const { container } = render(<FileList ref={ref} />)

const component = container.querySelector(':only-child')

Expand Down
49 changes: 11 additions & 38 deletions packages/react/src/FileList/FileList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,23 @@
* Copyright Gemeente Amsterdam
*/

import { DocumentIcon } from '@amsterdam/design-system-react-icons'
import clsx from 'clsx'
import { forwardRef } from 'react'
import type { ForwardedRef, HTMLAttributes } from 'react'
import { Button } from '../Button'
import { Icon } from '../Icon'
import { formatFileSize } from '../common/formatFileSize'
import { formatFileType } from '../common/formatFileType'
import type { ForwardedRef, HTMLAttributes, PropsWithChildren } from 'react'
import { FileListItem } from './FileListItem'

export type FileListProps = {
files: FileList
// eslint-disable-next-line no-unused-vars
onDelete?: (index: number) => void
} & HTMLAttributes<HTMLOListElement>
export type FileListProps = {} & PropsWithChildren<HTMLAttributes<HTMLUListElement>>

export const FileList = forwardRef(
({ files, onDelete, className, ...restProps }: FileListProps, ref: ForwardedRef<HTMLOListElement>) => (
export const FileListRoot = forwardRef(
({ children, className, ...restProps }: FileListProps, ref: ForwardedRef<HTMLOListElement>) => (
<ul {...restProps} ref={ref} className={clsx('ams-file-list', className)}>
{Array.from(files).map((file, index) => (
<li key={index} className="ams-file-list__file">
<div className="ams-file-list__file-preview">
{file.type.includes('image') ? (
<img src={URL.createObjectURL(file)} alt={file.name} />
) : (
<Icon svg={DocumentIcon} size="level-3" square />
)}
</div>
<div className="ams-file-list__file-info">
{file.name}
<div className="ams-file-input__file-details">
({formatFileType(file.type)}, {formatFileSize(file.size)})
</div>
</div>
{onDelete && (
<div>
<Button variant="tertiary" onClick={() => onDelete(index)}>
Verwijder
</Button>
</div>
)}
</li>
))}
{children}
</ul>
),
)

FileList.displayName = 'FileList'
FileListRoot.displayName = 'FileList'

export const FileList = Object.assign(FileListRoot, {
Item: FileListItem,
})
47 changes: 47 additions & 0 deletions packages/react/src/FileList/FileListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @license EUPL-1.2+
* Copyright Gemeente Amsterdam
*/

import { DocumentIcon } from '@amsterdam/design-system-react-icons'
import clsx from 'clsx'
import { forwardRef } from 'react'
import type { ForwardedRef, HTMLAttributes } from 'react'
import { Button } from '../Button'
import { Icon } from '../Icon'
import { formatFileSize } from '../common/formatFileSize'
import { formatFileType } from '../common/formatFileType'

export type FileListItemProps = {
file: File
onRemove?: () => void
} & HTMLAttributes<HTMLLIElement>

export const FileListItem = forwardRef(
({ file, onRemove, className, ...restProps }: FileListItemProps, ref: ForwardedRef<HTMLLIElement>) => (
<li {...restProps} ref={ref} className={clsx('ams-file-list__file', className)}>
<div className="ams-file-list__file-preview">
{file.type.includes('image') ? (
<img src={URL.createObjectURL(file)} alt={file.name} />
) : (
<Icon svg={DocumentIcon} size="level-3" square />
)}
</div>
<div className="ams-file-list__file-info">
{file.name}
<div className="ams-file-input__file-details">
({formatFileType(file.type)}, {formatFileSize(file.size)})
</div>
</div>
{onRemove && (
<div>
<Button variant="tertiary" onClick={() => onRemove()}>
Verwijder
</Button>
</div>
)}
</li>
),
)

FileListItem.displayName = 'FileList.Item'
8 changes: 7 additions & 1 deletion storybook/src/components/FileList/FileInputWithFileList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ export const FileInputWithFileList = () => {
return (
<>
<FileInput multiple ref={inputRef} onChange={changeFiles} />
{files && <FileList files={files} onDelete={removeFile} />}
{files && (
<FileList>
{Array.from(files).map((file, index) => (
<FileList.Item key={index} file={file} onRemove={() => removeFile(index)} />
))}
</FileList>
)}
</>
)
}
23 changes: 10 additions & 13 deletions storybook/src/components/FileList/FileList.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,20 @@ import { FileList } from '@amsterdam/design-system-react/src'
import { Meta, StoryObj } from '@storybook/react'
import { FileInputWithFileList } from './FileInputWithFileList'

const sampleDataTransfer = new DataTransfer()
sampleDataTransfer.items.add(new File(['sample1'], 'sample1.txt', { type: 'text/plain', lastModified: Date.now() }))
sampleDataTransfer.items.add(new File(['sample2'], 'sample2.txt', { type: 'text/plain', lastModified: Date.now() }))
const sampleFiles = sampleDataTransfer.files

const meta = {
title: 'Components/Forms/File List',
component: FileList,
args: {
files: sampleFiles,
},
argTypes: {
files: {
table: {
disable: true,
},
},
children: [
<FileList.Item
key="1"
file={new File(['sample1'], 'sample1.txt', { type: 'text/plain', lastModified: Date.now() })}
/>,
<FileList.Item
key="2"
file={new File(['sample2'], 'sample2.txt', { type: 'text/plain', lastModified: Date.now() })}
/>,
],
},
} satisfies Meta<typeof FileList>

Expand Down

0 comments on commit 49c7ba4

Please sign in to comment.