From 330434709ddf31e3916f6fdf96d082986f0f0503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85dne=20Rosenvinge?= <41082005+AadneRo@users.noreply.github.com> Date: Thu, 13 Oct 2022 07:57:29 +0200 Subject: [PATCH] feat(Upload): add new Upload component (#1581) * feat(Upload) Add Upload component mvp. Still missing some features: - Drag and drop to upload file * Use types instead of interface * Ensure css idiomatic ordering (declaration) Ref.: https://github.com/necolas/idiomatic-css#declaration-order * Add css dependencies * Import icons with "as" for less duplication * Remove unnecessary div * Fix lists spacing * Move less important elements down in the DOM tree * Ensure the outline (SVG stroke element) is not select able with mouse cursor * Ensure hidden input file element takes no space in layout * Correct UX spacing * Update snapshots * Fix failing axe test by linking input labels * Set correct spacing * Set focus on button when file gets deleted * Spacing correction after PR #1626 * Add mocked example files * Animate canvas * Update visual snapshots * Format proerties docs * Send skeleton properties down to sibling components Co-authored-by: Tobias --- .../src/docs/uilib/components/upload.md | 14 + .../docs/uilib/components/upload/Examples.tsx | 230 ++++++ .../src/docs/uilib/components/upload/demos.md | 58 ++ .../src/docs/uilib/components/upload/info.md | 7 + .../uilib/components/upload/properties.md | 22 + packages/dnb-eufemia/src/components/Upload.js | 14 + packages/dnb-eufemia/src/components/index.js | 2 + packages/dnb-eufemia/src/components/lib.js | 3 + .../src/components/upload/Upload.tsx | 193 +++++ .../src/components/upload/UploadFileInput.tsx | 104 +++ .../components/upload/UploadFileListCell.tsx | 186 +++++ .../__tests__/Upload.screenshot.test.js | 34 + .../upload/__tests__/Upload.test.tsx | 379 ++++++++++ .../upload/__tests__/UploadFileInput.test.tsx | 138 ++++ .../__tests__/UploadFileListCell.test.tsx | 335 +++++++++ .../__snapshots__/Upload.test.tsx.snap | 695 ++++++++++++++++++ ...t-have-to-match-file-list-1-c85d6.snap.png | Bin 0 -> 33711 bytes ...e-custom-accepted-formats-1-b65b2.snap.png | Bin 0 -> 24691 bytes ...have-to-match-the-default-1-33947.snap.png | Bin 0 -> 24861 bytes ...o-match-the-loading-state-1-b2ffe.snap.png | Bin 0 -> 29663 bytes .../upload/__tests__/testHelpers.ts | 13 + .../upload/__tests__/useUpload.test.tsx | 116 +++ .../src/components/upload/index.js | 8 + .../upload/stories/Upload.stories.tsx | 73 ++ .../src/components/upload/style.js | 6 + .../src/components/upload/style/_upload.scss | 109 +++ .../components/upload/style/dnb-upload.scss | 16 + .../src/components/upload/style/index.js | 6 + .../src/components/upload/types.ts | 56 ++ .../src/components/upload/useUpload.ts | 25 + packages/dnb-eufemia/src/index.js | 2 + packages/dnb-eufemia/src/shared/Context.tsx | 2 + .../dnb-eufemia/src/shared/locales/en-GB.js | 13 + .../dnb-eufemia/src/shared/locales/nb-NO.js | 13 + .../src/style/dnb-ui-components.scss | 1 + .../__snapshots__/Elements.test.js.snap | 5 +- .../dnb-eufemia/src/style/elements/lists.scss | 7 +- 37 files changed, 2883 insertions(+), 2 deletions(-) create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/components/upload.md create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/components/upload/demos.md create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/components/upload/info.md create mode 100644 packages/dnb-design-system-portal/src/docs/uilib/components/upload/properties.md create mode 100644 packages/dnb-eufemia/src/components/Upload.js create mode 100644 packages/dnb-eufemia/src/components/upload/Upload.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/UploadFileInput.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/UploadFileListCell.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/Upload.screenshot.test.js create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/Upload.test.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/UploadFileInput.test.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/UploadFileListCell.test.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-file-list-1-c85d6.snap.png create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-the-custom-accepted-formats-1-b65b2.snap.png create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-the-default-1-33947.snap.png create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-the-loading-state-1-b2ffe.snap.png create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/testHelpers.ts create mode 100644 packages/dnb-eufemia/src/components/upload/__tests__/useUpload.test.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/index.js create mode 100644 packages/dnb-eufemia/src/components/upload/stories/Upload.stories.tsx create mode 100644 packages/dnb-eufemia/src/components/upload/style.js create mode 100644 packages/dnb-eufemia/src/components/upload/style/_upload.scss create mode 100644 packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss create mode 100644 packages/dnb-eufemia/src/components/upload/style/index.js create mode 100644 packages/dnb-eufemia/src/components/upload/types.ts create mode 100644 packages/dnb-eufemia/src/components/upload/useUpload.ts diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/upload.md b/packages/dnb-design-system-portal/src/docs/uilib/components/upload.md new file mode 100644 index 00000000000..6ed3f5f338f --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/upload.md @@ -0,0 +1,14 @@ +--- +title: 'Upload' +description: 'The Upload widget should be used in scenarios where the user has to upload files. Files can be uploaded by clicking button. You also have the opportunity to add descriptive texts below the title where you could put max file size, allowed fileformats etc.' +status: 'new' +showTabs: true +hideTabs: + - title: Events +--- + +import UploadInfo from 'Docs/uilib/components/upload/info' +import UploadDemos from 'Docs/uilib/components/upload/demos' + + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx new file mode 100644 index 00000000000..8dd6fc25134 --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/Examples.tsx @@ -0,0 +1,230 @@ +/** + * UI lib Component Example + * + */ + +import React from 'react' +import ComponentBox from 'dnb-design-system-portal/src/shared/tags/ComponentBox' + +const createMockFile = (name: string, size: number, type: string) => { + const file = new File([], name, { type }) + Object.defineProperty(file, 'size', { + get() { + return size + }, + }) + return file +} + +const useMockFiles = (setFiles, extend) => { + React.useEffect(() => { + setFiles([ + { + file: createMockFile('fileName.png', 123, 'image/png'), + ...extend, + }, + ]) + }, []) +} + +export const UploadPrefilledFileList = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files, setFiles} = Upload.useUpload('file-list') + useMockFiles(setFiles, { errorMessage: 'This is no real file!' }) + + return ( + + ) +} +render() + ` + } + +) + +export const UploadBasic = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files} = Upload.useUpload('upload-basic') + + return ( + + ) +} +render() + ` + } + +) + +export const UploadMultipleFiles = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files, setFiles} = Upload.useUpload('upload-multiple-files') + return ( + + ) +} +render() + ` + } + +) +export const UploadRemoveFile = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files, setFiles} = Upload.useUpload('upload-remove-files') + return ( + <> + + + + ) +} +render() + ` + } + +) + +export const UploadIsLoading = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files, setFiles} = Upload.useUpload('upload-is-loading') + useMockFiles(setFiles, { isLoading: true }) + + return ( + <> + + { + setFiles(files.map((file) => { + return {...file, isLoading: checked} + })) + }} + >Files is loading toggle + + ) +} +render() + ` + } + +) + +export const UploadErrorMessage = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files, setFiles} = Upload.useUpload('upload-error-message') + return ( + <> + + { + setFiles( + files.map((file) => { + return {...file, errorMessage: checked? 'custom error message': null} + }) + ) + } + } + > + Toggle error message + + + ) +} +render() + ` + } + +) + +export const UploadAcceptedFormats = () => ( + + { + /* jsx */ ` +const Component = () => { + const {files, setFiles} = Upload.useUpload('upload-accepted-formats') + + return ( + + ) +} +render() + ` + } + +) + +export const UploadCustomText = () => ( + + { + /* jsx */ ` + + ` + } + +) diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/upload/demos.md b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/demos.md new file mode 100644 index 00000000000..96d5567e7dc --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/demos.md @@ -0,0 +1,58 @@ +--- +showTabs: true +--- + +import { +UploadBasic, +UploadPrefilledFileList, +UploadRemoveFile, +UploadMultipleFiles, +UploadIsLoading, +UploadErrorMessage, +UploadAcceptedFormats, +UploadCustomText, +} from 'Docs/uilib/components/upload/Examples' + +## Demos + +### Upload (default) + + + +### 'useUpload' React Hook + +By using the `Upload.useUpload` you can remove or add files or the status displayed in the component. + + + +### Upload multiple files + + + +### Upload loading state + +When uploading the file you can set the loading state of the request using the `Upload.useUpload` hook and passing isLoading to the file that is being uploaded. + + + +### Upload error message + +The only checks we do currently is for the file size and the file type. These errors are handled by the HTML element ´input´ so they aren't selectable. If you want any other error messages you can use the `Upload.useUpload` the same way as with the loading state. + + + +### Upload specific accepted file formats + +You can pass the file formats as a string array. This will restrict which files that can be selected. + + + +### Upload custom text + +All the text can be custom. + + + +### Upload with prefilled error + + diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/upload/info.md b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/info.md new file mode 100644 index 00000000000..a11fa3d49d5 --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/info.md @@ -0,0 +1,7 @@ +--- +showTabs: true +--- + +## Description + +The Upload should be used in scenarios where the user has to upload any kind of files. diff --git a/packages/dnb-design-system-portal/src/docs/uilib/components/upload/properties.md b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/properties.md new file mode 100644 index 00000000000..39887de7e18 --- /dev/null +++ b/packages/dnb-design-system-portal/src/docs/uilib/components/upload/properties.md @@ -0,0 +1,22 @@ +--- +showTabs: true +--- + +## Properties + +| Properties | Description | +| ------------------------------------------- | ----------------------------------------------------------------------------------- | +| `acceptedFileTypes` | _(required)_ List of accepted file types. | +| `fileMaxSize` | _(optional)_ fileMaxSize is max size of each file in MB | +| `multipleFiles` | _(optional)_ if set true, accepting multiple files is allowed | +| `title` | _(optional)_ Custom text property. Replaces the default title | +| `text` | _(optional)_ Custom text property. Replaces the default text | +| `formatsDescription` | _(optional)_ Custom text property. Replaces the default accepted format description | +| `fileSizeDescription` | _(optional)_ Custom text property. Replaces the default max file size description | +| `fileSizeContent` | _(optional)_ Custom text property. Replaces the default file size content | +| `uploadButtonText` | _(optional)_ Custom text property. Replaces the default upload button text | +| `uploadLoadingText` | _(optional)_ Custom text property. Replaces the default loading text | +| `deleteButton` | _(optional)_ Custom text property. Replaces the default delete button text | +| `fileListAriaLabel` | _(optional)_ Custom text property. Replaces the default list aria label | +| `skeleton` | _(optional)_ Skeleton should be applied when loading content Default: null. | +| [Space](/uilib/components/space/properties) | _(optional)_ Spacing properties like `top` or `bottom` are supported. | diff --git a/packages/dnb-eufemia/src/components/Upload.js b/packages/dnb-eufemia/src/components/Upload.js new file mode 100644 index 00000000000..90d57a9f132 --- /dev/null +++ b/packages/dnb-eufemia/src/components/Upload.js @@ -0,0 +1,14 @@ +/** + * ATTENTION: This file is auto generated by using "prepareTemplates". + * Do not change the content! + * + */ + +/** + * Library Index upload to autogenerate all the components and extensions + * Used by "prepareUploads" + */ + +import Upload from './upload/Upload' +export * from './upload/Upload' +export default Upload diff --git a/packages/dnb-eufemia/src/components/index.js b/packages/dnb-eufemia/src/components/index.js index 992019406e8..83e8722eddd 100644 --- a/packages/dnb-eufemia/src/components/index.js +++ b/packages/dnb-eufemia/src/components/index.js @@ -54,6 +54,7 @@ import Textarea from './textarea/Textarea' import Timeline from './timeline/Timeline' import ToggleButton from './toggle-button/ToggleButton' import Tooltip from './tooltip/Tooltip' +import Upload from './upload/Upload' import VisuallyHidden from './visually-hidden/VisuallyHidden' // define / export all the available components @@ -102,5 +103,6 @@ export { Timeline, ToggleButton, Tooltip, + Upload, VisuallyHidden, } diff --git a/packages/dnb-eufemia/src/components/lib.js b/packages/dnb-eufemia/src/components/lib.js index a6af619a972..a075f9a7b0a 100644 --- a/packages/dnb-eufemia/src/components/lib.js +++ b/packages/dnb-eufemia/src/components/lib.js @@ -56,6 +56,7 @@ import Textarea from './textarea/Textarea' import Timeline from './timeline/Timeline' import ToggleButton from './toggle-button/ToggleButton' import Tooltip from './tooltip/Tooltip' +import Upload from './upload/Upload' import VisuallyHidden from './visually-hidden/VisuallyHidden' // define / export all the available components @@ -104,6 +105,7 @@ export { Timeline, ToggleButton, Tooltip, + Upload, VisuallyHidden, } @@ -153,6 +155,7 @@ export const getComponents = () => { Timeline, ToggleButton, Tooltip, + Upload, VisuallyHidden, } } diff --git a/packages/dnb-eufemia/src/components/upload/Upload.tsx b/packages/dnb-eufemia/src/components/upload/Upload.tsx new file mode 100644 index 00000000000..909b3e35d1b --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/Upload.tsx @@ -0,0 +1,193 @@ +import React from 'react' +import classnames from 'classnames' + +// Components +import Lead from '../../elements/Lead' +import P from '../../elements/P' +import Dl from '../../elements/Dl' +import Dt from '../../elements/Dt' +import Dd from '../../elements/Dd' +import HeightAnimation from '../height-animation/HeightAnimation' + +// Shared +import { createSpacingClasses } from '../space/SpacingHelper' +import Provider from '../../shared/Provider' +import Context from '../../shared/Context' +import { extendPropsWithContext } from '../../shared/component-helper' +import { format } from '../number-format/NumberUtils' +import { LocaleProps, SpacingProps } from 'src/shared/types' + +// Internal +import UploadFileInput from './UploadFileInput' +import type { UploadFile, UploadProps } from './types' +import UploadFileListCell from './UploadFileListCell' +import useUpload from './useUpload' + +export const defaultProps = { + fileMaxSize: 5000, +} + +const Upload = (localProps: UploadProps & SpacingProps & LocaleProps) => { + const context = React.useContext(Context) + + const extendedProps = extendPropsWithContext( + localProps, + defaultProps, + { skeleton: context?.skeleton }, + context.getTranslation(localProps).Upload, + context.Upload + ) + + const { + id, + skeleton, + className, + acceptedFileTypes, + multipleFiles, + fileMaxSize, + title, + text, + formatsDescription, + fileSizeDescription, + fileSizeContent, + uploadButtonText, + uploadLoadingText, + uploadErrorLargeFile, + deleteButton, + fileListAriaLabel, + ...props + } = extendedProps + + const spacingClasses = createSpacingClasses(props) + + const { files, setFiles } = useUpload(id) + + const prettyfiedAcceptedFileFormats = acceptedFileTypes + .join(', ') + .toUpperCase() + + return ( + + + + {title} + + +

+ {text} +

+ +
+ +
+ {formatsDescription} +
+
+ {prettyfiedAcceptedFileFormats} +
+
+ + +
+ {fileSizeDescription} +
+
+ {String(fileSizeContent).replace( + '%size', + format(fileMaxSize).toString() + )} +
+
+
+ + + + + + + + +
+
+ ) + + function UploadFileList() { + if (files == null || files.length < 1) return null + + return ( +
    + {files.map((uploadFile: UploadFile, index: number) => { + const onDeleteFile = () => { + const cleanedFiles = files.filter( + (fileListElement) => fileListElement.file != uploadFile.file + ) + setFiles(cleanedFiles) + } + return ( + + ) + })} +
+ ) + } + + function onInputUpload(addedFiles: UploadFile[]) { + const newFiles = [...files, ...addedFiles] + + setFiles(newFiles) + } +} + +Upload.useUpload = useUpload + +export default Upload diff --git a/packages/dnb-eufemia/src/components/upload/UploadFileInput.tsx b/packages/dnb-eufemia/src/components/upload/UploadFileInput.tsx new file mode 100644 index 00000000000..80e5b532d30 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/UploadFileInput.tsx @@ -0,0 +1,104 @@ +import React, { useEffect, useRef } from 'react' + +// Components +import Button from '../button/Button' + +// Icons +import { folder as FolderIcon } from '../../icons' + +// Shared +import { format } from '../number-format/NumberUtils' +import { makeUniqueId } from '../../shared/component-helper' + +// Internal +import { UploadFile } from './types' + +export type UploadFileInputProps = { + id?: string + acceptedFormats: string[] + onUpload: (files: UploadFile[]) => void + fileMaxSize: number + uploadFileButtonText: React.ReactNode + uploadErrorLargeFile: React.ReactNode + multipleFiles: boolean +} + +const BYTES_IN_A_MEGA_BYTE = 1048576 + +const UploadFileInput = ({ + id, + acceptedFormats, + uploadFileButtonText, + onUpload, + fileMaxSize, + uploadErrorLargeFile, + multipleFiles = false, +}: UploadFileInputProps) => { + const fileInput = useRef(null) + + const accept = acceptedFormats.reduce((accept, format, index) => { + const previus = index === 0 ? '' : `${accept},` + return `${previus} .${format}` + }, '') + + useEffect(() => { + fileInput.current.value = null + fileInput.current.accept = accept + }, []) // eslint-disable-line react-hooks/exhaustive-deps + + const openFileDialog = () => fileInput.current?.click() + + const sharedId = id || makeUniqueId() + + return ( +
+ + + +
+ ) + + function handleFileInput({ target: { files } }) { + const uploadFile = [...Array(files.length)].map((_item, index) => { + const file: UploadFile = { file: files[index] } + const errorMessage = getErrorMessage(file.file.size) + + if (errorMessage) return { ...file, errorMessage } + return file + }) + onUpload(uploadFile) + } + + function getErrorMessage(fileSize: number) { + const errorMessage = String(uploadErrorLargeFile).replace( + '%size', + format(fileMaxSize).toString() + ) + // Converts from b (binary) to MB (decimal) + return fileSize / BYTES_IN_A_MEGA_BYTE > fileMaxSize + ? errorMessage + : null + } +} + +export default UploadFileInput diff --git a/packages/dnb-eufemia/src/components/upload/UploadFileListCell.tsx b/packages/dnb-eufemia/src/components/upload/UploadFileListCell.tsx new file mode 100644 index 00000000000..06dae5a4240 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/UploadFileListCell.tsx @@ -0,0 +1,186 @@ +import React, { useRef } from 'react' +import classnames from 'classnames' + +// Components +import Button from '../button/Button' +import Icon from '../../components/Icon' +import FormStatus from '../../components/FormStatus' +import ProgressIndicator from '../../components/progress-indicator' +import P from '../../../src/elements/P' + +// Icons +import { + trash as TrashIcon, + exclamation_medium as ExclamationIcon, + file_pdf_medium as pdf, + file_xls_medium as xls, + file_ppt_medium as ppt, + file_csv_medium as csv, + file_txt_medium as txt, + file_xml_medium as xml, + file_medium as file, +} from '../../icons' +import { UploadFile } from './types' + +// Shared +import { getPreviousSibling, warn } from '../../shared/component-helper' + +const images = { + pdf, + xls, + ppt, + csv, + txt, + xml, + file, +} + +export type UploadFileListCellProps = { + /** + * Uploaded file + */ + uploadFile: UploadFile + /** + * Calls onDelete when clicking the delete button + */ + onDelete: () => void + /** + * Text + */ + uploadLoadingText: React.ReactNode + deleteButtonText: React.ReactNode +} + +const UploadFileListCell = ({ + uploadFile, + onDelete, + uploadLoadingText, + deleteButtonText, +}: UploadFileListCellProps) => { + const { file, errorMessage, isLoading } = uploadFile + const { name, type } = file + + const fileType = type.split('/')[1] + + const hasWarning = errorMessage != null + + const imageUrl = URL.createObjectURL(file) + + const cellRef = useRef() + + const handleDisappearFocus = () => { + try { + const cellElement = cellRef.current + const focusElement = getPreviousSibling( + '.dnb-upload', + cellElement + ).querySelector( + '.dnb-upload__file-input-button' + ) as HTMLButtonElement + focusElement.focus() + } catch (e) { + warn(e) + } + } + + const onDeleteHandler = () => { + handleDisappearFocus() + + onDelete() + } + + return ( +
  • +
    +
    + {getIcon()} + {getTitle()} +
    +
    + +
    +
    + + {getWarning()} +
  • + ) + + function getIcon() { + if (isLoading) { + return + } + + if (hasWarning) return + + let iconFileType = fileType + + if (!Object.prototype.hasOwnProperty.call(images, fileType)) { + iconFileType = 'file' + } + return + } + + function getTitle() { + return isLoading ? ( +
    + {uploadLoadingText} +
    + ) : ( +
    + + {name} + +

    + {fileType.toUpperCase()} +

    +
    + ) + } + + function getWarning() { + return hasWarning ? ( + + ) : null + } +} + +export default UploadFileListCell diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/Upload.screenshot.test.js b/packages/dnb-eufemia/src/components/upload/__tests__/Upload.screenshot.test.js new file mode 100644 index 00000000000..a86e61d7d82 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/Upload.screenshot.test.js @@ -0,0 +1,34 @@ +/** + * Screenshot Test + * This file will not run on "test:staged" because we don't require any related files + */ + +import { + testPageScreenshot, + setupPageScreenshot, +} from '../../../core/jest/jestSetupScreenshots' + +describe('Upload screenshot', () => { + setupPageScreenshot({ url: '/uilib/components/upload/demos' }) + + it('have to match the default', async () => { + const screenshot = await testPageScreenshot({ + selector: '[data-visual-test="upload-basic"]', + }) + expect(screenshot).toMatchImageSnapshot() + }) + + it('have to match the loading state', async () => { + const screenshot = await testPageScreenshot({ + selector: '[data-visual-test="upload-is-loading"] .dnb-upload', + }) + expect(screenshot).toMatchImageSnapshot() + }) + + it('have to match file list', async () => { + const screenshot = await testPageScreenshot({ + selector: '[data-visual-test="upload-file-list"]', + }) + expect(screenshot).toMatchImageSnapshot() + }) +}) diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/Upload.test.tsx b/packages/dnb-eufemia/src/components/upload/__tests__/Upload.test.tsx new file mode 100644 index 00000000000..60ec704e2d5 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/Upload.test.tsx @@ -0,0 +1,379 @@ +import React, { useEffect } from 'react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import Upload from '../Upload' +import nbNO from '../../../shared/locales/nb-NO' +import createMockFile from './testHelpers' +import { loadScss, axeComponent } from '../../../core/jest/jestSetup' +import { UploadProps } from '../types' +import useUpload from '../useUpload' + +const nb = nbNO['nb-NO'].Upload + +global.URL.createObjectURL = jest.fn(() => 'url') + +const defaultProps: UploadProps = { + id: 'id', + acceptedFileTypes: ['png'], +} + +describe('Upload', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + it('renders the component', () => { + render() + + expect(screen.queryByTestId('upload')).not.toBeNull() + }) + + it('renders the upload file input section', () => { + render() + + expect(screen.queryByTestId('upload-file-input')).not.toBeNull() + }) + + describe('Text', () => { + it('renders the title', () => { + render() + + const element = screen.queryByTestId('upload-title') + + expect(element).not.toBeNull() + + expect(element.textContent).toMatch(nb.title) + }) + + it('renders the custom title', () => { + const customTitle = 'custom title' + + render() + + const element = screen.queryByTestId('upload-title') + + expect(element.textContent).toMatch(customTitle) + }) + + it('renders the text', () => { + render() + + const element = screen.queryByTestId('upload-text') + + expect(element).not.toBeNull() + + expect(element.textContent).toMatch(nb.text) + }) + + it('renders the custom text', () => { + const customText = 'custom text' + + render() + + const element = screen.queryByTestId('upload-text') + + expect(element.textContent).toMatch(customText) + }) + + it('renders the format description', () => { + render() + + const element = screen.queryByTestId( + 'upload-accepted-formats-description' + ) + + expect(element).not.toBeNull() + + expect(element.textContent).toMatch(nb.formatsDescription) + }) + + it('renders the custom format description', () => { + const customFormatDescription = 'custom formats description' + + render( + + ) + + const element = screen.queryByTestId( + 'upload-accepted-formats-description' + ) + + expect(element.textContent).toMatch(customFormatDescription) + }) + + it('renders the custom accepted format', () => { + const acceptedFileTypes = ['png, jpg'] + + render( + + ) + + const element = screen.queryByTestId('upload-accepted-formats') + + const formattedFileTypes = acceptedFileTypes.join(', ').toUpperCase() + + expect(element).not.toBeNull() + expect(element.textContent).toMatch(formattedFileTypes) + }) + + it('renders the file size description', () => { + render() + + const element = screen.queryByTestId('upload-file-size-description') + + expect(element).not.toBeNull() + }) + + it('renders the custom file size description', () => { + const fileSizeDescription = 'file size description' + + render( + + ) + + const element = screen.queryByTestId('upload-file-size-description') + + expect(element.textContent).toMatch(fileSizeDescription) + }) + + it('renders the file size', () => { + const fileMaxSize = 2 + render() + + const element = screen.queryByTestId('upload-file-size') + + expect(element).not.toBeNull() + expect(element.textContent).toMatch( + String(nb.fileSizeContent).replace('%size', fileMaxSize.toString()) + ) + }) + + it('renders the custom file size', () => { + const fileMaxSize = 2 + const fileSizeContent = '%size custom' + + render( + + ) + + const element = screen.queryByTestId('upload-file-size') + + expect(element.textContent).toMatch( + String(fileMaxSize).replace('%size', `${fileMaxSize}`) + ) + }) + + it('renders the upload file input section button text', () => { + render() + + const element = screen.queryByTestId('upload-file-input-button') + + expect(element.textContent).toMatch(nb.uploadButtonText) + }) + + it('renders the upload file input section button custom text', () => { + const uploadButtonText = 'upload button text' + + render( + + ) + + const element = screen.queryByTestId('upload-file-input-button') + + expect(element.textContent).toMatch(uploadButtonText) + }) + }) + + describe('useUpload', () => { + it('calls uses the useUpload hook to store files', async () => { + const validationFunction = jest.fn() + + const file = createMockFile('fileName.png', 100, 'image/png') + + const id = 'random-id' + + const expectedResult = [{ file }] + + render() + + const inputElement = screen.queryByTestId('upload-file-input-input') + + await waitFor(() => + fireEvent.change(inputElement, { + target: { files: [file] }, + }) + ) + + const MockComponent = () => { + const { files } = useUpload(id) + useEffect(() => validationFunction(files), []) + + return
    + } + + render() + + expect(validationFunction).toHaveBeenCalledWith(expectedResult) + }) + + it('renders the list of files', async () => { + const files = [ + { file: createMockFile('fileName.png', 100, 'image/png') }, + { file: createMockFile('fileName2.png', 100, 'image/png') }, + { file: createMockFile('fileName3.png', 100, 'image/png') }, + ] + + const id = 'random-id' + + render() + + const MockComponent = () => { + const { setFiles } = useUpload(id) + + useEffect(() => setFiles(files), []) + + return
    + } + + render() + + const fileCells = screen.queryAllByTestId('upload-file-list-cell') + + expect(fileCells.length).toBe(files.length) + }) + + it('shows no files', async () => { + const files = [] + + const id = 'random-id3' + + const { queryByTestId } = render( + + ) + + const MockComponent = () => { + const { setFiles } = useUpload(id) + + useEffect(() => setFiles(files), []) + + return
    + } + render() + + const emptyFileCell = queryByTestId('upload-file-list-cell') + + expect(emptyFileCell).toBeNull() + }) + + it('shows the file when the file is added', async () => { + const files = [ + { file: createMockFile('fileName.png', 100, 'image/png') }, + ] + + const id = 'random-id2' + + render() + + const MockComponent = () => { + const { setFiles } = useUpload(id) + + useEffect(() => setFiles(files), []) + + return
    + } + + const emptyFileCell = screen.queryByTestId('upload-file-list-cell') + + expect(emptyFileCell).toBeNull() + + render() + + const fileCell = screen.queryByTestId('upload-file-list-cell') + + expect(fileCell).not.toBeNull() + }) + + it('removes the file from the list when clicking delete', () => { + const files = [ + { file: createMockFile('fileName.png', 100, 'image/png') }, + ] + + const id = 'random-id3' + + const { queryByTestId } = render( + + ) + const MockComponent = () => { + const { setFiles } = useUpload(id) + + useEffect(() => setFiles(files), []) + + return
    + } + + render() + + const fileCell = queryByTestId('upload-file-list-cell') + + expect(fileCell).not.toBeNull() + + const deleteButton = queryByTestId('upload-delete-button') + + fireEvent.click(deleteButton) + + expect(queryByTestId('upload-file-list-cell')).toBeNull() + }) + + it('sets focus on choose button when clicking delete', () => { + const files = [ + { file: createMockFile('fileName.png', 100, 'image/png') }, + ] + + const id = 'random-id3' + + const { queryByTestId } = render( + + ) + const MockComponent = () => { + const { setFiles } = useUpload(id) + + useEffect(() => setFiles(files), []) + + return
    + } + + render() + + const deleteButton = queryByTestId('upload-delete-button') + + fireEvent.click(deleteButton) + + expect(document.activeElement).toBe( + queryByTestId('upload-file-input-button') + ) + }) + }) +}) + +describe('Upload aria', () => { + it('should validate', async () => { + const Component = render() + expect(await axeComponent(Component)).toHaveNoViolations() + }) +}) + +describe('Upload scss', () => { + it('have to match snapshot', () => { + const scss = loadScss(require.resolve('../style/dnb-upload.scss')) + expect(scss).toMatchSnapshot() + }) +}) diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/UploadFileInput.test.tsx b/packages/dnb-eufemia/src/components/upload/__tests__/UploadFileInput.test.tsx new file mode 100644 index 00000000000..a23267093ea --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/UploadFileInput.test.tsx @@ -0,0 +1,138 @@ +import React from 'react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import UploadFileInput, { UploadFileInputProps } from '../UploadFileInput' +import createMockFile from './testHelpers' + +const defaultProps: UploadFileInputProps = { + acceptedFormats: ['png'], + onUpload: jest.fn(), + uploadFileButtonText: 'upload button text', + fileMaxSize: 1000, + uploadErrorLargeFile: 'error message', + multipleFiles: false, +} + +describe('UploadFileInput', () => { + it('renders the component', () => { + render() + + expect(screen.queryByTestId('upload-file-input')).not.toBeNull() + }) + + it('renders the upload button', () => { + render() + expect(screen.queryByTestId('upload-file-input-button')).not.toBeNull() + }) + + it('renders the upload button text', () => { + const buttonText = 'button text' + render( + + ) + expect( + screen.queryByTestId('upload-file-input-button').textContent + ).toMatch(buttonText) + }) + + it('accepts multiple files when multipleFiles is true', () => { + render() + + const element = screen.queryByTestId('upload-file-input-input') + + expect(element.hasAttribute('multiple')).toBeTruthy() + }) + + it('renders the input', () => { + render() + const element = screen.queryByTestId('upload-file-input-input') + + expect(element).not.toBeNull() + expect(element.getAttribute('class')).toMatch('dnb-upload__file-input') + }) + + it('simulates a click on the input when clicking the button', () => { + render() + + const buttonElement = screen.queryByTestId('upload-file-input-button') + + const inputElement = screen.queryByTestId('upload-file-input-input') + + const clickEventListener = jest.fn() + inputElement.addEventListener('click', clickEventListener) + + fireEvent.click(buttonElement) + + expect(clickEventListener).toHaveBeenCalled() + }) + + it('calls the onUpload function', async () => { + const file = createMockFile('fileName.png', 100, 'image/png') + + const onUpload = jest.fn() + + render() + + const inputElement = screen.queryByTestId('upload-file-input-input') + + await waitFor(() => + fireEvent.change(inputElement, { + target: { files: [file] }, + }) + ) + expect(onUpload).toHaveBeenCalledWith([{ file }]) + }) + + it('can upload multiple files', async () => { + const file1 = createMockFile('fileName1.png', 100, 'image/png') + const file2 = createMockFile('fileName2.png', 100, 'image/png') + + const onUpload = jest.fn() + + render() + + const inputElement = screen.queryByTestId('upload-file-input-input') + + await waitFor(() => + fireEvent.change(inputElement, { + target: { files: [file1, file2] }, + }) + ) + expect(onUpload).toHaveBeenCalledWith([ + { file: file1 }, + { file: file2 }, + ]) + }) + + it('returns the file size error message', async () => { + const file1 = createMockFile('fileName1.png', 100000000, 'image/png') + + const fileMaxSize = 1 + const errorMessage = 'error message %size' + const errorMessageFormatted = `error message ${fileMaxSize}` + + const onUpload = jest.fn() + + render( + + ) + + const inputElement = screen.queryByTestId('upload-file-input-input') + + await waitFor(() => + fireEvent.change(inputElement, { + target: { files: [file1] }, + }) + ) + expect(onUpload).toHaveBeenCalledWith([ + { file: file1, errorMessage: errorMessageFormatted }, + ]) + }) +}) diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/UploadFileListCell.test.tsx b/packages/dnb-eufemia/src/components/upload/__tests__/UploadFileListCell.test.tsx new file mode 100644 index 00000000000..fe7ae0b7c77 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/UploadFileListCell.test.tsx @@ -0,0 +1,335 @@ +import UploadFileListCell, { + UploadFileListCellProps, +} from '../UploadFileListCell' +import createMockFile from './testHelpers' +import { render, screen } from '@testing-library/react' +import React from 'react' + +global.URL.createObjectURL = jest.fn(() => 'url') + +const defaultProps: UploadFileListCellProps = { + deleteButtonText: 'delete', + onDelete: jest.fn(), + uploadFile: { file: createMockFile('file.png', 100, 'image/png') }, + uploadLoadingText: 'loading', +} + +describe('UploadFileListCell', () => { + it('renders the component', () => { + render() + + const element = screen.queryByTestId('upload-file-list-cell') + + expect(element).not.toBeNull() + }) + + it('renders the error styling', () => { + render( + + ) + + const element = screen.queryByTestId('upload-file-list-cell') + + expect(element.className).toMatch('dnb-upload__file-cell--warning') + }) + + it('renders the no error styling', () => { + render( + + ) + + const element = screen.queryByTestId('upload-file-list-cell') + + expect(element.className).not.toMatch('dnb-upload__file-cell--error') + expect(element.className).toMatch('dnb-upload__file-cell') + }) + + it('renders the subtitle', () => { + render( + + ) + + const element = screen.queryByTestId('upload-subtitle') + + expect(element).not.toBeNull() + expect(element.textContent).toMatch('PNG') + }) + + it('renders the form errorMessage warning', () => { + render( + + ) + + const element = screen.queryByTestId('upload-warning') + + expect(element).not.toBeNull() + }) + + it('renders the form errorMessage warning message', () => { + const errorMessage = 'error message' + + render( + + ) + + const element = screen.queryByTestId('upload-warning') + + expect(element.textContent).toMatch(errorMessage) + }) + + describe('Icons', () => { + it('renders the exclamation icon', () => { + render( + + ) + + expect( + screen.queryByTestId('exclamation medium icon') + ).not.toBeNull() + }) + + it('renders the pdf icon', () => { + render( + + ) + + expect(screen.queryByTestId('file pdf medium icon')).not.toBeNull() + }) + + it('renders the xls icon', () => { + render( + + ) + + expect(screen.queryByTestId('file xls medium icon')).not.toBeNull() + }) + + it('renders the ppt icon', () => { + render( + + ) + + expect(screen.queryByTestId('file ppt medium icon')).not.toBeNull() + }) + + it('renders the csv icon', () => { + render( + + ) + + expect(screen.queryByTestId('file csv medium icon')).not.toBeNull() + }) + + it('renders the txt icon', () => { + render( + + ) + + expect(screen.queryByTestId('file txt medium icon')).not.toBeNull() + }) + + it('renders the xml icon', () => { + render( + + ) + + expect(screen.queryByTestId('file xml medium icon')).not.toBeNull() + }) + + it('renders the file icon as default', () => { + render( + + ) + + expect(screen.queryByTestId('file medium icon')).not.toBeNull() + }) + }) + + describe('File Anchor', () => { + it('renders the anchor', () => { + render() + const anchorElement = screen.queryByTestId('upload-file-anchor') + + expect(anchorElement).not.toBeNull() + }) + + it('renders the anchor text', () => { + const fileName = 'file.png' + + render( + + ) + const anchorElement = screen.queryByTestId('upload-file-anchor') + expect(anchorElement.textContent).toMatch(fileName) + }) + + it('renders the anchor href', () => { + const mockUrl = 'mock-url' + + global.URL.createObjectURL = jest.fn().mockReturnValueOnce(mockUrl) + + render( + + ) + const anchorElement = screen.queryByTestId( + 'upload-file-anchor' + ) as HTMLAnchorElement + expect(anchorElement.href).toMatch(mockUrl) + }) + + it('renders without the error style', () => { + render( + + ) + + const anchorElement = screen.queryByTestId('upload-file-anchor') + + expect(anchorElement.className).not.toMatch( + 'dnb-upload__file-cell--error' + ) + }) + }) + + describe('Delete Button', () => { + it('renders the delete button', () => { + render() + + const element = screen.queryByTestId('upload-delete-button') + + expect(element).not.toBeNull() + }) + + it('renders the delete button text', () => { + const deleteButtonText = 'delete' + + render( + + ) + + const element = screen.queryByTestId('upload-delete-button') + + expect(element.textContent).toMatch(deleteButtonText) + }) + + it('renders button as tertiary', () => { + render() + + const element = screen.queryByTestId('upload-delete-button') + + expect(element.className).toMatch('dnb-button--tertiary') + }) + + it('renders the file cell loading state', () => { + render( + + ) + + const element = screen.queryByTestId('upload-progress-indicator') + + expect(element).not.toBeNull() + }) + + it('does not render the loading state when not loading', () => { + render( + + ) + + const element = screen.queryByTestId('upload-progress-indicator') + + expect(element).toBeNull() + }) + }) +}) diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap b/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap new file mode 100644 index 00000000000..208c1ba6477 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/Upload.test.tsx.snap @@ -0,0 +1,695 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Upload scss have to match snapshot 1`] = ` +"/* +* DNB Upload +* +*/ +/** + * This file is only used to make components independent + * so that they can get imported individually, without the core styles + * + */ +/* + * Utilities + */ +/* + * Scopes + * + */ +/* + * Document Reset + * + */ +/* +* DNB icon +* +*/ +/* +* Icon component +* +*/ +.dnb-icon { + display: inline-block; + vertical-align: middle; + font-size: 1rem; + line-height: 1rem; + color: inherit; + width: 1em; + height: 1em; } + .dnb-icon img, + .dnb-icon svg { + width: inherit; + height: inherit; + shape-rendering: geometricPrecision; + vertical-align: top; } + .dnb-icon svg[width='100%'] { + width: inherit; } + .dnb-icon svg[height='100%'] { + height: inherit; } + .dnb-icon--inherit-color svg:not([fill]), + .dnb-icon--inherit-color svg [fill] { + fill: currentColor; } + .dnb-icon--inherit-color svg [stroke] { + stroke: currentColor; } + .dnb-icon--small { + font-size: 0.75rem; } + .dnb-icon--default { + font-size: 1rem; } + .dnb-icon--medium { + font-size: 1.5rem; } + .dnb-icon--large { + font-size: 2rem; } + .dnb-icon--x-large { + font-size: 2.5rem; } + .dnb-icon--xx-large { + font-size: 3rem; } + .dnb-icon--custom-size { + width: auto; + height: auto; } + .dnb-icon--auto { + font-size: 1em; } + .dnb-icon--auto > .dnb-icon--wrapper { + display: inline-flex; + align-items: center; + justify-content: center; } + h1 .dnb-icon, + h2 .dnb-icon, + h3 .dnb-icon, + h4 .dnb-icon, + h5 .dnb-icon, + h6 .dnb-icon { + vertical-align: middle; } + .dnb-icon.dnb-skeleton { + color: var(--skeleton-color) !important; } + .dnb-icon.dnb-skeleton::before, .dnb-icon.dnb-skeleton::after { + content: none !important; } + @media screen and (-ms-high-contrast: none) { + .dnb-icon { + flex: none; } } + +/* +* DNB Button +* +*/ +/* +* DNB Tooltip +* +*/ +.dnb-tooltip { + font-family: var(--font-family-default); + font-weight: var(--font-weight-basis); + font-size: var(--font-size-small); + font-style: normal; + line-height: var(--line-height-basis); + color: var(--color-black-80, #333); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + /** + * Ensure consistency and use the same as HTML reset -> html {...} + * between base and code package + */ + -moz-tab-size: 4; + tab-size: 4; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + word-break: break-word; + /** + * 1. Remove repeating backgrounds in all browsers (opinionated). + * 2. Add border box sizing in all browsers (opinionated). + */ + /** + * 1. Add text decoration inheritance in all browsers (opinionated). + * 2. Add vertical alignment inheritance in all browsers (opinionated). + */ + margin: 0; + padding: 0; } + .dnb-tooltip *, + .dnb-tooltip ::before, + .dnb-tooltip ::after { + background-repeat: no-repeat; + /* 1 */ + box-sizing: border-box; + /* 2 */ } + .dnb-tooltip ::before, + .dnb-tooltip ::after { + text-decoration: inherit; + /* 1 */ + vertical-align: inherit; + /* 2 */ } + +/* +* Tooltip component +* +*/ +.dnb-tooltip { + position: absolute; + z-index: 3100; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 0 1rem; + opacity: 0; + visibility: hidden; + transition: opacity 200ms cubic-bezier(0.42, 0, 0, 1); } + .dnb-tooltip--large { + padding: 0.25rem 1rem; } + .dnb-tooltip--animate_position { + transition: all 200ms cubic-bezier(0.42, 0, 0, 1), opacity 200ms cubic-bezier(0.42, 0, 0, 1); } + .dnb-tooltip--active { + visibility: visible; + /* + because of the first \\"show\\" we also use animation + also, use forwards because of the usage of visibility + */ + animation: show-tooltip 200ms cubic-bezier(0.42, 0, 0, 1) forwards; } + html[data-visual-test] .dnb-tooltip--active, .dnb-tooltip--active.dnb-tooltip--no-animation { + animation: show-tooltip 1ms cubic-bezier(0.42, 0, 0, 1) forwards; } + .dnb-tooltip--hide { + visibility: visible; + animation: hide-tooltip 200ms cubic-bezier(0.42, 0, 0, 1) forwards; } + .dnb-tooltip--hide.dnb-tooltip--no-animation { + animation: hide-tooltip 1ms linear forwards; } + .dnb-tooltip--fixed { + position: fixed; } + html[data-visual-test] .dnb-tooltip--hide { + animation: hide-tooltip 1ms linear 1s forwards; } + .dnb-tooltip__portal { + position: absolute; + top: 0; + left: 0; + right: 0; } + .dnb-tooltip__content { + min-width: 2rem; + min-height: 1.5rem; + padding: 0; } + .dnb-tooltip__arrow { + position: absolute; + pointer-events: none; + margin: 0; + width: 1rem; + height: 0.5rem; + overflow: hidden; } + .dnb-tooltip__arrow::before { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 1rem; + height: 1rem; + transform: translateY(70%) rotate(45deg); } + .dnb-tooltip__arrow__position--bottom { + top: -0.5rem; } + .dnb-tooltip__arrow__position--top { + bottom: -0.5rem; + transform: rotate(180deg); } + .dnb-tooltip__arrow__position--left { + right: -0.75rem; + margin-right: 3px; + transform: rotate(90deg); } + .dnb-tooltip__arrow__position--right { + left: -0.75rem; + margin-left: 3px; + transform: rotate(270deg); } + .dnb-tooltip__arrow__arrow--left { + align-self: flex-start; } + .dnb-tooltip__arrow__arrow--right { + align-self: flex-end; } + +@keyframes show-tooltip { + from { + opacity: 0; } + to { + opacity: 1; } } + +@keyframes hide-tooltip { + from { + opacity: 1; } + to { + opacity: 0; + visibility: hidden; } } + +/* +* DNB FormStatus +* +*/ +.dnb-form-status { + font-family: var(--font-family-default); + font-weight: var(--font-weight-basis); + font-size: var(--font-size-small); + font-style: normal; + line-height: var(--line-height-basis); + color: var(--color-black-80, #333); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + /** + * Ensure consistency and use the same as HTML reset -> html {...} + * between base and code package + */ + -moz-tab-size: 4; + tab-size: 4; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + word-break: break-word; + /** + * 1. Remove repeating backgrounds in all browsers (opinionated). + * 2. Add border box sizing in all browsers (opinionated). + */ + /** + * 1. Add text decoration inheritance in all browsers (opinionated). + * 2. Add vertical alignment inheritance in all browsers (opinionated). + */ + margin: 0; + padding: 0; } + .dnb-form-status *, + .dnb-form-status ::before, + .dnb-form-status ::after { + background-repeat: no-repeat; + /* 1 */ + box-sizing: border-box; + /* 2 */ } + .dnb-form-status ::before, + .dnb-form-status ::after { + text-decoration: inherit; + /* 1 */ + vertical-align: inherit; + /* 2 */ } + +/* + * FormStatus component + * + */ +:root { + --form-status-radius: 0.25rem; } + +.dnb-form-status { + display: flex; + opacity: 1; + transition: height 400ms cubic-bezier(0.42, 0, 0, 1), opacity 400ms cubic-bezier(0.42, 0, 0, 1), margin 400ms cubic-bezier(0.42, 0, 0, 1), padding 400ms cubic-bezier(0.42, 0, 0, 1); } + .dnb-form-status--hidden { + will-change: height, opacity, margin, padding; + width: 0; + height: 0; + opacity: 0; } + .dnb-form-status--is-animating { + overflow: hidden; + width: auto; } + .dnb-form-status--disappear, .dnb-form-status--hidden { + margin: 0 !important; + padding: 0 !important; } + .dnb-form-status__shell { + display: flex; + justify-content: flex-start; + align-items: flex-start; + min-width: inherit; + border-radius: var(--form-status-radius); } + .dnb-form-status__text { + padding: 0.625rem 1rem; + cursor: text; + color: inherit; + line-height: var(--line-height-small); + font-size: var(--font-size-small); + white-space: normal; } + button .dnb-form-status__text { + cursor: inherit; } + .dnb-form-status__text .dnb-anchor { + font-size: inherit; } + .dnb-icon + .dnb-form-status__text { + padding-left: 0.5rem; } + .dnb-form-status__shell > .dnb-icon { + display: flex; + justify-content: center; + align-items: center; + margin: 0.3333333em 0.3333333em 0.3333333em 0.6666666em; } + .dnb-form-status__size--large .dnb-form-status__text { + padding-top: 1.125rem; + padding-bottom: 1.125rem; } + .dnb-form-status__size--large .dnb-form-status__shell > .dnb-icon { + margin-top: 0.6666666em; + margin-bottom: 0.6666666em; } + .dnb-form-status--stretch { + flex-grow: 1; } + .dnb-form-status--stretch .dnb-form-status__shell { + width: 100%; } + .dnb-form-status--stretch .dnb-form-status__text { + max-width: 47rem; } + .dnb-form-status[hidden] { + display: none; } + .dnb-form-status--no-animation, + html[data-visual-test] .dnb-form-status { + transition-duration: 1ms !important; } + @media screen and (-ms-high-contrast: none) { + .dnb-form-status__shell > .dnb-icon { + border-width: 1px; } } + +.dnb-button { + font-family: var(--font-family-default); + font-weight: var(--font-weight-basis); + font-size: var(--font-size-small); + font-style: normal; + line-height: var(--line-height-basis); + color: var(--color-black-80, #333); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + /** + * Ensure consistency and use the same as HTML reset -> html {...} + * between base and code package + */ + -moz-tab-size: 4; + tab-size: 4; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + word-break: break-word; + /** + * 1. Remove repeating backgrounds in all browsers (opinionated). + * 2. Add border box sizing in all browsers (opinionated). + */ + /** + * 1. Add text decoration inheritance in all browsers (opinionated). + * 2. Add vertical alignment inheritance in all browsers (opinionated). + */ + margin: 0; + padding: 0; } + .dnb-button *, + .dnb-button ::before, + .dnb-button ::after { + background-repeat: no-repeat; + /* 1 */ + box-sizing: border-box; + /* 2 */ } + .dnb-button ::before, + .dnb-button ::after { + text-decoration: inherit; + /* 1 */ + vertical-align: inherit; + /* 2 */ } + +/* +* Button component +* +*/ +:root { + --button-font-size: var(--font-size-basis); + --button-font-size-small: var(--font-size-small); + --button-width: 2.5rem; + --button-height: 2.5rem; + --button-width--small: 1.5rem; + --button-height--small: 1.5rem; + --button-width--medium: 2rem; + --button-height--medium: 2rem; + --button-width--large: 3rem; + --button-height--large: 3rem; + --button-icon-size: 1rem; + --button-border-width: 0.0625rem; + --button-border-width--hover: 0.1875rem; + --button-border-radius: calc(var(--button-height) / 2); + --button-border-radius--small: calc(var(--button-height--small) / 2); + --button-border-radius--medium: calc(var(--button-height--medium) / 2); + --button-border-radius--large: calc(var(--button-height--large) / 2); } + +.dnb-button { + position: relative; + user-select: none; + -webkit-user-select: none; + cursor: pointer; + white-space: nowrap; + display: inline-flex; + align-items: center; + justify-content: center; + width: var(--button-width); + height: auto; + padding: 0; + border: var(--button-border-width) solid transparent; + border-radius: var(--button-border-radius); + text-decoration: none; + font-size: var(--font-size-small); + /* stylelint-disable-next-line */ } + .dnb-button--wrap { + overflow-wrap: break-word; + white-space: normal; } + .dnb-button, + .dnb-core-style .dnb-button { + line-height: var(--button-height); } + .dnb-button__text { + margin: 0.5rem 0; + font-size: var(--button-font-size); + line-height: var(--line-height-basis); + color: inherit; + transform: translateY(-0.03125rem); } + .dnb-button__text [data-os='linux'] { + transform: translateY(-0.035rem); } + .dnb-button__alignment { + display: inline-block; + width: 0; } + .dnb-button__bounding { + position: absolute; + top: 0; + bottom: 0; + right: 0; + left: 0; + transform: scale(1.1, 1.4); + background-color: transparent; + border-radius: var(--button-border-radius); } + .dnb-button--has-text { + padding-left: 1.5rem; + padding-right: 1.5rem; } + .dnb-button--size-small { + width: var(--button-width--small); + font-size: var(--button-font-size-small); + border-radius: var(--button-border-radius--small); } + .dnb-button--size-small, + .dnb-core-style .dnb-button--size-small { + line-height: var(--button-height--small); } + .dnb-button--size-small .dnb-button__text { + margin: 0; } + .dnb-button--has-text.dnb-button--size-small { + padding-left: 1rem; + padding-right: 1rem; } + .dnb-button--has-text.dnb-button--icon-position-left.dnb-button--size-small { + padding-left: 0.5rem; } + .dnb-button--has-text.dnb-button--icon-position-right.dnb-button--size-small { + padding-right: 0.5rem; } + .dnb-button--size-medium { + width: var(--button-width--medium); + border-radius: var(--button-border-radius--medium); } + .dnb-button--size-medium, + .dnb-core-style .dnb-button--size-medium { + line-height: var(--button-height--medium); } + .dnb-button--size-medium .dnb-button__text { + margin: 0; } + .dnb-button--has-text.dnb-button--size-medium { + padding-left: 1rem; + padding-right: 1rem; } + .dnb-button--has-text.dnb-button--icon-position-left.dnb-button--size-medium { + padding-left: 0.5rem; } + .dnb-button--has-text.dnb-button--icon-position-right.dnb-button--size-medium { + padding-right: 0.5rem; } + .dnb-button--size-large { + width: var(--button-width--large); + border-radius: var(--button-border-radius--large); } + .dnb-button--size-large, + .dnb-core-style .dnb-button--size-large { + line-height: var(--button-height--large); } + .dnb-button--has-text.dnb-button--size-large { + padding-left: 2rem; + padding-right: 2rem; } + .dnb-button--has-text.dnb-button--icon-position-left.dnb-button--size-large { + padding-left: 1rem; } + .dnb-button--has-text.dnb-button--icon-position-right.dnb-button--size-large { + padding-right: 1rem; } + .dnb-button--has-text { + width: auto; } + .dnb-button--has-text .dnb-button__icon { + margin: 0 calc(var(--button-icon-size) / 2); } + .dnb-button--has-text.dnb-button--icon-position-left { + padding-left: 0.5rem; } + .dnb-button--has-text.dnb-button--icon-position-right { + padding-right: 0.5rem; } + .dnb-button--has-text.dnb-button--has-icon .dnb-button__icon { + order: 2; } + .dnb-button--has-text.dnb-button--has-icon .dnb-button__text { + order: 1; } + .dnb-button:not(.dnb-button--has-text) .dnb-button__icon { + width: inherit; } + .dnb-button__icon.dnb-icon svg:not([width]):not([height]) { + width: var(--button-icon-size); + height: var(--button-icon-size); } + [href] > .dnb-button__icon.dnb-icon { + line-height: var(--button-font-size); } + .dnb-button--has-text.dnb-button--has-icon.dnb-button--icon-position-left .dnb-button__icon, .dnb-button--has-text.dnb-button--has-icon.dnb-button--icon-position-top .dnb-button__icon { + order: 1; } + .dnb-button--has-text.dnb-button--has-icon.dnb-button--icon-position-left > *, + .dnb-button--has-text.dnb-button--has-icon.dnb-button--icon-position-left .dnb-button__text, .dnb-button--has-text.dnb-button--has-icon.dnb-button--icon-position-top > *, + .dnb-button--has-text.dnb-button--has-icon.dnb-button--icon-position-top .dnb-button__text { + order: 2; } + .dnb-button--stretch { + width: 100%; } + .dnb-button--reset { + margin: 0; + padding: 0; + width: auto; + height: auto; + overflow: visible; + border: none; + border-radius: 0; + background-color: transparent; + appearance: none; + box-shadow: none; + color: inherit; + font: inherit; + text-align: inherit; + line-height: inherit; } + html:not([data-whatintent='touch']) .dnb-button--reset:hover[disabled] { + cursor: not-allowed; } + html:not([data-whatintent='touch']) .dnb-button--reset:hover:not([disabled]) { + box-shadow: none; + border: none; } + .dnb-button--reset:not([disabled]):focus, .dnb-button--reset:not([disabled]):active { + outline: none; } + html[data-whatinput='keyboard'] .dnb-button--reset:not([disabled]):focus, html[data-whatinput='keyboard'] .dnb-button--reset:not([disabled]):active { + --border-color: var(--color-emerald-green); + box-shadow: 0 0 0 0.125rem var(--border-color); + border-color: transparent; } + @media screen and (-ms-high-contrast: none) { + html[data-whatinput='keyboard'] .dnb-button--reset:not([disabled]):focus, html[data-whatinput='keyboard'] .dnb-button--reset:not([disabled]):active { + box-shadow: 0 0 0 0.125rem var(--color-emerald-green); } } + html[data-whatinput='mouse'] .dnb-button--reset:not([disabled]):focus, + html[data-whatinput='mouse'] .dnb-button--reset:not([disabled]):active { + box-shadow: none; + color: inherit; + border: none; } + .dnb-button[type='button'], .dnb-button[type='reset'], .dnb-button[type='submit'] { + appearance: none; + -moz-appearance: none; + -webkit-appearance: none; } + .dnb-button[disabled] { + cursor: not-allowed; + outline: none; } + .dnb-form-row--vertical .dnb-form-row__content > .dnb-button { + align-self: flex-start; } + .dnb-form-row--horizontal .dnb-form-row__content .dnb-button__text { + white-space: nowrap; } + .dnb-button + .dnb-form-status { + margin-top: 0.5rem; } + @media screen and (-ms-high-contrast: none) { + .dnb-button { + flex: none; } + .dnb-button__icon, .dnb-button__text { + transform: translateY(-0.0625rem); } } + +/* Firefox includes a hidden border which messes up button dimensions */ +button.dnb-button::-moz-focus-inner { + border: none; } + +.dnb-upload { + font-family: var(--font-family-default); + font-weight: var(--font-weight-basis); + font-size: var(--font-size-small); + font-style: normal; + line-height: var(--line-height-basis); + color: var(--color-black-80, #333); + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + /** + * Ensure consistency and use the same as HTML reset -> html {...} + * between base and code package + */ + -moz-tab-size: 4; + tab-size: 4; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + word-break: break-word; + /** + * 1. Remove repeating backgrounds in all browsers (opinionated). + * 2. Add border box sizing in all browsers (opinionated). + */ + /** + * 1. Add text decoration inheritance in all browsers (opinionated). + * 2. Add vertical alignment inheritance in all browsers (opinionated). + */ + margin: 0; + padding: 0; } + .dnb-upload *, + .dnb-upload ::before, + .dnb-upload ::after { + background-repeat: no-repeat; + /* 1 */ + box-sizing: border-box; + /* 2 */ } + .dnb-upload ::before, + .dnb-upload ::after { + text-decoration: inherit; + /* 1 */ + vertical-align: inherit; + /* 2 */ } + +/* +* Upload component +* +*/ +.dnb-upload { + display: flex; + flex-direction: column; + position: relative; + padding: var(--spacing-medium); + background-color: var(--color-white); } + .dnb-upload__outline { + content: ''; + pointer-events: none; + position: absolute; + inset: 0; + height: 100%; + width: 100%; + stroke: var(--color-sea-green); + border-radius: 0.25rem; } + .dnb-upload__condition-list__label { + font-weight: var(--font-weight-medium); + margin-right: var(--spacing-x-small); } + .dnb-upload__text.dnb-p { + color: var(--color-black-55); } + .dnb-upload__file-input { + position: absolute; + visibility: hidden; } + .dnb-upload__file-list { + position: relative; + padding: 0; + margin-top: var(--spacing-medium); + margin-bottom: 0; + list-style: none; } + .dnb-upload__file-list::before, .dnb-upload__file-cell::after { + content: ''; + position: absolute; + inset: 0; + height: 1px; + background-color: var(--color-black-8); } + .dnb-upload__file-cell { + position: relative; + padding: var(--spacing-small) 0; } + .dnb-upload__file-cell::after { + top: auto; } + .dnb-upload__file-cell__content { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; } + .dnb-upload__file-cell__content__left { + display: flex; + flex-direction: row; + align-items: center; } + .dnb-upload__file-cell--warning .dnb-upload__file-cell__content__left .dnb-icon { + color: var(--color-fire-red); } + .dnb-upload__file-cell__text-container { + display: flex; + flex-direction: column; + margin-left: var(--spacing-small); } + .dnb-upload__file-cell__text-container--loading { + font-size: var(--font-size-basis); } + .dnb-upload__file-cell__subtitle.dnb-p { + color: var(--color-black-55); } +" +`; diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-file-list-1-c85d6.snap.png b/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-file-list-1-c85d6.snap.png new file mode 100644 index 0000000000000000000000000000000000000000..6d36216ae88fb9c1778a3148b735189a1514fdfe GIT binary patch literal 33711 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 VPW#Twi39&0Kai9BdjHj%{{v}+q_O}2 literal 0 HcmV?d00001 diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-the-custom-accepted-formats-1-b65b2.snap.png b/packages/dnb-eufemia/src/components/upload/__tests__/__snapshots__/upload-screenshot-test-js-upload-screenshot-have-to-match-the-custom-accepted-formats-1-b65b2.snap.png new file mode 100644 index 0000000000000000000000000000000000000000..20ac300b211cab625c595277b795c44a17e90ed9 GIT binary patch literal 24691 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 V?>f9RNG8>|w-Y!M&_BJ|=;wF6`FB8xC=2M; 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 Vrc%}#*tnBSURqfy^R}Vy{{UG>Z$SV6 literal 0 HcmV?d00001 diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/testHelpers.ts b/packages/dnb-eufemia/src/components/upload/__tests__/testHelpers.ts new file mode 100644 index 00000000000..6210e3d8a5f --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/testHelpers.ts @@ -0,0 +1,13 @@ +export default function createMockFile( + name: string, + size: number, + type: string +) { + const file = new File([], name, { type }) + Object.defineProperty(file, 'size', { + get() { + return size + }, + }) + return file +} diff --git a/packages/dnb-eufemia/src/components/upload/__tests__/useUpload.test.tsx b/packages/dnb-eufemia/src/components/upload/__tests__/useUpload.test.tsx new file mode 100644 index 00000000000..3719a899a51 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/__tests__/useUpload.test.tsx @@ -0,0 +1,116 @@ +import { act, render } from '@testing-library/react' +import useUpload from './../useUpload' +import React, { useEffect } from 'react' +import createMockFile from './testHelpers' +import EventEmitter from '../../../../src/shared/helpers/EventEmitter' + +describe('useUpload', () => { + afterEach(() => { + jest.clearAllMocks() + }) + + it('return an empty list', () => { + const validationFunction = jest.fn() + + const MockComponents = () => { + const { files } = useUpload('id') + + validationFunction(files) + + return
    + } + + render() + + expect(validationFunction).toHaveBeenCalledWith([]) + }) + + it('return the updateFiles function', () => { + const validationFunction = jest.fn() + + const MockComponents = () => { + const { setFiles } = useUpload('id') + + validationFunction(setFiles) + + return
    + } + + render() + + expect(validationFunction).toHaveBeenCalledWith(expect.any(Function)) + }) + + it('return the added files', () => { + const validationFunction = jest.fn() + + const mockFile = createMockFile('fileName.png', 100, 'image/png') + + const MockComponents = () => { + const { setFiles, files } = useUpload('id') + + useEffect(() => { + setFiles([{ file: mockFile }]) + }, []) + + validationFunction(files) + return
    + } + + render() + act(() => { + expect(validationFunction).toHaveBeenCalledWith([{ file: mockFile }]) + }) + }) + + it('use the event emitter to store a file', () => { + const mockFile = { + file: createMockFile('fileName.png', 100, 'image/png'), + } + const id = 'unique-id-1' + + const MockComponents = () => { + const { setFiles } = useUpload(id) + + useEffect(() => setFiles([mockFile]), []) + + return
    + } + + render() + + const emitter = EventEmitter.createInstance(id) + const emitterFiles = emitter.get().files + + expect(emitterFiles).toMatchObject([mockFile]) + }) + + it('use the event emitter to return a file', () => { + const mockFile = { + file: createMockFile('fileName.png', 100, 'image/png'), + } + const id = 'unique-id-2' + + const validationFunction = jest.fn() + + const MockComponents = () => { + const { files } = useUpload(id) + + useEffect(() => { + validationFunction(files) + }, []) + + return
    + } + + const emitter = EventEmitter.createInstance(id) + const emitterData = { files: [mockFile] } + emitter.update(emitterData) + + render() + + act(() => { + expect(validationFunction).toHaveBeenCalledWith([mockFile]) + }) + }) +}) diff --git a/packages/dnb-eufemia/src/components/upload/index.js b/packages/dnb-eufemia/src/components/upload/index.js new file mode 100644 index 00000000000..67067079c7c --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/index.js @@ -0,0 +1,8 @@ +/** + * Component Entry + * + */ + +import Upload from './Upload' +export default Upload +export * from './Upload' diff --git a/packages/dnb-eufemia/src/components/upload/stories/Upload.stories.tsx b/packages/dnb-eufemia/src/components/upload/stories/Upload.stories.tsx new file mode 100644 index 00000000000..b8c14b7fd5a --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/stories/Upload.stories.tsx @@ -0,0 +1,73 @@ +/** + * @dnb/eufemia Component Story + * + */ + +import React, { useEffect } from 'react' +import { Wrapper, Box } from 'storybook-utils/helpers' +import { Upload } from '../..' + +export default { + title: 'Eufemia/Components/Upload', +} + +export const UploadSandbox = () => { + const { files: files1 } = Upload.useUpload('upload-example-1') + Upload.useUpload('upload-example-6') + + useEffect(() => { + console.log(files1) + }, [files1]) + + return ( + + + + + + + + + + + + + + + + + + Two Upload components can be controlled using the same id + + + + + ) +} diff --git a/packages/dnb-eufemia/src/components/upload/style.js b/packages/dnb-eufemia/src/components/upload/style.js new file mode 100644 index 00000000000..a767e2f50a8 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/style.js @@ -0,0 +1,6 @@ +/** + * Web Style Import + * + */ + +import './style/dnb-upload.scss' diff --git a/packages/dnb-eufemia/src/components/upload/style/_upload.scss b/packages/dnb-eufemia/src/components/upload/style/_upload.scss new file mode 100644 index 00000000000..438ffbd4fe3 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/style/_upload.scss @@ -0,0 +1,109 @@ +/* +* Upload component +* +*/ + +.dnb-upload { + display: flex; + flex-direction: column; + + position: relative; + padding: var(--spacing-medium); + + background-color: var(--color-white); + + // svg element + &__outline { + content: ''; + + pointer-events: none; + + position: absolute; + inset: 0; + + height: 100%; + width: 100%; + + stroke: var(--color-sea-green); + border-radius: 0.25rem; + } + + &__condition-list { + &__label { + font-weight: var(--font-weight-medium); + margin-right: var(--spacing-x-small); + } + } + + &__text.dnb-p { + color: var(--color-black-55); + } + + &__file-input { + position: absolute; + visibility: hidden; + } + + &__file-list { + position: relative; + + padding: 0; + margin-top: var(--spacing-medium); + margin-bottom: 0; + + list-style: none; + } + + // Border on top/bottom of list items + &__file-list::before, + &__file-cell::after { + content: ''; + position: absolute; + inset: 0; + height: 1px; + background-color: var(--color-black-8); + } + + &__file-cell { + position: relative; + padding: var(--spacing-small) 0; + + // Align border to bottom + &::after { + top: auto; + } + + &__content { + display: flex; + flex-direction: row; + + justify-content: space-between; + align-items: center; + + &__left { + display: flex; + flex-direction: row; + align-items: center; + } + } + + &--warning &__content__left .dnb-icon { + color: var(--color-fire-red); + } + + &__text-container { + display: flex; + flex-direction: column; + + margin-left: var(--spacing-small); + + &--loading { + font-size: var(--font-size-basis); + } + } + + &__subtitle.dnb-p { + color: var(--color-black-55); + } + } +} diff --git a/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss b/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss new file mode 100644 index 00000000000..2803ec38d51 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/style/dnb-upload.scss @@ -0,0 +1,16 @@ +/* +* DNB Upload +* +*/ + +// import dependencies +@import '../../../style/components/imports.scss'; +@import '../../icon/style/dnb-icon.scss'; +@import '../../button/style/dnb-button.scss'; +@import '../../form-status/style/dnb-form-status.scss'; + +.dnb-upload { + @include componentReset(); +} + +@import './_upload.scss'; diff --git a/packages/dnb-eufemia/src/components/upload/style/index.js b/packages/dnb-eufemia/src/components/upload/style/index.js new file mode 100644 index 00000000000..4ef8acea2ce --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/style/index.js @@ -0,0 +1,6 @@ +/** + * Web Style Import + * + */ + +import './dnb-upload.scss' diff --git a/packages/dnb-eufemia/src/components/upload/types.ts b/packages/dnb-eufemia/src/components/upload/types.ts new file mode 100644 index 00000000000..18bd7a38bf0 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/types.ts @@ -0,0 +1,56 @@ +import React from 'react' +import { SkeletonShow } from '../skeleton/Skeleton' + +export type UploadProps = { + /** + * unique id used with the useUpload hook to manage the files + */ + id: string + + /** + * list of accepted file types. + */ + acceptedFileTypes: string[] + + /** + * Custom className on the component root + * Default: null + */ + className?: string + + /** + * Skeleton should be applied when loading content + * Default: null + */ + skeleton?: SkeletonShow + + /** + * If set true, accepting multiple files is allowed + */ + multipleFiles?: boolean + + /** + * fileMaxSize is max size of each file in MB + */ + fileMaxSize?: number + + /** + * Custom text properties + */ + title?: React.ReactNode + text?: React.ReactNode + formatsDescription?: React.ReactNode + fileSizeDescription?: React.ReactNode + fileSizeContent?: React.ReactNode + uploadButtonText?: React.ReactNode + uploadErrorLargeFile?: React.ReactNode + uploadLoadingText?: React.ReactNode + deleteButton?: React.ReactNode + fileListAriaLabel?: string +} + +export type UploadFile = { + file: File + errorMessage?: React.ReactNode + isLoading?: boolean +} diff --git a/packages/dnb-eufemia/src/components/upload/useUpload.ts b/packages/dnb-eufemia/src/components/upload/useUpload.ts new file mode 100644 index 00000000000..7b9ad643cf2 --- /dev/null +++ b/packages/dnb-eufemia/src/components/upload/useUpload.ts @@ -0,0 +1,25 @@ +import { useEventEmitter } from '../../../src/shared/component-helper' +import type { UploadFile } from './types' + +/** + * Use together with Upload with the same id to manage the files from outside the component. + * @param id string, must match the id of the Upload component + * @returns { files: UploadFile[], setFiles: (file: UploadFile[]) => void } + */ +function useUpload(id: string): { + files: UploadFile[] + setFiles: (files: UploadFile[]) => void +} { + const { data, update } = useEventEmitter(id) + + function setFiles(files: UploadFile[]) { + update({ files }) + } + + return { + files: data?.files || [], + setFiles, + } +} + +export default useUpload diff --git a/packages/dnb-eufemia/src/index.js b/packages/dnb-eufemia/src/index.js index a9c8a5eddcd..6c912c8414c 100644 --- a/packages/dnb-eufemia/src/index.js +++ b/packages/dnb-eufemia/src/index.js @@ -82,6 +82,7 @@ import Textarea from './components/textarea/Textarea' import Timeline from './components/timeline/Timeline' import ToggleButton from './components/toggle-button/ToggleButton' import Tooltip from './components/tooltip/Tooltip' +import Upload from './components/upload/Upload' import VisuallyHidden from './components/visually-hidden/VisuallyHidden' // define / export all the available components @@ -158,6 +159,7 @@ export { Timeline, ToggleButton, Tooltip, + Upload, VisuallyHidden, } diff --git a/packages/dnb-eufemia/src/shared/Context.tsx b/packages/dnb-eufemia/src/shared/Context.tsx index 0baed64d747..adab4fa7437 100644 --- a/packages/dnb-eufemia/src/shared/Context.tsx +++ b/packages/dnb-eufemia/src/shared/Context.tsx @@ -26,6 +26,7 @@ import type { DrawerProps } from '../components/drawer/types' import type { DialogProps } from '../components/dialog/types' import type { TooltipProps } from '../components/tooltip/types' import type { SectionProps } from '../components/section/Section' +import { UploadProps } from '../components/upload/types' // All TypeScript based Eufemia elements import type { AnchorProps } from '../elements/Anchor' @@ -53,6 +54,7 @@ export type ContextProps = { Tooltip?: Partial Section?: Partial ScrollView?: Partial + Upload?: Partial // -- TODO: Not converted yet -- diff --git a/packages/dnb-eufemia/src/shared/locales/en-GB.js b/packages/dnb-eufemia/src/shared/locales/en-GB.js index d1710e5137c..06857e8d569 100644 --- a/packages/dnb-eufemia/src/shared/locales/en-GB.js +++ b/packages/dnb-eufemia/src/shared/locales/en-GB.js @@ -122,5 +122,18 @@ export default { Tag: { removeIconTitle: 'Remove', }, + Upload: { + title: 'Upload documents', + text: 'Drag & drop your files or choose files to upload.', + formatsDescription: 'Allowed formats:', + fileSizeDescription: 'Max. filesize:', + fileSizeContent: '%size MB', + uploadButtonText: 'Browse files', + uploadLoadingText: 'Uploading', + uploadErrorLargeFile: + 'The file you are trying to upload is too big, the maximum size supported is %size MB. Please try again.', + deleteButton: 'Delete', + fileListAriaLabel: 'uploaded files', + }, }, } diff --git a/packages/dnb-eufemia/src/shared/locales/nb-NO.js b/packages/dnb-eufemia/src/shared/locales/nb-NO.js index 0f0ff46d371..47e6c6c5ea2 100644 --- a/packages/dnb-eufemia/src/shared/locales/nb-NO.js +++ b/packages/dnb-eufemia/src/shared/locales/nb-NO.js @@ -122,5 +122,18 @@ export default { Tag: { removeIconTitle: 'Fjern', }, + Upload: { + title: 'Last opp dokumenter', + text: 'Dra & slipp eller velg hvilke filer du vil laste opp.', + formatsDescription: 'Tilatte formater:', + fileSizeDescription: 'Maks filstørrelse:', + fileSizeContent: '%size MB', + uploadButtonText: 'Utforsk filer', + uploadLoadingText: 'Laster opp', + uploadErrorLargeFile: + 'Filen du prøver å laste opp er for stor, vi støtter ikke filer større enn %size MB. Vennligst prøv igjen.', + deleteButton: 'Slett', + fileListAriaLabel: 'opplastede filer', + }, }, } diff --git a/packages/dnb-eufemia/src/style/dnb-ui-components.scss b/packages/dnb-eufemia/src/style/dnb-ui-components.scss index 6ca4f51e95d..faf8f352a94 100644 --- a/packages/dnb-eufemia/src/style/dnb-ui-components.scss +++ b/packages/dnb-eufemia/src/style/dnb-ui-components.scss @@ -49,4 +49,5 @@ @import '../components/timeline/style/_timeline.scss'; @import '../components/toggle-button/style/_toggle-button.scss'; @import '../components/tooltip/style/_tooltip.scss'; +@import '../components/upload/style/_upload.scss'; @import '../components/visually-hidden/style/_visually-hidden.scss'; diff --git a/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap b/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap index 8c116e51171..5f8c9e1ac25 100644 --- a/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap +++ b/packages/dnb-eufemia/src/style/elements/__tests__/__snapshots__/Elements.test.js.snap @@ -711,11 +711,14 @@ a.dnb-button { content: none; } .dnb-dl { - margin: 0; padding: 0; font-size: var(--font-size-basis); line-height: var(--line-height-basis); color: var(--theme-color-black-80, currentColor); } + .dnb-dl:not([class*='dnb-space__top']) { + margin-top: 0; } + .dnb-dl:not([class*='dnb-space__bottom']) { + margin-bottom: 0; } .dnb-dl dt { margin-top: 1rem; padding: 0; diff --git a/packages/dnb-eufemia/src/style/elements/lists.scss b/packages/dnb-eufemia/src/style/elements/lists.scss index 27636075a7f..23f87708800 100644 --- a/packages/dnb-eufemia/src/style/elements/lists.scss +++ b/packages/dnb-eufemia/src/style/elements/lists.scss @@ -105,7 +105,12 @@ } } @mixin dlStyle() { - margin: 0; + &:not([class*='dnb-space__top']) { + margin-top: 0; + } + &:not([class*='dnb-space__bottom']) { + margin-bottom: 0; + } padding: 0; font-size: var(--font-size-basis);