@navikt/ds-react 6.2.0 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/form/combobox/ComboboxProvider.js +5 -1
- package/cjs/form/combobox/ComboboxProvider.js.map +1 -1
- package/cjs/form/combobox/FilteredOptions/FilteredOptions.js +14 -12
- package/cjs/form/combobox/FilteredOptions/FilteredOptions.js.map +1 -1
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.d.ts +3 -3
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.js +1 -3
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.js.map +1 -1
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.d.ts +8 -5
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +8 -13
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/cjs/form/combobox/Input/Input.js +9 -7
- package/cjs/form/combobox/Input/Input.js.map +1 -1
- package/cjs/form/combobox/SelectedOptions/SelectedOptions.d.ts +2 -1
- package/cjs/form/combobox/SelectedOptions/SelectedOptions.js +3 -3
- package/cjs/form/combobox/SelectedOptions/SelectedOptions.js.map +1 -1
- package/cjs/form/combobox/SelectedOptions/selectedOptionsContext.d.ts +10 -7
- package/cjs/form/combobox/SelectedOptions/selectedOptionsContext.js +6 -8
- package/cjs/form/combobox/SelectedOptions/selectedOptionsContext.js.map +1 -1
- package/cjs/form/combobox/combobox-utils.d.ts +10 -0
- package/cjs/form/combobox/combobox-utils.js +27 -0
- package/cjs/form/combobox/combobox-utils.js.map +1 -0
- package/cjs/form/combobox/customOptionsContext.d.ts +5 -4
- package/cjs/form/combobox/customOptionsContext.js +1 -1
- package/cjs/form/combobox/customOptionsContext.js.map +1 -1
- package/cjs/form/combobox/types.d.ts +22 -11
- package/cjs/form/file-upload/FileUpload.context.d.ts +8 -0
- package/cjs/form/file-upload/FileUpload.context.js +7 -0
- package/cjs/form/file-upload/FileUpload.context.js.map +1 -0
- package/cjs/form/file-upload/FileUpload.d.ts +118 -0
- package/cjs/form/file-upload/FileUpload.js +73 -0
- package/cjs/form/file-upload/FileUpload.js.map +1 -0
- package/cjs/form/file-upload/FileUpload.types.d.ts +55 -0
- package/cjs/form/file-upload/FileUpload.types.js +8 -0
- package/cjs/form/file-upload/FileUpload.types.js.map +1 -0
- package/cjs/form/file-upload/i18n/get.d.ts +2 -0
- package/cjs/form/file-upload/i18n/get.js +38 -0
- package/cjs/form/file-upload/i18n/get.js.map +1 -0
- package/cjs/form/file-upload/i18n/i18n.context.d.ts +11 -0
- package/cjs/form/file-upload/i18n/i18n.context.js +39 -0
- package/cjs/form/file-upload/i18n/i18n.context.js.map +1 -0
- package/cjs/form/file-upload/i18n/i18n.types.d.ts +13 -0
- package/cjs/form/file-upload/i18n/i18n.types.js +3 -0
- package/cjs/form/file-upload/i18n/i18n.types.js.map +1 -0
- package/cjs/form/file-upload/i18n/locales/nb.json +20 -0
- package/cjs/form/file-upload/i18n/merge.d.ts +2 -0
- package/cjs/form/file-upload/i18n/merge.js +29 -0
- package/cjs/form/file-upload/i18n/merge.js.map +1 -0
- package/cjs/form/file-upload/index.d.ts +7 -0
- package/cjs/form/file-upload/index.js +16 -0
- package/cjs/form/file-upload/index.js.map +1 -0
- package/cjs/form/file-upload/parts/Trigger.d.ts +7 -0
- package/cjs/form/file-upload/parts/Trigger.js +43 -0
- package/cjs/form/file-upload/parts/Trigger.js.map +1 -0
- package/cjs/form/file-upload/parts/dropzone/Dropzone.d.ts +4 -0
- package/cjs/form/file-upload/parts/dropzone/Dropzone.js +106 -0
- package/cjs/form/file-upload/parts/dropzone/Dropzone.js.map +1 -0
- package/cjs/form/file-upload/parts/dropzone/dropzone.types.d.ts +18 -0
- package/cjs/form/file-upload/parts/dropzone/dropzone.types.js +3 -0
- package/cjs/form/file-upload/parts/dropzone/dropzone.types.js.map +1 -0
- package/cjs/form/file-upload/parts/dropzone/useDropzone.d.ts +13 -0
- package/cjs/form/file-upload/parts/dropzone/useDropzone.js +34 -0
- package/cjs/form/file-upload/parts/dropzone/useDropzone.js.map +1 -0
- package/cjs/form/file-upload/parts/item/Item.d.ts +55 -0
- package/cjs/form/file-upload/parts/item/Item.js +79 -0
- package/cjs/form/file-upload/parts/item/Item.js.map +1 -0
- package/cjs/form/file-upload/parts/item/Item.types.d.ts +5 -0
- package/cjs/form/file-upload/parts/item/Item.types.js +3 -0
- package/cjs/form/file-upload/parts/item/Item.types.js.map +1 -0
- package/cjs/form/file-upload/parts/item/ItemButton.d.ts +12 -0
- package/cjs/form/file-upload/parts/item/ItemButton.js +22 -0
- package/cjs/form/file-upload/parts/item/ItemButton.js.map +1 -0
- package/cjs/form/file-upload/parts/item/ItemIcon.d.ts +9 -0
- package/cjs/form/file-upload/parts/item/ItemIcon.js +51 -0
- package/cjs/form/file-upload/parts/item/ItemIcon.js.map +1 -0
- package/cjs/form/file-upload/parts/item/ItemName.d.ts +9 -0
- package/cjs/form/file-upload/parts/item/ItemName.js +32 -0
- package/cjs/form/file-upload/parts/item/ItemName.js.map +1 -0
- package/cjs/form/file-upload/parts/item/utils/download-file.d.ts +1 -0
- package/cjs/form/file-upload/parts/item/utils/download-file.js +13 -0
- package/cjs/form/file-upload/parts/item/utils/download-file.js.map +1 -0
- package/cjs/form/file-upload/parts/item/utils/file-type-checker.d.ts +2 -0
- package/cjs/form/file-upload/parts/item/utils/file-type-checker.js +6 -0
- package/cjs/form/file-upload/parts/item/utils/file-type-checker.js.map +1 -0
- package/cjs/form/file-upload/parts/item/utils/format-file-size.d.ts +2 -0
- package/cjs/form/file-upload/parts/item/utils/format-file-size.js +24 -0
- package/cjs/form/file-upload/parts/item/utils/format-file-size.js.map +1 -0
- package/cjs/form/file-upload/useFileUpload.d.ts +12 -0
- package/cjs/form/file-upload/useFileUpload.js +33 -0
- package/cjs/form/file-upload/useFileUpload.js.map +1 -0
- package/cjs/form/file-upload/utils/is-accepted-file-type.d.ts +1 -0
- package/cjs/form/file-upload/utils/is-accepted-file-type.js +26 -0
- package/cjs/form/file-upload/utils/is-accepted-file-type.js.map +1 -0
- package/cjs/form/file-upload/utils/is-accepted-size.d.ts +1 -0
- package/cjs/form/file-upload/utils/is-accepted-size.js +11 -0
- package/cjs/form/file-upload/utils/is-accepted-size.js.map +1 -0
- package/cjs/form/file-upload/utils/validate-files.d.ts +8 -0
- package/cjs/form/file-upload/utils/validate-files.js +48 -0
- package/cjs/form/file-upload/utils/validate-files.js.map +1 -0
- package/cjs/index.d.ts +1 -0
- package/cjs/index.js +3 -1
- package/cjs/index.js.map +1 -1
- package/cjs/loader/Loader.d.ts +0 -7
- package/cjs/loader/Loader.js.map +1 -1
- package/cjs/table/DataCell.d.ts +1 -3
- package/cjs/table/DataCell.js.map +1 -1
- package/cjs/util/create-context.d.ts +1 -0
- package/cjs/util/create-context.js.map +1 -1
- package/cjs/util/hooks/useEventListener.d.ts +1 -1
- package/cjs/util/hooks/useMergeRefs.d.ts +1 -1
- package/esm/form/combobox/ComboboxProvider.js +5 -1
- package/esm/form/combobox/ComboboxProvider.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/FilteredOptions.js +14 -12
- package/esm/form/combobox/FilteredOptions/FilteredOptions.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filtered-options-util.d.ts +3 -3
- package/esm/form/combobox/FilteredOptions/filtered-options-util.js +1 -3
- package/esm/form/combobox/FilteredOptions/filtered-options-util.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.d.ts +8 -5
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +8 -13
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/esm/form/combobox/Input/Input.js +9 -7
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/form/combobox/SelectedOptions/SelectedOptions.d.ts +2 -1
- package/esm/form/combobox/SelectedOptions/SelectedOptions.js +3 -3
- package/esm/form/combobox/SelectedOptions/SelectedOptions.js.map +1 -1
- package/esm/form/combobox/SelectedOptions/selectedOptionsContext.d.ts +10 -7
- package/esm/form/combobox/SelectedOptions/selectedOptionsContext.js +6 -8
- package/esm/form/combobox/SelectedOptions/selectedOptionsContext.js.map +1 -1
- package/esm/form/combobox/combobox-utils.d.ts +10 -0
- package/esm/form/combobox/combobox-utils.js +22 -0
- package/esm/form/combobox/combobox-utils.js.map +1 -0
- package/esm/form/combobox/customOptionsContext.d.ts +5 -4
- package/esm/form/combobox/customOptionsContext.js +1 -1
- package/esm/form/combobox/customOptionsContext.js.map +1 -1
- package/esm/form/combobox/types.d.ts +22 -11
- package/esm/form/file-upload/FileUpload.context.d.ts +8 -0
- package/esm/form/file-upload/FileUpload.context.js +3 -0
- package/esm/form/file-upload/FileUpload.context.js.map +1 -0
- package/esm/form/file-upload/FileUpload.d.ts +118 -0
- package/esm/form/file-upload/FileUpload.js +44 -0
- package/esm/form/file-upload/FileUpload.js.map +1 -0
- package/esm/form/file-upload/FileUpload.types.d.ts +55 -0
- package/esm/form/file-upload/FileUpload.types.js +5 -0
- package/esm/form/file-upload/FileUpload.types.js.map +1 -0
- package/esm/form/file-upload/i18n/get.d.ts +2 -0
- package/esm/form/file-upload/i18n/get.js +34 -0
- package/esm/form/file-upload/i18n/get.js.map +1 -0
- package/esm/form/file-upload/i18n/i18n.context.d.ts +11 -0
- package/esm/form/file-upload/i18n/i18n.context.js +32 -0
- package/esm/form/file-upload/i18n/i18n.context.js.map +1 -0
- package/esm/form/file-upload/i18n/i18n.types.d.ts +13 -0
- package/esm/form/file-upload/i18n/i18n.types.js +2 -0
- package/esm/form/file-upload/i18n/i18n.types.js.map +1 -0
- package/esm/form/file-upload/i18n/locales/nb.json +20 -0
- package/esm/form/file-upload/i18n/merge.d.ts +2 -0
- package/esm/form/file-upload/i18n/merge.js +25 -0
- package/esm/form/file-upload/i18n/merge.js.map +1 -0
- package/esm/form/file-upload/index.d.ts +7 -0
- package/esm/form/file-upload/index.js +6 -0
- package/esm/form/file-upload/index.js.map +1 -0
- package/esm/form/file-upload/parts/Trigger.d.ts +7 -0
- package/esm/form/file-upload/parts/Trigger.js +18 -0
- package/esm/form/file-upload/parts/Trigger.js.map +1 -0
- package/esm/form/file-upload/parts/dropzone/Dropzone.d.ts +4 -0
- package/esm/form/file-upload/parts/dropzone/Dropzone.js +78 -0
- package/esm/form/file-upload/parts/dropzone/Dropzone.js.map +1 -0
- package/esm/form/file-upload/parts/dropzone/dropzone.types.d.ts +18 -0
- package/esm/form/file-upload/parts/dropzone/dropzone.types.js +2 -0
- package/esm/form/file-upload/parts/dropzone/dropzone.types.js.map +1 -0
- package/esm/form/file-upload/parts/dropzone/useDropzone.d.ts +13 -0
- package/esm/form/file-upload/parts/dropzone/useDropzone.js +30 -0
- package/esm/form/file-upload/parts/dropzone/useDropzone.js.map +1 -0
- package/esm/form/file-upload/parts/item/Item.d.ts +55 -0
- package/esm/form/file-upload/parts/item/Item.js +50 -0
- package/esm/form/file-upload/parts/item/Item.js.map +1 -0
- package/esm/form/file-upload/parts/item/Item.types.d.ts +5 -0
- package/esm/form/file-upload/parts/item/Item.types.js +2 -0
- package/esm/form/file-upload/parts/item/Item.types.js.map +1 -0
- package/esm/form/file-upload/parts/item/ItemButton.d.ts +12 -0
- package/esm/form/file-upload/parts/item/ItemButton.js +17 -0
- package/esm/form/file-upload/parts/item/ItemButton.js.map +1 -0
- package/esm/form/file-upload/parts/item/ItemIcon.d.ts +9 -0
- package/esm/form/file-upload/parts/item/ItemIcon.js +46 -0
- package/esm/form/file-upload/parts/item/ItemIcon.js.map +1 -0
- package/esm/form/file-upload/parts/item/ItemName.d.ts +9 -0
- package/esm/form/file-upload/parts/item/ItemName.js +27 -0
- package/esm/form/file-upload/parts/item/ItemName.js.map +1 -0
- package/esm/form/file-upload/parts/item/utils/download-file.d.ts +1 -0
- package/esm/form/file-upload/parts/item/utils/download-file.js +9 -0
- package/esm/form/file-upload/parts/item/utils/download-file.js.map +1 -0
- package/esm/form/file-upload/parts/item/utils/file-type-checker.d.ts +2 -0
- package/esm/form/file-upload/parts/item/utils/file-type-checker.js +2 -0
- package/esm/form/file-upload/parts/item/utils/file-type-checker.js.map +1 -0
- package/esm/form/file-upload/parts/item/utils/format-file-size.d.ts +2 -0
- package/esm/form/file-upload/parts/item/utils/format-file-size.js +20 -0
- package/esm/form/file-upload/parts/item/utils/format-file-size.js.map +1 -0
- package/esm/form/file-upload/useFileUpload.d.ts +12 -0
- package/esm/form/file-upload/useFileUpload.js +29 -0
- package/esm/form/file-upload/useFileUpload.js.map +1 -0
- package/esm/form/file-upload/utils/is-accepted-file-type.d.ts +1 -0
- package/esm/form/file-upload/utils/is-accepted-file-type.js +22 -0
- package/esm/form/file-upload/utils/is-accepted-file-type.js.map +1 -0
- package/esm/form/file-upload/utils/is-accepted-size.d.ts +1 -0
- package/esm/form/file-upload/utils/is-accepted-size.js +7 -0
- package/esm/form/file-upload/utils/is-accepted-size.js.map +1 -0
- package/esm/form/file-upload/utils/validate-files.d.ts +8 -0
- package/esm/form/file-upload/utils/validate-files.js +44 -0
- package/esm/form/file-upload/utils/validate-files.js.map +1 -0
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/loader/Loader.d.ts +0 -7
- package/esm/loader/Loader.js.map +1 -1
- package/esm/table/DataCell.d.ts +1 -3
- package/esm/table/DataCell.js.map +1 -1
- package/esm/util/create-context.d.ts +1 -0
- package/esm/util/create-context.js.map +1 -1
- package/esm/util/hooks/useEventListener.d.ts +1 -1
- package/esm/util/hooks/useMergeRefs.d.ts +1 -1
- package/package.json +13 -3
- package/src/form/combobox/ComboboxProvider.tsx +7 -3
- package/src/form/combobox/FilteredOptions/FilteredOptions.tsx +22 -15
- package/src/form/combobox/FilteredOptions/filtered-options-util.ts +5 -10
- package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +19 -29
- package/src/form/combobox/Input/Input.tsx +14 -8
- package/src/form/combobox/SelectedOptions/SelectedOptions.tsx +8 -5
- package/src/form/combobox/SelectedOptions/selectedOptionsContext.tsx +24 -25
- package/src/form/combobox/combobox-utils.test.ts +67 -0
- package/src/form/combobox/combobox-utils.ts +32 -0
- package/src/form/combobox/combobox.stories.tsx +67 -32
- package/src/form/combobox/combobox.test.tsx +32 -1
- package/src/form/combobox/customOptionsContext.tsx +9 -8
- package/src/form/combobox/types.ts +23 -11
- package/src/form/file-upload/FileUpload.context.tsx +9 -0
- package/src/form/file-upload/FileUpload.tsx +142 -0
- package/src/form/file-upload/FileUpload.types.ts +57 -0
- package/src/form/file-upload/file-upload-dropzone.stories.tsx +123 -0
- package/src/form/file-upload/file-upload-item.stories.tsx +136 -0
- package/src/form/file-upload/file-upload.stories.tsx +236 -0
- package/src/form/file-upload/i18n/get.ts +48 -0
- package/src/form/file-upload/i18n/i18n.context.test.tsx +92 -0
- package/src/form/file-upload/i18n/i18n.context.ts +67 -0
- package/src/form/file-upload/i18n/i18n.types.ts +20 -0
- package/src/form/file-upload/i18n/locales/nb.json +20 -0
- package/src/form/file-upload/i18n/merge.ts +35 -0
- package/src/form/file-upload/index.ts +21 -0
- package/src/form/file-upload/parts/Trigger.tsx +48 -0
- package/src/form/file-upload/parts/dropzone/Dropzone.tsx +181 -0
- package/src/form/file-upload/parts/dropzone/dropzone.types.ts +22 -0
- package/src/form/file-upload/parts/dropzone/useDropzone.ts +43 -0
- package/src/form/file-upload/parts/item/Item.tsx +165 -0
- package/src/form/file-upload/parts/item/Item.types.ts +6 -0
- package/src/form/file-upload/parts/item/ItemButton.tsx +52 -0
- package/src/form/file-upload/parts/item/ItemIcon.tsx +74 -0
- package/src/form/file-upload/parts/item/ItemName.tsx +58 -0
- package/src/form/file-upload/parts/item/utils/download-file.ts +9 -0
- package/src/form/file-upload/parts/item/utils/file-type-checker.ts +4 -0
- package/src/form/file-upload/parts/item/utils/format-file-size.test.ts +76 -0
- package/src/form/file-upload/parts/item/utils/format-file-size.ts +25 -0
- package/src/form/file-upload/useFileUpload.ts +54 -0
- package/src/form/file-upload/utils/is-accepted-file-type.test.ts +69 -0
- package/src/form/file-upload/utils/is-accepted-file-type.ts +25 -0
- package/src/form/file-upload/utils/is-accepted-size.test.ts +26 -0
- package/src/form/file-upload/utils/is-accepted-size.ts +7 -0
- package/src/form/file-upload/utils/validate-files.test.ts +132 -0
- package/src/form/file-upload/utils/validate-files.ts +62 -0
- package/src/index.ts +14 -0
- package/src/internal-header/header.stories.tsx +8 -5
- package/src/loader/Loader.tsx +0 -7
- package/src/table/DataCell.tsx +1 -6
- package/src/util/create-context.tsx +1 -0
- package/src/util/hooks/useMergeRefs.ts +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createContext } from "../../util/create-context";
|
|
2
|
+
import { ComponentTranslation } from "./i18n/i18n.types";
|
|
3
|
+
|
|
4
|
+
export type FileUploadLocaleContextProps = {
|
|
5
|
+
translations?: ComponentTranslation<"FileUpload">;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const [FileUploadLocaleContextProvider, useFileUploadTranslation] =
|
|
9
|
+
createContext<FileUploadLocaleContextProps>();
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import cl from "clsx";
|
|
2
|
+
import React, { HTMLAttributes, forwardRef } from "react";
|
|
3
|
+
import { FileUploadLocaleContextProvider } from "./FileUpload.context";
|
|
4
|
+
import { ComponentTranslation } from "./i18n/i18n.types";
|
|
5
|
+
import Trigger from "./parts/Trigger";
|
|
6
|
+
import Dropzone from "./parts/dropzone/Dropzone";
|
|
7
|
+
import Item from "./parts/item/Item";
|
|
8
|
+
|
|
9
|
+
interface FileUploadProps extends HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* i18n-API for customizing texts and labels
|
|
13
|
+
*/
|
|
14
|
+
translations?: ComponentTranslation<"FileUpload">;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface FileUploadComponent
|
|
18
|
+
extends React.ForwardRefExoticComponent<
|
|
19
|
+
FileUploadProps & React.RefAttributes<HTMLDivElement>
|
|
20
|
+
> {
|
|
21
|
+
/**
|
|
22
|
+
* Framed area to drag-n-drop files, upload files with button-click or copy-paste.
|
|
23
|
+
* @example
|
|
24
|
+
* Single file
|
|
25
|
+
* ```jsx
|
|
26
|
+
* <FileUpload.Dropzone
|
|
27
|
+
* label="Last opp fil"
|
|
28
|
+
* multiple={false}
|
|
29
|
+
* onSelect={onSelect}
|
|
30
|
+
* />
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* Multiple files
|
|
35
|
+
* ```jsx
|
|
36
|
+
* <FileUpload.Dropzone
|
|
37
|
+
* label="Last opp fil"
|
|
38
|
+
* multiple={true}
|
|
39
|
+
* onSelect={onSelect}
|
|
40
|
+
* />
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* Error
|
|
45
|
+
* ```jsx
|
|
46
|
+
* <FileUpload.Dropzone
|
|
47
|
+
* label="Last opp filer"
|
|
48
|
+
* onSelect={onSelect}
|
|
49
|
+
* error={error}
|
|
50
|
+
* />
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
Dropzone: typeof Dropzone;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Displays a file with status, file size, action and error message.
|
|
57
|
+
* @example
|
|
58
|
+
* Single
|
|
59
|
+
* ```jsx
|
|
60
|
+
* <FileUpload.Item file={file} status="uploading" />
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* Multiple items can be semantically grouped as a list.
|
|
65
|
+
* ```jsx
|
|
66
|
+
* <FileUpload>
|
|
67
|
+
* <VStack gap="4" as="ul">
|
|
68
|
+
* <FileUpload.Item as="li" file={file} />
|
|
69
|
+
* <FileUpload.Item as="li" file={file2} />
|
|
70
|
+
* <FileUpload.Item as="li" file={file3} status="uploading" />
|
|
71
|
+
* </VStack>
|
|
72
|
+
* </FileUpload>
|
|
73
|
+
* ```
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* Custom file object
|
|
77
|
+
* ```jsx
|
|
78
|
+
* <FileUpload.Item file={{ name: "fileName.pdf", size: 1_048_576 }} />
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* Error
|
|
83
|
+
* ```jsx
|
|
84
|
+
* <FileUpload.Item file={file} error="Something went wrong" />
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* Status & actions
|
|
89
|
+
* ```jsx
|
|
90
|
+
* <FileUpload.Item file={file} status="uploading" />
|
|
91
|
+
* <FileUpload.Item file={file} status="downloading" />
|
|
92
|
+
* <FileUpload.Item file={file} itemAction="retry" onRetry={...} />
|
|
93
|
+
* <FileUpload.Item file={file} itemAction="delete" onDelete={...} />
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
Item: typeof Item;
|
|
97
|
+
/**
|
|
98
|
+
* Wrapper for a button to trigger file select.
|
|
99
|
+
* @example
|
|
100
|
+
* ```jsx
|
|
101
|
+
* <FileUpload.Trigger onSelect={...}>
|
|
102
|
+
* <Button variant="secondary">Last opp filer</Button>
|
|
103
|
+
* </FileUpload.Trigger>
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
Trigger: typeof Trigger;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A set of components used to upload and display files.
|
|
111
|
+
* @see [📝 Documentation](https://aksel.nav.no/komponenter/core/fileupload)
|
|
112
|
+
* @example Dropzone
|
|
113
|
+
* ```jsx
|
|
114
|
+
* <FileUpload.Dropzone />
|
|
115
|
+
* ```
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* Items
|
|
119
|
+
* ```jsx
|
|
120
|
+
* <VStack gap="4" as="ul">
|
|
121
|
+
* <FileUpload.Item as="li" file={myFile} />
|
|
122
|
+
* <FileUpload.Item as="li" file={mySecondFile} />
|
|
123
|
+
* </VStack>
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export const FileUpload = forwardRef<HTMLDivElement, FileUploadProps>(
|
|
127
|
+
({ children, className, translations, ...rest }: FileUploadProps, ref) => {
|
|
128
|
+
return (
|
|
129
|
+
<FileUploadLocaleContextProvider translations={translations}>
|
|
130
|
+
<div ref={ref} {...rest} className={cl("navds-file-upload", className)}>
|
|
131
|
+
{children}
|
|
132
|
+
</div>
|
|
133
|
+
</FileUploadLocaleContextProvider>
|
|
134
|
+
);
|
|
135
|
+
},
|
|
136
|
+
) as FileUploadComponent;
|
|
137
|
+
|
|
138
|
+
FileUpload.Dropzone = Dropzone;
|
|
139
|
+
FileUpload.Item = Item;
|
|
140
|
+
FileUpload.Trigger = Trigger;
|
|
141
|
+
|
|
142
|
+
export default FileUpload;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export const fileRejectionReason = {
|
|
2
|
+
FileType: "fileType" as const,
|
|
3
|
+
FileSize: "fileSize" as const,
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type FileRejectionReason =
|
|
7
|
+
(typeof fileRejectionReason)[keyof typeof fileRejectionReason];
|
|
8
|
+
|
|
9
|
+
export type FileRejected = {
|
|
10
|
+
file: File;
|
|
11
|
+
error: true;
|
|
12
|
+
reasons: string[];
|
|
13
|
+
};
|
|
14
|
+
export type FileAccepted = { file: File; error: false };
|
|
15
|
+
|
|
16
|
+
export type FileObject = FileRejected | FileAccepted;
|
|
17
|
+
export type FileRejectedPartitioned = {
|
|
18
|
+
file: File;
|
|
19
|
+
reasons: string[];
|
|
20
|
+
};
|
|
21
|
+
export type FilesPartitioned = {
|
|
22
|
+
accepted: File[];
|
|
23
|
+
rejected: FileRejectedPartitioned[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface FileUploadBaseProps {
|
|
27
|
+
/**
|
|
28
|
+
* Indicates if it is possible to select multiple files at once.
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
multiple?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Indicates which file types to accept.
|
|
34
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept
|
|
35
|
+
*/
|
|
36
|
+
accept?: string;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum size of a file to accept
|
|
39
|
+
*/
|
|
40
|
+
maxSizeInBytes?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Custom validator that is used to decide if a file is accepted or rejected.
|
|
43
|
+
* @return true if the file is accepted, otherwise a string with the reason for rejection
|
|
44
|
+
*/
|
|
45
|
+
validator?: (file: File) => true | string;
|
|
46
|
+
/**
|
|
47
|
+
* Callback triggered on file select
|
|
48
|
+
*/
|
|
49
|
+
onSelect: (files: FileObject[], partitionedFiles: FilesPartitioned) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Disables the dropzone when current >= max, unless `disabled` prop is set to `false`.
|
|
52
|
+
*/
|
|
53
|
+
fileLimit?: {
|
|
54
|
+
max: number;
|
|
55
|
+
current: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { fireEvent, within } from "@storybook/test";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { ImageIcon } from "@navikt/aksel-icons";
|
|
5
|
+
import {
|
|
6
|
+
type FileObject,
|
|
7
|
+
UNSAFE_FileUpload as FileUpload,
|
|
8
|
+
type FilesPartitioned,
|
|
9
|
+
} from ".";
|
|
10
|
+
|
|
11
|
+
const meta: Meta<typeof FileUpload.Dropzone> = {
|
|
12
|
+
title: "ds-react/FileUpload/Dropzone",
|
|
13
|
+
component: FileUpload.Dropzone,
|
|
14
|
+
decorators: [
|
|
15
|
+
(Story) => (
|
|
16
|
+
<div style={{ width: 500, maxWidth: "100%" }}>
|
|
17
|
+
<Story />
|
|
18
|
+
</div>
|
|
19
|
+
),
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default meta;
|
|
24
|
+
|
|
25
|
+
const onSelect = (
|
|
26
|
+
allFiles: FileObject[],
|
|
27
|
+
{ accepted, rejected }: FilesPartitioned,
|
|
28
|
+
) => {
|
|
29
|
+
alert(
|
|
30
|
+
`Lastet opp ${allFiles.length} filer. Accepted: ${accepted.length}. Rejected: ${rejected.length}`,
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Default: StoryObj<typeof FileUpload.Dropzone> = {
|
|
35
|
+
render: (props) => <FileUpload.Dropzone {...props} onSelect={console.log} />,
|
|
36
|
+
args: {
|
|
37
|
+
label: "Last opp filer",
|
|
38
|
+
description: "",
|
|
39
|
+
error: "",
|
|
40
|
+
multiple: true,
|
|
41
|
+
accept: "",
|
|
42
|
+
maxSizeInBytes: 0,
|
|
43
|
+
fileLimit: { max: 2, current: 1 },
|
|
44
|
+
},
|
|
45
|
+
argTypes: {
|
|
46
|
+
disabled: { control: { type: "boolean" } },
|
|
47
|
+
},
|
|
48
|
+
parameters: { chromatic: { disable: true } },
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const States: StoryObj = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<div>
|
|
54
|
+
<h2>Disabled</h2>
|
|
55
|
+
<FileUpload.Dropzone
|
|
56
|
+
label="Disabled prop"
|
|
57
|
+
onSelect={console.log}
|
|
58
|
+
disabled
|
|
59
|
+
/>
|
|
60
|
+
<FileUpload.Dropzone
|
|
61
|
+
label="FileLimit disabled"
|
|
62
|
+
onSelect={console.log}
|
|
63
|
+
fileLimit={{ max: 1, current: 2 }}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
<h2>Error</h2>
|
|
67
|
+
<FileUpload.Dropzone
|
|
68
|
+
label="Last opp filer"
|
|
69
|
+
onSelect={onSelect}
|
|
70
|
+
error="Du må laste opp en fil"
|
|
71
|
+
description="Bruk filtype DOC, PPT eller PDF. Maks filstørrelse 10 MB."
|
|
72
|
+
/>
|
|
73
|
+
|
|
74
|
+
<h2>Dragging</h2>
|
|
75
|
+
<FileUpload.Dropzone
|
|
76
|
+
label="Drag over test"
|
|
77
|
+
multiple={false}
|
|
78
|
+
onSelect={onSelect}
|
|
79
|
+
/>
|
|
80
|
+
</div>
|
|
81
|
+
),
|
|
82
|
+
};
|
|
83
|
+
States.play = async ({ canvasElement }) => {
|
|
84
|
+
const canvas = within(canvasElement);
|
|
85
|
+
const button = canvas.getByText("Velg fil");
|
|
86
|
+
fireEvent.dragEnter(button);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const Translation: StoryObj = {
|
|
90
|
+
render: () => (
|
|
91
|
+
<div>
|
|
92
|
+
<h2>Single file</h2>
|
|
93
|
+
<FileUpload.Dropzone
|
|
94
|
+
label="Last opp fil"
|
|
95
|
+
multiple={false}
|
|
96
|
+
onSelect={onSelect}
|
|
97
|
+
/>
|
|
98
|
+
|
|
99
|
+
<h2>Custom texts</h2>
|
|
100
|
+
<FileUpload.Dropzone
|
|
101
|
+
translations={{
|
|
102
|
+
dragAndDropMultiple: "Dra og slipp bilder i format .png",
|
|
103
|
+
buttonMultiple: "Velg bilder",
|
|
104
|
+
disabled: "Du kan ikke laste opp flere bilder",
|
|
105
|
+
}}
|
|
106
|
+
label="Last opp bilder"
|
|
107
|
+
onSelect={console.log}
|
|
108
|
+
icon={ImageIcon}
|
|
109
|
+
/>
|
|
110
|
+
|
|
111
|
+
<h3>Disabled</h3>
|
|
112
|
+
<FileUpload.Dropzone
|
|
113
|
+
translations={{
|
|
114
|
+
disabled: "Du kan ikke laste opp flere bilder",
|
|
115
|
+
}}
|
|
116
|
+
label="Last opp bilder"
|
|
117
|
+
onSelect={console.log}
|
|
118
|
+
icon={ImageIcon}
|
|
119
|
+
disabled
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
),
|
|
123
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { FileItem, UNSAFE_FileUpload as FileUpload } from ".";
|
|
4
|
+
import { VStack } from "../../layout/stack";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof FileUpload.Item> = {
|
|
7
|
+
title: "ds-react/FileUpload/Item",
|
|
8
|
+
component: FileUpload.Item,
|
|
9
|
+
decorators: [
|
|
10
|
+
(Story) => (
|
|
11
|
+
<div style={{ width: 400, maxWidth: "100%" }}>
|
|
12
|
+
<Story />
|
|
13
|
+
</div>
|
|
14
|
+
),
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default meta;
|
|
19
|
+
|
|
20
|
+
const onDelete = (file: FileItem) => alert(`Delete ${file.name}`);
|
|
21
|
+
const onRetry = (file: FileItem) => alert(`Retry ${file.name}`);
|
|
22
|
+
const fileTxt = new File(["abc".repeat(10000)], "file.txt");
|
|
23
|
+
const filePng = new File(["abc".repeat(10000)], "file.png");
|
|
24
|
+
const filePdf = new File(["abc".repeat(100000)], "file.pdf");
|
|
25
|
+
const fileXlsx = new File(["abc"], "file.xlsx");
|
|
26
|
+
const fileCsv = new File(["abc"], "file.csv");
|
|
27
|
+
const filePptx = new File(["abc"], "file.pptx");
|
|
28
|
+
const fileWebp = new File(["abc"], "file.webp");
|
|
29
|
+
const fileDocx = new File(["abc"], "file.docx");
|
|
30
|
+
|
|
31
|
+
export const Icons: StoryObj<typeof FileUpload.Item> = {
|
|
32
|
+
render: () => (
|
|
33
|
+
<FileUpload>
|
|
34
|
+
<VStack gap="5" as="ul">
|
|
35
|
+
<FileUpload.Item file={fileTxt} as="li" />
|
|
36
|
+
<FileUpload.Item file={filePng} as="li" />
|
|
37
|
+
<FileUpload.Item file={fileWebp} as="li" />
|
|
38
|
+
<FileUpload.Item file={filePdf} as="li" />
|
|
39
|
+
<FileUpload.Item file={fileDocx} as="li" />
|
|
40
|
+
<FileUpload.Item file={fileDocx} as="li" status="uploading" />
|
|
41
|
+
<FileUpload.Item file={fileDocx} as="li" status="downloading" />
|
|
42
|
+
<FileUpload.Item file={fileXlsx} as="li" />
|
|
43
|
+
<FileUpload.Item file={fileCsv} as="li" />
|
|
44
|
+
<FileUpload.Item file={filePptx} as="li" />
|
|
45
|
+
</VStack>
|
|
46
|
+
</FileUpload>
|
|
47
|
+
),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const States: StoryObj<typeof FileUpload.Item> = {
|
|
51
|
+
render: () => (
|
|
52
|
+
<div>
|
|
53
|
+
<h2>Error</h2>
|
|
54
|
+
<FileUpload.Item file={fileTxt} error="Plain error" />
|
|
55
|
+
<h3>error + status</h3>
|
|
56
|
+
<FileUpload.Item
|
|
57
|
+
file={fileTxt}
|
|
58
|
+
error="Error og uploading"
|
|
59
|
+
status="uploading"
|
|
60
|
+
onRetry={() => onRetry(fileTxt)}
|
|
61
|
+
onDelete={() => onDelete(fileTxt)}
|
|
62
|
+
/>
|
|
63
|
+
<h2>Item Actions</h2>
|
|
64
|
+
<h3>status + delete</h3>
|
|
65
|
+
<FileUpload.Item
|
|
66
|
+
file={fileDocx}
|
|
67
|
+
onDelete={() => onDelete(fileDocx)}
|
|
68
|
+
onRetry={() => onRetry(fileDocx)}
|
|
69
|
+
status="uploading"
|
|
70
|
+
itemAction="delete"
|
|
71
|
+
/>
|
|
72
|
+
<h3>status + retry</h3>
|
|
73
|
+
<FileUpload.Item
|
|
74
|
+
file={fileDocx}
|
|
75
|
+
onDelete={() => onDelete(fileDocx)}
|
|
76
|
+
onRetry={() => onRetry(fileDocx)}
|
|
77
|
+
status="downloading"
|
|
78
|
+
itemAction="retry"
|
|
79
|
+
/>
|
|
80
|
+
<h3>retry</h3>
|
|
81
|
+
<FileUpload.Item
|
|
82
|
+
file={fileCsv}
|
|
83
|
+
onRetry={() => onRetry(fileCsv)}
|
|
84
|
+
itemAction="retry"
|
|
85
|
+
/>
|
|
86
|
+
<h3>delete</h3>
|
|
87
|
+
<FileUpload.Item
|
|
88
|
+
file={filePptx}
|
|
89
|
+
onDelete={() => onDelete(filePptx)}
|
|
90
|
+
itemAction="delete"
|
|
91
|
+
/>
|
|
92
|
+
<h3>retry + error</h3>
|
|
93
|
+
<FileUpload.Item
|
|
94
|
+
file={fileCsv}
|
|
95
|
+
error="Error og onRetry"
|
|
96
|
+
onRetry={() => onRetry(fileCsv)}
|
|
97
|
+
itemAction="retry"
|
|
98
|
+
/>
|
|
99
|
+
<h3>delete + error</h3>
|
|
100
|
+
<FileUpload.Item
|
|
101
|
+
file={filePptx}
|
|
102
|
+
error="Error og onDelete"
|
|
103
|
+
onDelete={() => onDelete(filePptx)}
|
|
104
|
+
itemAction="delete"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const Download: StoryObj = {
|
|
111
|
+
render: () => (
|
|
112
|
+
<VStack gap="5">
|
|
113
|
+
<FileUpload.Item
|
|
114
|
+
file={{
|
|
115
|
+
name: "with onClick.txt",
|
|
116
|
+
size: 1_048_576,
|
|
117
|
+
}}
|
|
118
|
+
onFileClick={() => alert("onFileClick")}
|
|
119
|
+
/>
|
|
120
|
+
<FileUpload.Item
|
|
121
|
+
file={{
|
|
122
|
+
name: "with href.txt",
|
|
123
|
+
size: 1,
|
|
124
|
+
}}
|
|
125
|
+
href="https://www.nav.no"
|
|
126
|
+
/>
|
|
127
|
+
<FileUpload.Item
|
|
128
|
+
file={{
|
|
129
|
+
name: "without href/onFileClick.txt",
|
|
130
|
+
size: 2_000_000,
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
<FileUpload.Item file={fileTxt} />
|
|
134
|
+
</VStack>
|
|
135
|
+
),
|
|
136
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { Meta, StoryFn, StoryObj } from "@storybook/react";
|
|
2
|
+
import React, { useEffect, useState } from "react";
|
|
3
|
+
import { UploadIcon } from "@navikt/aksel-icons";
|
|
4
|
+
import { UNSAFE_FileUpload as FileUpload, FileUploadItemProps } from ".";
|
|
5
|
+
import { Alert } from "../../alert";
|
|
6
|
+
import { Button } from "../../button";
|
|
7
|
+
import { VStack } from "../../layout/stack";
|
|
8
|
+
import { Heading } from "../../typography";
|
|
9
|
+
import {
|
|
10
|
+
FileObject,
|
|
11
|
+
FileRejected,
|
|
12
|
+
FileRejectionReason,
|
|
13
|
+
} from "./FileUpload.types";
|
|
14
|
+
|
|
15
|
+
const meta: Meta<typeof FileUpload.Dropzone> = {
|
|
16
|
+
title: "ds-react/FileUpload",
|
|
17
|
+
component: FileUpload.Dropzone,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default meta;
|
|
21
|
+
|
|
22
|
+
const MAX_FILES = 3;
|
|
23
|
+
const MAX_SIZE_MB = 1;
|
|
24
|
+
const MAX_SIZE = MAX_SIZE_MB * 1024 * 1024;
|
|
25
|
+
|
|
26
|
+
const CustomItem = ({
|
|
27
|
+
index,
|
|
28
|
+
...props
|
|
29
|
+
}: FileUploadItemProps & {
|
|
30
|
+
index: number;
|
|
31
|
+
onDelete: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
32
|
+
}) => {
|
|
33
|
+
const [loading, setLoading] = useState(true);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
setTimeout(
|
|
37
|
+
() => {
|
|
38
|
+
setLoading(false);
|
|
39
|
+
},
|
|
40
|
+
1700 * index + 1,
|
|
41
|
+
);
|
|
42
|
+
}, [index]);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<FileUpload.Item
|
|
46
|
+
{...props}
|
|
47
|
+
status={loading ? "uploading" : "idle"}
|
|
48
|
+
itemAction="delete"
|
|
49
|
+
as="li"
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const Default: StoryFn = () => {
|
|
55
|
+
const [files, setFiles] = useState<FileObject[]>([]);
|
|
56
|
+
|
|
57
|
+
function addFiles(filesToAdd: FileObject[]) {
|
|
58
|
+
setFiles([...files, ...filesToAdd]);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function removeFile(fileToRemove: FileObject) {
|
|
62
|
+
setFiles(files.filter((file) => file !== fileToRemove));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const acceptedFiles = files.filter((file) => !file.error);
|
|
66
|
+
const rejectedFiles = files.filter((f): f is FileRejected => f.error);
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<FileUpload style={{ width: 500, maxWidth: "100%", margin: "0 auto" }}>
|
|
70
|
+
<VStack gap="6">
|
|
71
|
+
<FileUpload.Dropzone
|
|
72
|
+
label="Last opp filer til søknaden"
|
|
73
|
+
description={`Maks størrelse ${MAX_SIZE_MB} MB`}
|
|
74
|
+
accept=".doc,.docx,.xls,.xlsx,.pdf"
|
|
75
|
+
maxSizeInBytes={MAX_SIZE}
|
|
76
|
+
fileLimit={{ max: MAX_FILES, current: acceptedFiles.length }}
|
|
77
|
+
onSelect={addFiles}
|
|
78
|
+
/>
|
|
79
|
+
|
|
80
|
+
{getListError(acceptedFiles) && (
|
|
81
|
+
<Alert variant="error">{getListError(acceptedFiles)}</Alert>
|
|
82
|
+
)}
|
|
83
|
+
|
|
84
|
+
{acceptedFiles.length > 0 && (
|
|
85
|
+
<VStack gap="2">
|
|
86
|
+
<Heading level="3" size="xsmall">
|
|
87
|
+
{`Vedlegg (${acceptedFiles.length} av maks ${MAX_FILES})`}
|
|
88
|
+
</Heading>
|
|
89
|
+
<VStack as="ul" gap="3">
|
|
90
|
+
{acceptedFiles.map((file, index) => (
|
|
91
|
+
<CustomItem
|
|
92
|
+
key={index}
|
|
93
|
+
index={index}
|
|
94
|
+
file={file.file}
|
|
95
|
+
onDelete={() => removeFile(file)}
|
|
96
|
+
/>
|
|
97
|
+
))}
|
|
98
|
+
</VStack>
|
|
99
|
+
</VStack>
|
|
100
|
+
)}
|
|
101
|
+
{rejectedFiles.length > 0 && (
|
|
102
|
+
<VStack gap="2">
|
|
103
|
+
<Heading level="3" size="xsmall">
|
|
104
|
+
Vedlegg med feil
|
|
105
|
+
</Heading>
|
|
106
|
+
<VStack as="ul" gap="3">
|
|
107
|
+
{rejectedFiles.map((rejected, index) => (
|
|
108
|
+
<CustomItem
|
|
109
|
+
key={index}
|
|
110
|
+
index={index}
|
|
111
|
+
file={rejected.file}
|
|
112
|
+
error={errors[rejected.reasons[0]]}
|
|
113
|
+
onDelete={() => removeFile(rejected)}
|
|
114
|
+
/>
|
|
115
|
+
))}
|
|
116
|
+
</VStack>
|
|
117
|
+
</VStack>
|
|
118
|
+
)}
|
|
119
|
+
</VStack>
|
|
120
|
+
</FileUpload>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
Default.parameters = {
|
|
124
|
+
chromatic: { disable: true },
|
|
125
|
+
layout: "padded",
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const errors: Record<FileRejectionReason, string> = {
|
|
129
|
+
fileType: "Filformatet støttes ikke",
|
|
130
|
+
fileSize: `Filen er større enn ${MAX_SIZE_MB} MB`,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function getListError(acceptedFiles: FileObject[]) {
|
|
134
|
+
const filesTooMany = acceptedFiles.length - MAX_FILES;
|
|
135
|
+
if (filesTooMany === 1)
|
|
136
|
+
return "Du har lagt ved en fil for mye, vennligst fjern en fil";
|
|
137
|
+
if (filesTooMany > 1)
|
|
138
|
+
return `Du har lagt ved ${filesTooMany} filer for mye, vennligst fjern ${filesTooMany} filer`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export const Single: StoryFn = () => {
|
|
142
|
+
const [files, setFiles] = useState<FileObject[]>([]);
|
|
143
|
+
|
|
144
|
+
function addFiles(filesToAdd: FileObject[]) {
|
|
145
|
+
setFiles(filesToAdd);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function removeFile() {
|
|
149
|
+
setFiles([]);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<VStack gap="6" style={{ width: 500, maxWidth: "100%" }}>
|
|
154
|
+
<FileUpload.Dropzone
|
|
155
|
+
label="Last opp fil til søknaden"
|
|
156
|
+
description={`Maks størrelse ${MAX_SIZE_MB} MB`}
|
|
157
|
+
accept=".doc,.docx,.xls,.xlsx,.pdf"
|
|
158
|
+
maxSizeInBytes={MAX_SIZE}
|
|
159
|
+
fileLimit={{ max: 1, current: files.length }}
|
|
160
|
+
multiple={false}
|
|
161
|
+
onSelect={addFiles}
|
|
162
|
+
/>
|
|
163
|
+
{files.map((file) => (
|
|
164
|
+
<FileUpload.Item
|
|
165
|
+
key={file.file.name}
|
|
166
|
+
file={file.file}
|
|
167
|
+
error={file.error ? errors[file.reasons[0]] : undefined}
|
|
168
|
+
onDelete={removeFile}
|
|
169
|
+
/>
|
|
170
|
+
))}
|
|
171
|
+
</VStack>
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
Single.parameters = { chromatic: { disable: true } };
|
|
175
|
+
|
|
176
|
+
export const Translation = () => (
|
|
177
|
+
<FileUpload
|
|
178
|
+
translations={{
|
|
179
|
+
dropzone: {
|
|
180
|
+
dragAndDropMultiple: "Dra og slipp bilder i format .png",
|
|
181
|
+
buttonMultiple: "Velg bilder",
|
|
182
|
+
or: "eventuelt",
|
|
183
|
+
disabled: "Du kan ikke laste opp flere bilder",
|
|
184
|
+
},
|
|
185
|
+
item: {
|
|
186
|
+
deleteButtonTitle: "Slett bilde",
|
|
187
|
+
downloading: "Laster bilde...",
|
|
188
|
+
uploading: "Laster opp bilde...",
|
|
189
|
+
retryButtonTitle: "Last opp bilde på nytt",
|
|
190
|
+
},
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
193
|
+
<VStack gap="3" style={{ width: 500, maxWidth: "100%" }}>
|
|
194
|
+
<FileUpload.Dropzone label="Last opp bilder" onSelect={console.log} />
|
|
195
|
+
<FileUpload.Item
|
|
196
|
+
file={{ name: "eksempel.png", size: 200000 }}
|
|
197
|
+
onDelete={() => null}
|
|
198
|
+
/>
|
|
199
|
+
<FileUpload.Item
|
|
200
|
+
file={{ name: "eksempel.png", size: 200000 }}
|
|
201
|
+
itemAction="retry"
|
|
202
|
+
onRetry={() => null}
|
|
203
|
+
/>
|
|
204
|
+
<FileUpload.Item
|
|
205
|
+
file={{ name: "eksempel.png", size: 200000 }}
|
|
206
|
+
status="downloading"
|
|
207
|
+
/>
|
|
208
|
+
<FileUpload.Item
|
|
209
|
+
file={{ name: "eksempel.png", size: 200000 }}
|
|
210
|
+
status="uploading"
|
|
211
|
+
/>
|
|
212
|
+
<FileUpload.Item
|
|
213
|
+
file={{ name: "eksempel.png", size: 200000 }}
|
|
214
|
+
status="uploading"
|
|
215
|
+
translations={{ uploading: "Sender bilde..." }}
|
|
216
|
+
/>
|
|
217
|
+
</VStack>
|
|
218
|
+
</FileUpload>
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
export const TriggerWithButton: StoryObj<typeof FileUpload.Trigger> = {
|
|
222
|
+
render: (props) => {
|
|
223
|
+
return (
|
|
224
|
+
<FileUpload.Trigger {...props} onSelect={console.log}>
|
|
225
|
+
<Button variant="secondary" icon={<UploadIcon aria-hidden />}>
|
|
226
|
+
Last opp filer
|
|
227
|
+
</Button>
|
|
228
|
+
</FileUpload.Trigger>
|
|
229
|
+
);
|
|
230
|
+
},
|
|
231
|
+
args: {
|
|
232
|
+
multiple: true,
|
|
233
|
+
accept: "",
|
|
234
|
+
maxSizeInBytes: 0,
|
|
235
|
+
},
|
|
236
|
+
};
|