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

fix(Forms): add support for sessionStorageId in Field.Upload #4424

Merged
merged 7 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Flex } from '@dnb/eufemia/src'
import ComponentBox from '../../../../../../../shared/tags/ComponentBox'
import { Field, Form, Tools } from '@dnb/eufemia/src/extensions/forms'
import {
Field,
Form,
Tools,
Value,
} from '@dnb/eufemia/src/extensions/forms'
import { createMockFile } from '../../../../../../../docs/uilib/components/upload/Examples'
import useUpload from '@dnb/eufemia/src/components/upload/useUpload'
import { UploadValue } from '@dnb/eufemia/src/extensions/forms/Field/Upload'
Expand Down Expand Up @@ -252,3 +257,27 @@ export const WithAsyncOnFileClick = () => {
</ComponentBox>
)
}

export function SessionStorage() {
return (
<ComponentBox>
<Form.Handler sessionStorageId="documents">
<Flex.Stack>
<Form.Card>
<Field.Upload path="/documents" />
<Value.Upload
path="/documents"
label="Uploaded files"
placeholder="No files uploaded."
variant="ol"
showEmpty
/>
</Form.Card>

<Form.SubmitButton />
<Tools.Log />
</Flex.Stack>
</Form.Handler>
</ComponentBox>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ import * as Examples from './Examples'

<Examples.Customized />

### Session storage support

The `sessionStorageId` property can be used to store the files in the session storage so they persist between page reloads.

<Examples.SessionStorage />

### With asynchronous file handler

The `fileHandler` property supports an asynchronous function, and can be used for handling/validating files asynchronously, like to upload files to a virus checker and display errors based on the outcome:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,9 @@ function MyForm() {
)
}
```

### Persist files in session storage

The `sessionStorageId` property can be used to store the files in the session storage so they persist between page reloads.

But the persisted files only render the file name, and not the file itself. The file blob will be lost during the serialization process.
39 changes: 33 additions & 6 deletions packages/dnb-eufemia/src/extensions/forms/Field/Upload/Upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,22 @@ function UploadComponent(props: Props) {
[formsTr.errorRequired]
)

const fromInput = useCallback((value: UploadValue) => {
value.forEach((item, index) => {
value[index] = item

// Store the name in the value, to support session storage (serialization)
value[index]['name'] = item['name'] || item.file?.name
})

return value
}, [])

const preparedProps = {
errorMessages,
validateRequired,
fromInput,
toInput: transformFiles,
...props,
}

Expand Down Expand Up @@ -128,11 +141,7 @@ function UploadComponent(props: Props) {
}, [files])

useEffect(() => {
// Files stored in session storage will not have a property (due to serialization).
const hasInvalidFiles = value?.some(({ file }) => !file?.name)
if (!hasInvalidFiles) {
setFiles(value)
}
setFiles(value)
}, [setFiles, value])

const handleChangeAsync = useCallback(
Expand Down Expand Up @@ -173,7 +182,7 @@ function UploadComponent(props: Props) {
handleChange(existingFiles)
}
},
[files, setFiles, fileHandler, handleChange]
[setFiles, fileHandler, handleChange]
)

const changeHandler = useCallback(
Expand Down Expand Up @@ -241,3 +250,21 @@ function UploadComponent(props: Props) {
export default UploadComponent

UploadComponent._supportsSpacingProps = true

export function transformFiles(value: UploadValue) {
if (Array.isArray(value)) {
if (value.length === 0) {
return undefined
}

value.map((item) => {
if (item?.file && !(item.file instanceof File)) {
// To support session storage, we recreated the file blob.
item['file'] = new File([], item['name'])
}
return item
})
}

return value
}
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,13 @@ describe('Field.Upload', () => {
file: file1,
id: expect.any(String),
exists: expect.any(Boolean),
name: 'fileName-1.png',
},
{
file: file2,
id: expect.any(String),
exists: expect.any(Boolean),
name: 'fileName-2.png',
},
],
},
Expand Down Expand Up @@ -256,6 +258,7 @@ describe('Field.Upload', () => {
file: file1,
exists: false,
id: expect.anything(),
name: 'fileName-1.png',
},
{
errorMessage: nbShared.Upload.errorLargeFile.replace(
Expand All @@ -265,6 +268,7 @@ describe('Field.Upload', () => {
file: file2,
exists: false,
id: expect.anything(),
name: 'fileName-2.png',
},
],
},
Expand All @@ -276,6 +280,7 @@ describe('Field.Upload', () => {
file: file1,
exists: false,
id: expect.anything(),
name: 'fileName-1.png',
},
{
errorMessage: nbShared.Upload.errorLargeFile.replace(
Expand All @@ -285,6 +290,7 @@ describe('Field.Upload', () => {
file: file2,
exists: false,
id: expect.anything(),
name: 'fileName-2.png',
},
])

Expand All @@ -310,6 +316,7 @@ describe('Field.Upload', () => {
file: file1,
exists: false,
id: expect.anything(),
name: 'fileName-1.png',
},
],
},
Expand Down Expand Up @@ -353,6 +360,7 @@ describe('Field.Upload', () => {
exists: false,
file: file1,
id: expect.any(String),
name: 'fileName-1.png',
}),
],
},
Expand Down Expand Up @@ -399,6 +407,7 @@ describe('Field.Upload', () => {
exists: false,
file: file1,
id: expect.any(String),
name: 'fileName-1.png',
}),
],
},
Expand Down Expand Up @@ -468,6 +477,7 @@ describe('Field.Upload', () => {
file: file1,
exists: false,
id: expect.anything(),
name: 'fileName-1.png',
},
],
},
Expand Down Expand Up @@ -734,6 +744,7 @@ describe('Field.Upload', () => {
file: file1,
exists: false,
id: expect.anything(),
name: 'fileName-1.png',
},
],
},
Expand Down Expand Up @@ -1475,7 +1486,7 @@ describe('Field.Upload', () => {
})
})

it('should not set files from session storage if they are invalid', async () => {
it('should recreate files from session storage', async () => {
const file = createMockFile('fileName.png', 100, 'image/png')

const { unmount } = render(
Expand Down Expand Up @@ -1517,15 +1528,16 @@ describe('Field.Upload', () => {
expect(dataContext.internalDataRef.current.myFiles).toEqual([
{
exists: false,
file: {},
file: new File([], 'fileName.png'),
id: expect.any(String),
name: 'fileName.png',
},
])
const [title] = Array.from(document.querySelectorAll('p'))
expect(title).toHaveTextContent(nbShared.Upload.title)
expect(
document.querySelectorAll('.dnb-upload__file-cell').length
).toBe(0)
).toBe(1)
})

describe('transformIn and transformOut', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Field, Form, Tools } from '../../..'
import { Field, Form, Tools, Value } from '../../..'
import { Flex } from '../../../../../components'
import { UploadFileNative } from '../../../../../components/Upload'
import { createRequest } from '../../../Form/Handler/stories/FormHandler.stories'
Expand Down Expand Up @@ -216,8 +216,6 @@ export function TransformInAndOut() {
>
<Flex.Stack>
<Field.Upload
label="Label"
placeholder="This is a Field"
path="/documents"
transformIn={transformIn}
transformOut={transformOut}
Expand All @@ -233,3 +231,25 @@ export function TransformInAndOut() {
</Form.Handler>
)
}

export function SessionStorage() {
return (
<Form.Handler sessionStorageId="documents">
<Flex.Stack>
<Form.Card>
<Field.Upload path="/documents" />
<Value.Upload
path="/documents"
label="Uploaded files"
placeholder="No files uploaded."
variant="ol"
showEmpty
/>
</Form.Card>

<Form.SubmitButton />
<Tools.Log />
</Flex.Stack>
</Form.Handler>
)
}
23 changes: 19 additions & 4 deletions packages/dnb-eufemia/src/extensions/forms/Value/Upload/Upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import ListFormat, {
import type { UploadFile } from '../../../../components/upload/types'
import { getFileIcon } from '../../../../components/upload/UploadFileListCell'
import { BYTES_IN_A_MEGA_BYTE } from '../../../../components/upload/UploadVerify'
import { Props as FieldUploadProps } from '../../Field/Upload/Upload'
import {
Props as FieldUploadProps,
transformFiles,
} from '../../Field/Upload/Upload'
import { format } from '../../../../components/number-format/NumberUtils'
import { UploadFileLink } from '../../../../components/upload/UploadFileListLink'
import { isAsync } from '../../../../shared/helpers/isAsync'
Expand All @@ -21,8 +24,12 @@ export type Props = ValueProps<Array<UploadFile>> &
}

function Upload(props: Props) {
const preparedProps = {
fromExternal: transformFiles,
...props,
}

const {
path,
value,
format,
className,
Expand All @@ -32,7 +39,7 @@ function Upload(props: Props) {
displaySize = false,
onFileClick,
...rest
} = useValueProps(props)
} = useValueProps(preparedProps)

const list = useMemo(() => {
const valueToUse =
Expand Down Expand Up @@ -62,7 +69,15 @@ function Upload(props: Props) {
/>
)
}
}, [path, value, variant, listType])
}, [
value,
download,
displaySize,
onFileClick,
format,
variant,
listType,
])

return (
<ValueBlock
Expand Down
Loading
Loading