Optimize dependencies with Rollup (#3258)
This commit is contained in:
parent
49ad81943e
commit
a51c9c19d3
11 changed files with 654 additions and 85 deletions
|
@ -188,18 +188,23 @@ describe("Clients test", () => {
|
|||
commonPage.tableUtils().checkRowItemExists(itemName, false);
|
||||
});
|
||||
|
||||
// TODO: https://github.com/keycloak/keycloak-admin-ui/issues/1854
|
||||
it("Should remove multiple client scopes from search bar", () => {
|
||||
const itemName1 = clientScopeName + 1;
|
||||
const itemName2 = clientScopeName + 2;
|
||||
cy.intercept("/admin/realms/master/client-scopes").as("load");
|
||||
commonPage.tableToolbarUtils().clickSearchButton();
|
||||
cy.wait("@load");
|
||||
cy.wait(1000);
|
||||
commonPage.tableToolbarUtils().checkActionItemIsEnabled("Remove", false);
|
||||
commonPage.tableToolbarUtils().searchItem(clientScopeName, false);
|
||||
commonPage
|
||||
.tableUtils()
|
||||
.selectRowItemCheckbox(itemName1)
|
||||
.selectRowItemCheckbox(itemName2);
|
||||
cy.intercept("/admin/realms/master/client-scopes").as("load");
|
||||
commonPage.tableToolbarUtils().clickSearchButton();
|
||||
cy.wait("@load");
|
||||
cy.wait(1000);
|
||||
commonPage.tableToolbarUtils().clickActionItem("Remove");
|
||||
commonPage.masthead().checkNotificationMessage(msgScopeMappingRemoved);
|
||||
commonPage.tableToolbarUtils().searchItem(clientScopeName, false);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"lodash-es": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"react-flow-renderer": "^9.7.4",
|
||||
"react-hook-form": "^6.15.8",
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
FileUpload,
|
||||
Form,
|
||||
FormGroup,
|
||||
Modal,
|
||||
|
@ -24,6 +23,7 @@ import {
|
|||
import type KeyStoreConfig from "@keycloak/keycloak-admin-client/lib/defs/keystoreConfig";
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { StoreSettings } from "./StoreSettings";
|
||||
import { FileUpload } from "../../components/json-file-upload/patternfly/FileUpload";
|
||||
|
||||
type GenerateKeyDialogProps = {
|
||||
clientId: string;
|
||||
|
|
|
@ -4,7 +4,6 @@ import { Controller, FormProvider, useForm, useWatch } from "react-hook-form";
|
|||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
FileUpload,
|
||||
Form,
|
||||
FormGroup,
|
||||
Modal,
|
||||
|
@ -17,6 +16,7 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { StoreSettings } from "./StoreSettings";
|
||||
import { FileUpload } from "../../components/json-file-upload/patternfly/FileUpload";
|
||||
|
||||
type ImportKeyDialogProps = {
|
||||
toggleDialog: () => void;
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import { FileUpload, FormGroup } from "@patternfly/react-core";
|
||||
import { FormGroup } from "@patternfly/react-core";
|
||||
|
||||
import { HelpItem } from "../help-enabler/HelpItem";
|
||||
import type { ComponentProps } from "./components";
|
||||
import { convertToName } from "./DynamicComponents";
|
||||
import { FileUpload } from "../json-file-upload/patternfly/FileUpload";
|
||||
|
||||
export const FileComponent = ({
|
||||
name,
|
||||
|
|
|
@ -4,17 +4,13 @@ import {
|
|||
MouseEvent as ReactMouseEvent,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
FormGroup,
|
||||
FileUpload,
|
||||
Modal,
|
||||
ModalVariant,
|
||||
Button,
|
||||
FileUploadProps,
|
||||
} from "@patternfly/react-core";
|
||||
import { FormGroup, Modal, ModalVariant, Button } from "@patternfly/react-core";
|
||||
import { DropEvent } from "react-dropzone";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CodeEditor, Language } from "@patternfly/react-code-editor";
|
||||
|
||||
import { FileUpload, FileUploadProps } from "./patternfly/FileUpload";
|
||||
|
||||
type FileUploadType = {
|
||||
value: string;
|
||||
filename: string;
|
||||
|
@ -54,27 +50,18 @@ export const FileUploadForm = ({
|
|||
};
|
||||
const [fileUpload, setFileUpload] = useState<FileUploadType>(defaultUpload);
|
||||
const removeDialog = () => setFileUpload({ ...fileUpload, modal: false });
|
||||
const handleChange: FileUploadProps["onChange"] = (
|
||||
value,
|
||||
filename,
|
||||
event
|
||||
) => {
|
||||
if (
|
||||
event.nativeEvent instanceof MouseEvent &&
|
||||
!(event.nativeEvent instanceof DragEvent)
|
||||
) {
|
||||
setFileUpload({ ...fileUpload, modal: true });
|
||||
} else {
|
||||
setFileUpload({
|
||||
...fileUpload,
|
||||
value: value.toString(),
|
||||
filename,
|
||||
});
|
||||
|
||||
if (value) {
|
||||
onChange(value.toString());
|
||||
}
|
||||
}
|
||||
const handleFileInputChange = (_event: DropEvent, file: File) => {
|
||||
setFileUpload({ ...fileUpload, filename: file.name });
|
||||
};
|
||||
|
||||
const handleTextOrDataChange = (value: string) => {
|
||||
setFileUpload({ ...fileUpload, value });
|
||||
onChange(value);
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
setFileUpload({ ...fileUpload, modal: true });
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -117,7 +104,10 @@ export const FileUploadForm = ({
|
|||
type="text"
|
||||
value={fileUpload.value}
|
||||
filename={fileUpload.filename}
|
||||
onChange={handleChange}
|
||||
onFileInputChange={handleFileInputChange}
|
||||
onDataChange={handleTextOrDataChange}
|
||||
onTextChange={handleTextOrDataChange}
|
||||
onClearClick={handleClear}
|
||||
onReadStarted={() =>
|
||||
setFileUpload({ ...fileUpload, isLoading: true })
|
||||
}
|
||||
|
@ -126,7 +116,7 @@ export const FileUploadForm = ({
|
|||
}
|
||||
isLoading={fileUpload.isLoading}
|
||||
dropzoneProps={{
|
||||
accept: extension,
|
||||
accept: { "application/text": [extension] },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
@ -142,7 +132,10 @@ export const FileUploadForm = ({
|
|||
type="text"
|
||||
value={fileUpload.value}
|
||||
filename={fileUpload.filename}
|
||||
onChange={handleChange}
|
||||
onFileInputChange={handleFileInputChange}
|
||||
onDataChange={handleTextOrDataChange}
|
||||
onTextChange={handleTextOrDataChange}
|
||||
onClearClick={handleClear}
|
||||
onReadStarted={() =>
|
||||
setFileUpload({ ...fileUpload, isLoading: true })
|
||||
}
|
||||
|
@ -158,9 +151,7 @@ export const FileUploadForm = ({
|
|||
code={fileUpload.value}
|
||||
language={language}
|
||||
height="128px"
|
||||
onChange={(value, event) =>
|
||||
handleChange(value || "", fileUpload.filename, event as any)
|
||||
}
|
||||
onChange={(value) => setFileUpload({ ...fileUpload, value })}
|
||||
isReadOnly={!rest.allowEditingUploadedText}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
import {
|
||||
DropEvent,
|
||||
DropzoneInputProps,
|
||||
DropzoneOptions,
|
||||
FileRejection,
|
||||
useDropzone,
|
||||
} from "react-dropzone";
|
||||
import { FileUploadField, FileUploadFieldProps } from "./FileUploadField";
|
||||
import { readFile, fileReaderType } from "./fileUtils";
|
||||
import { fromEvent } from "file-selector";
|
||||
|
||||
export interface FileUploadProps
|
||||
extends Omit<
|
||||
FileUploadFieldProps,
|
||||
| "children"
|
||||
| "onBrowseButtonClick"
|
||||
| "onClearButtonClick"
|
||||
| "isDragActive"
|
||||
| "containerRef"
|
||||
> {
|
||||
/** Unique id for the TextArea, also used to generate ids for accessible labels. */
|
||||
id: string;
|
||||
/** What type of file. Determines what is is passed to `onChange` and expected by `value`
|
||||
* (a string for 'text' and 'dataURL', or a File object otherwise. */
|
||||
type?: "text" | "dataURL";
|
||||
/** Value of the file's contents
|
||||
* (string if text file, File object otherwise) */
|
||||
value?: string | File;
|
||||
/** Value to be shown in the read-only filename field. */
|
||||
filename?: string;
|
||||
/** @deprecated A callback for when the file contents change. Please instead use onFileInputChange, onTextChange, onDataChange, onClearClick individually. */
|
||||
onChange?: (
|
||||
value: string | File,
|
||||
filename: string,
|
||||
event:
|
||||
| React.MouseEvent<HTMLButtonElement, MouseEvent> // Clear button was clicked
|
||||
| React.ChangeEvent<HTMLElement> // User typed in the TextArea
|
||||
| DropEvent
|
||||
) => void;
|
||||
/** Change event emitted from the hidden \<input type="file" \> field associated with the component */
|
||||
onFileInputChange?: (event: DropEvent, file: File) => void;
|
||||
/** Callback for clicking on the FileUploadField text area. By default, prevents a click in the text area from opening file dialog. */
|
||||
onClick?: (event: React.MouseEvent) => void;
|
||||
/** Additional classes added to the FileUpload container element. */
|
||||
className?: string;
|
||||
/** Flag to show if the field is disabled. */
|
||||
isDisabled?: boolean;
|
||||
/** Flag to show if the field is read only. */
|
||||
isReadOnly?: boolean;
|
||||
/** Flag to show if a file is being loaded. */
|
||||
isLoading?: boolean;
|
||||
/** Aria-valuetext for the loading spinner */
|
||||
spinnerAriaValueText?: string;
|
||||
/** Flag to show if the field is required. */
|
||||
isRequired?: boolean;
|
||||
/** Value to indicate if the field is modified to show that validation state.
|
||||
* If set to success, field will be modified to indicate valid state.
|
||||
* If set to error, field will be modified to indicate error state.
|
||||
*/
|
||||
validated?: "success" | "error" | "default";
|
||||
/** Aria-label for the TextArea. */
|
||||
"aria-label"?: string;
|
||||
/** Placeholder string to display in the empty filename field */
|
||||
filenamePlaceholder?: string;
|
||||
/** Aria-label for the read-only filename field */
|
||||
filenameAriaLabel?: string;
|
||||
/** Text for the Browse button */
|
||||
browseButtonText?: string;
|
||||
/** Text for the Clear button */
|
||||
clearButtonText?: string;
|
||||
/** Flag to hide the built-in preview of the file (where available).
|
||||
* If true, you can use children to render an alternate preview. */
|
||||
hideDefaultPreview?: boolean;
|
||||
/** Flag to allow editing of a text file's contents after it is selected from disk */
|
||||
allowEditingUploadedText?: boolean;
|
||||
/** Additional children to render after (or instead of) the file preview. */
|
||||
children?: React.ReactNode;
|
||||
|
||||
// Props available in FileUpload but not FileUploadField:
|
||||
|
||||
/** A callback for when a selected file starts loading */
|
||||
onReadStarted?: (fileHandle: File) => void;
|
||||
/** A callback for when a selected file finishes loading */
|
||||
onReadFinished?: (fileHandle: File) => void;
|
||||
/** A callback for when the FileReader API fails */
|
||||
onReadFailed?: (error: DOMException, fileHandle: File) => void;
|
||||
/** Optional extra props to customize react-dropzone. */
|
||||
dropzoneProps?: DropzoneOptions;
|
||||
/** Clear button was clicked */
|
||||
onClearClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
/** Text area text changed */
|
||||
onTextChange?: (text: string) => void;
|
||||
/** On data changed - if type='text' or type='dataURL' and file was loaded it will call this method */
|
||||
onDataChange?: (data: string) => void;
|
||||
}
|
||||
|
||||
export const FileUpload: React.FunctionComponent<FileUploadProps> = ({
|
||||
id,
|
||||
type,
|
||||
value = type === fileReaderType.text || type === fileReaderType.dataURL
|
||||
? ""
|
||||
: undefined,
|
||||
filename = "",
|
||||
children = null,
|
||||
onChange,
|
||||
onFileInputChange,
|
||||
onReadStarted,
|
||||
onReadFinished,
|
||||
onReadFailed,
|
||||
onClearClick,
|
||||
onClick = (event) => event.preventDefault(),
|
||||
onTextChange,
|
||||
onDataChange,
|
||||
dropzoneProps = {},
|
||||
...props
|
||||
}: FileUploadProps) => {
|
||||
const onDropAccepted = (acceptedFiles: File[], event: DropEvent) => {
|
||||
if (acceptedFiles.length > 0) {
|
||||
const fileHandle = acceptedFiles[0];
|
||||
if (event.type === "drop") {
|
||||
onFileInputChange?.(event, fileHandle);
|
||||
}
|
||||
if (type === fileReaderType.text || type === fileReaderType.dataURL) {
|
||||
onChange?.("", fileHandle.name, event); // Show the filename while reading
|
||||
onReadStarted?.(fileHandle);
|
||||
readFile(fileHandle, type as fileReaderType)
|
||||
.then((data) => {
|
||||
onReadFinished?.(fileHandle);
|
||||
onChange?.(data as string, fileHandle.name, event);
|
||||
onDataChange?.(data as string);
|
||||
})
|
||||
.catch((error: DOMException) => {
|
||||
onReadFailed?.(error, fileHandle);
|
||||
onReadFinished?.(fileHandle);
|
||||
onChange?.("", "", event); // Clear the filename field on a failure
|
||||
onDataChange?.("");
|
||||
});
|
||||
} else {
|
||||
onChange?.(fileHandle, fileHandle.name, event);
|
||||
}
|
||||
}
|
||||
dropzoneProps.onDropAccepted?.(acceptedFiles, event);
|
||||
};
|
||||
|
||||
const onDropRejected = (rejectedFiles: FileRejection[], event: DropEvent) => {
|
||||
if (rejectedFiles.length > 0) {
|
||||
onChange?.("", rejectedFiles[0].file.name, event);
|
||||
}
|
||||
|
||||
dropzoneProps.onDropRejected?.(rejectedFiles, event);
|
||||
};
|
||||
|
||||
const onClearButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
onChange?.("", "", event);
|
||||
onClearClick?.(event);
|
||||
setFileValue("");
|
||||
};
|
||||
|
||||
const { getRootProps, getInputProps, isDragActive, open, inputRef } =
|
||||
useDropzone({
|
||||
multiple: false,
|
||||
...dropzoneProps,
|
||||
onDropAccepted,
|
||||
onDropRejected,
|
||||
});
|
||||
|
||||
const setFileValue = (filename: string) => {
|
||||
if (!inputRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputRef.current.value = filename;
|
||||
};
|
||||
|
||||
const oldInputProps = getInputProps();
|
||||
const inputProps: DropzoneInputProps = {
|
||||
...oldInputProps,
|
||||
onChange: async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
oldInputProps.onChange?.(e);
|
||||
const files = await fromEvent(e.nativeEvent);
|
||||
if (files.length === 1) {
|
||||
onFileInputChange?.(e, files[0] as File);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<FileUploadField
|
||||
{...getRootProps({
|
||||
...props,
|
||||
refKey: "containerRef",
|
||||
onClick: (event) => event.preventDefault(),
|
||||
})}
|
||||
tabIndex={undefined} // Omit the unwanted tabIndex from react-dropzone's getRootProps
|
||||
id={id}
|
||||
type={type}
|
||||
filename={filename}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
isDragActive={isDragActive}
|
||||
onBrowseButtonClick={open}
|
||||
onClearButtonClick={onClearButtonClick}
|
||||
onTextAreaClick={onClick}
|
||||
onTextChange={onTextChange}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<input
|
||||
/* hidden, necessary for react-dropzone */
|
||||
{...inputProps}
|
||||
ref={inputRef}
|
||||
/>
|
||||
{children}
|
||||
</FileUploadField>
|
||||
);
|
||||
};
|
||||
|
||||
FileUpload.displayName = "FileUpload";
|
|
@ -0,0 +1,201 @@
|
|||
import styles from "@patternfly/react-styles/css/components/FileUpload/file-upload";
|
||||
import { css } from "@patternfly/react-styles";
|
||||
import {
|
||||
Button,
|
||||
ButtonVariant,
|
||||
InputGroup,
|
||||
Spinner,
|
||||
spinnerSize,
|
||||
TextArea,
|
||||
TextAreResizeOrientation,
|
||||
TextInput,
|
||||
} from "@patternfly/react-core";
|
||||
import { fileReaderType } from "./fileUtils";
|
||||
|
||||
export interface FileUploadFieldProps
|
||||
extends Omit<React.HTMLProps<HTMLDivElement>, "value" | "onChange"> {
|
||||
/** Unique id for the TextArea, also used to generate ids for accessible labels */
|
||||
id: string;
|
||||
/** What type of file. Determines what is is expected by `value`
|
||||
* (a string for 'text' and 'dataURL', or a File object otherwise). */
|
||||
type?: "text" | "dataURL";
|
||||
/** Value of the file's contents
|
||||
* (string if text file, File object otherwise) */
|
||||
value?: string | File;
|
||||
/** Value to be shown in the read-only filename field. */
|
||||
filename?: string;
|
||||
/** A callback for when the TextArea value changes. */
|
||||
onChange?: (
|
||||
value: string,
|
||||
filename: string,
|
||||
event:
|
||||
| React.ChangeEvent<HTMLTextAreaElement> // User typed in the TextArea
|
||||
| React.MouseEvent<HTMLButtonElement, MouseEvent> // User clicked Clear button
|
||||
) => void;
|
||||
/** Additional classes added to the FileUploadField container element. */
|
||||
className?: string;
|
||||
/** Flag to show if the field is disabled. */
|
||||
isDisabled?: boolean;
|
||||
/** Flag to show if the field is read only. */
|
||||
isReadOnly?: boolean;
|
||||
/** Flag to show if a file is being loaded. */
|
||||
isLoading?: boolean;
|
||||
/** Aria-valuetext for the loading spinner */
|
||||
spinnerAriaValueText?: string;
|
||||
/** Flag to show if the field is required. */
|
||||
isRequired?: boolean;
|
||||
/** Value to indicate if the field is modified to show that validation state.
|
||||
* If set to success, field will be modified to indicate valid state.
|
||||
* If set to error, field will be modified to indicate error state.
|
||||
*/
|
||||
validated?: "success" | "error" | "default";
|
||||
/** Aria-label for the TextArea. */
|
||||
"aria-label"?: string;
|
||||
/** Placeholder string to display in the empty filename field */
|
||||
filenamePlaceholder?: string;
|
||||
/** Aria-label for the read-only filename field */
|
||||
filenameAriaLabel?: string;
|
||||
/** Text for the Browse button */
|
||||
browseButtonText?: string;
|
||||
/** Text for the Clear button */
|
||||
clearButtonText?: string;
|
||||
/** Flag to disable the Clear button */
|
||||
isClearButtonDisabled?: boolean;
|
||||
/** Flag to hide the built-in preview of the file (where available).
|
||||
* If true, you can use children to render an alternate preview. */
|
||||
hideDefaultPreview?: boolean;
|
||||
/** Flag to allow editing of a text file's contents after it is selected from disk */
|
||||
allowEditingUploadedText?: boolean;
|
||||
/** Additional children to render after (or instead of) the file preview. */
|
||||
children?: React.ReactNode;
|
||||
|
||||
// Props available in FileUploadField but not FileUpload:
|
||||
|
||||
/** A callback for when the Browse button is clicked. */
|
||||
onBrowseButtonClick?: (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => void;
|
||||
/** A callback for when the Clear button is clicked. */
|
||||
onClearButtonClick?: (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => void;
|
||||
/** A callback from when the text area is clicked. Can also be set via the onClick property of FileUpload. */
|
||||
onTextAreaClick?: (
|
||||
event: React.MouseEvent<HTMLTextAreaElement, MouseEvent>
|
||||
) => void;
|
||||
/** Flag to show if a file is being dragged over the field */
|
||||
isDragActive?: boolean;
|
||||
/** A reference object to attach to the FileUploadField container element. */
|
||||
containerRef?: React.Ref<HTMLDivElement>;
|
||||
/** Text area text changed */
|
||||
onTextChange?: (text: string) => void;
|
||||
}
|
||||
|
||||
export const FileUploadField: React.FunctionComponent<FileUploadFieldProps> = ({
|
||||
id,
|
||||
type,
|
||||
value = "",
|
||||
filename = "",
|
||||
onChange,
|
||||
onBrowseButtonClick,
|
||||
onClearButtonClick,
|
||||
onTextAreaClick,
|
||||
onTextChange,
|
||||
className = "",
|
||||
isDisabled = false,
|
||||
isReadOnly = false,
|
||||
isLoading = false,
|
||||
spinnerAriaValueText,
|
||||
isRequired = false,
|
||||
isDragActive = false,
|
||||
validated = "default" as "success" | "error" | "default",
|
||||
"aria-label": ariaLabel = "File upload",
|
||||
filenamePlaceholder = "Drag a file here or browse to upload",
|
||||
filenameAriaLabel = filename ? "Read only filename" : filenamePlaceholder,
|
||||
browseButtonText = "Browse...",
|
||||
clearButtonText = "Clear",
|
||||
isClearButtonDisabled = !filename && !value,
|
||||
containerRef = null as React.Ref<HTMLDivElement>,
|
||||
allowEditingUploadedText = false,
|
||||
hideDefaultPreview = false,
|
||||
children = null,
|
||||
|
||||
...props
|
||||
}: FileUploadFieldProps) => {
|
||||
const onTextAreaChange = (
|
||||
newValue: string,
|
||||
event: React.ChangeEvent<HTMLTextAreaElement>
|
||||
) => {
|
||||
onChange?.(newValue, filename, event);
|
||||
onTextChange?.(newValue);
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={css(
|
||||
styles.fileUpload,
|
||||
isDragActive && styles.modifiers.dragHover,
|
||||
isLoading && styles.modifiers.loading,
|
||||
className
|
||||
)}
|
||||
ref={containerRef}
|
||||
{...props}
|
||||
>
|
||||
<div className={styles.fileUploadFileSelect}>
|
||||
<InputGroup>
|
||||
<TextInput
|
||||
isReadOnly // Always read-only regardless of isReadOnly prop (which is just for the TextArea)
|
||||
isDisabled={isDisabled}
|
||||
id={`${id}-filename`}
|
||||
name={`${id}-filename`}
|
||||
aria-label={filenameAriaLabel}
|
||||
placeholder={filenamePlaceholder}
|
||||
aria-describedby={`${id}-browse-button`}
|
||||
value={filename}
|
||||
/>
|
||||
<Button
|
||||
id={`${id}-browse-button`}
|
||||
variant={ButtonVariant.control}
|
||||
onClick={onBrowseButtonClick}
|
||||
isDisabled={isDisabled}
|
||||
>
|
||||
{browseButtonText}
|
||||
</Button>
|
||||
<Button
|
||||
variant={ButtonVariant.control}
|
||||
isDisabled={isDisabled || isClearButtonDisabled}
|
||||
onClick={onClearButtonClick}
|
||||
>
|
||||
{clearButtonText}
|
||||
</Button>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<div className={styles.fileUploadFileDetails}>
|
||||
{!hideDefaultPreview && type === fileReaderType.text && (
|
||||
<TextArea
|
||||
readOnly={isReadOnly || (!!filename && !allowEditingUploadedText)}
|
||||
disabled={isDisabled}
|
||||
isRequired={isRequired}
|
||||
resizeOrientation={TextAreResizeOrientation.vertical}
|
||||
validated={validated}
|
||||
id={id}
|
||||
name={id}
|
||||
aria-label={ariaLabel}
|
||||
value={value as string}
|
||||
onChange={onTextAreaChange}
|
||||
onClick={onTextAreaClick}
|
||||
/>
|
||||
)}
|
||||
{isLoading && (
|
||||
<div className={styles.fileUploadFileDetailsSpinner}>
|
||||
<Spinner
|
||||
size={spinnerSize.lg}
|
||||
aria-valuetext={spinnerAriaValueText}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
FileUploadField.displayName = "FileUploadField";
|
|
@ -0,0 +1,31 @@
|
|||
export enum fileReaderType {
|
||||
text = "text",
|
||||
dataURL = "dataURL",
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a file using the FileReader API, either as a plain text string or as a DataURL string.
|
||||
* Returns a promise which will resolve with the file contents as a string or reject with a DOMException.
|
||||
*
|
||||
* @param {File} fileHandle - File object to read
|
||||
* @param {fileReaderType} type - How to read it
|
||||
*/
|
||||
export function readFile(fileHandle: File, type: fileReaderType) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = () => reject(reader.error);
|
||||
|
||||
switch (type) {
|
||||
case fileReaderType.text:
|
||||
reader.readAsText(fileHandle);
|
||||
break;
|
||||
case fileReaderType.dataURL:
|
||||
reader.readAsDataURL(fileHandle);
|
||||
break;
|
||||
default:
|
||||
reject("unknown type");
|
||||
}
|
||||
});
|
||||
}
|
|
@ -13,16 +13,6 @@ export default defineConfig({
|
|||
mainFields: ["module"],
|
||||
dedupe: ["react", "react-dom"],
|
||||
},
|
||||
optimizeDeps: {
|
||||
// Enable optimization of dependencies using esbuild (see https://vitejs.dev/guide/migration.html#using-esbuild-deps-optimization-at-build-time).
|
||||
disabled: false,
|
||||
},
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
// Ensure `@rollup/plugin-commonjs` is not loaded, using esbuild instead (see https://vitejs.dev/guide/migration.html#using-esbuild-deps-optimization-at-build-time).
|
||||
include: [],
|
||||
},
|
||||
},
|
||||
plugins: [react(), checker({ typescript: true })],
|
||||
test: {
|
||||
setupFiles: "vitest.setup.ts",
|
||||
|
|
204
package-lock.json
generated
204
package-lock.json
generated
|
@ -41,6 +41,7 @@
|
|||
"lodash-es": "^4.17.21",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"react-flow-renderer": "^9.7.4",
|
||||
"react-hook-form": "^6.15.8",
|
||||
|
@ -3208,6 +3209,45 @@
|
|||
"react-monaco-editor": "^0.41.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-code-editor/node_modules/attr-accept": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz",
|
||||
"integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==",
|
||||
"dependencies": {
|
||||
"core-js": "^2.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-code-editor/node_modules/file-selector": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz",
|
||||
"integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-code-editor/node_modules/react-dropzone": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz",
|
||||
"integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==",
|
||||
"dependencies": {
|
||||
"attr-accept": "^1.1.3",
|
||||
"file-selector": "^0.1.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"prop-types-extra": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-core": {
|
||||
"version": "4.235.7",
|
||||
"resolved": "https://registry.npmjs.org/@patternfly/react-core/-/react-core-4.235.7.tgz",
|
||||
|
@ -3226,6 +3266,45 @@
|
|||
"react-dom": "^16.8.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-core/node_modules/attr-accept": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz",
|
||||
"integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==",
|
||||
"dependencies": {
|
||||
"core-js": "^2.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-core/node_modules/file-selector": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz",
|
||||
"integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-core/node_modules/react-dropzone": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz",
|
||||
"integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==",
|
||||
"dependencies": {
|
||||
"attr-accept": "^1.1.3",
|
||||
"file-selector": "^0.1.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"prop-types-extra": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=0.14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@patternfly/react-icons": {
|
||||
"version": "4.86.7",
|
||||
"resolved": "https://registry.npmjs.org/@patternfly/react-icons/-/react-icons-4.86.7.tgz",
|
||||
|
@ -4688,12 +4767,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/attr-accept": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz",
|
||||
"integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==",
|
||||
"dependencies": {
|
||||
"core-js": "^2.5.0"
|
||||
},
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
|
@ -8398,14 +8474,14 @@
|
|||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
|
||||
},
|
||||
"node_modules/file-selector": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz",
|
||||
"integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
|
||||
"integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.1"
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/file-type": {
|
||||
|
@ -12155,20 +12231,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-dropzone": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz",
|
||||
"integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==",
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.2.tgz",
|
||||
"integrity": "sha512-5oyGN/B5rNhop2ggUnxztXBQ6q6zii+OMEftPzsxAR2hhpVWz0nAV+3Ktxo2h5bZzdcCKrpd8bfWAVsveIBM+w==",
|
||||
"dependencies": {
|
||||
"attr-accept": "^1.1.3",
|
||||
"file-selector": "^0.1.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"prop-types-extra": "^1.1.0"
|
||||
"attr-accept": "^2.2.2",
|
||||
"file-selector": "^0.6.0",
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
"node": ">= 10.13"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=0.14.0"
|
||||
"react": ">= 16.8 || 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
|
@ -17927,6 +18002,35 @@
|
|||
"@patternfly/react-styles": "^4.85.7",
|
||||
"react-dropzone": "9.0.0",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"attr-accept": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz",
|
||||
"integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==",
|
||||
"requires": {
|
||||
"core-js": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz",
|
||||
"integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz",
|
||||
"integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==",
|
||||
"requires": {
|
||||
"attr-accept": "^1.1.3",
|
||||
"file-selector": "^0.1.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"prop-types-extra": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@patternfly/react-core": {
|
||||
|
@ -17941,6 +18045,35 @@
|
|||
"react-dropzone": "9.0.0",
|
||||
"tippy.js": "5.1.2",
|
||||
"tslib": "^2.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"attr-accept": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz",
|
||||
"integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==",
|
||||
"requires": {
|
||||
"core-js": "^2.5.0"
|
||||
}
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz",
|
||||
"integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz",
|
||||
"integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==",
|
||||
"requires": {
|
||||
"attr-accept": "^1.1.3",
|
||||
"file-selector": "^0.1.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"prop-types-extra": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@patternfly/react-icons": {
|
||||
|
@ -18894,6 +19027,7 @@
|
|||
"progress-promise": "^0.0.6",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-dropzone": "^14.2.2",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
"react-flow-renderer": "^9.7.4",
|
||||
"react-hook-form": "^6.15.8",
|
||||
|
@ -19199,12 +19333,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"attr-accept": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-1.1.3.tgz",
|
||||
"integrity": "sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==",
|
||||
"requires": {
|
||||
"core-js": "^2.5.0"
|
||||
}
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz",
|
||||
"integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg=="
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
|
@ -21993,11 +22124,11 @@
|
|||
"integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
|
||||
},
|
||||
"file-selector": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz",
|
||||
"integrity": "sha512-kCWw3+Aai8Uox+5tHCNgMFaUdgidxvMnLWO6fM5sZ0hA2wlHP5/DHGF0ECe84BiB95qdJbKNEJhWKVDvMN+JDQ==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.6.0.tgz",
|
||||
"integrity": "sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==",
|
||||
"requires": {
|
||||
"tslib": "^2.0.1"
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"file-type": {
|
||||
|
@ -24826,14 +24957,13 @@
|
|||
}
|
||||
},
|
||||
"react-dropzone": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-9.0.0.tgz",
|
||||
"integrity": "sha512-wZ2o9B2qkdE3RumWhfyZT9swgJYJPeU5qHEcMU8weYpmLex1eeWX0CC32/Y0VutB+BBi2D+iePV/YZIiB4kZGw==",
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-14.2.2.tgz",
|
||||
"integrity": "sha512-5oyGN/B5rNhop2ggUnxztXBQ6q6zii+OMEftPzsxAR2hhpVWz0nAV+3Ktxo2h5bZzdcCKrpd8bfWAVsveIBM+w==",
|
||||
"requires": {
|
||||
"attr-accept": "^1.1.3",
|
||||
"file-selector": "^0.1.8",
|
||||
"prop-types": "^15.6.2",
|
||||
"prop-types-extra": "^1.1.0"
|
||||
"attr-accept": "^2.2.2",
|
||||
"file-selector": "^0.6.0",
|
||||
"prop-types": "^15.8.1"
|
||||
}
|
||||
},
|
||||
"react-error-boundary": {
|
||||
|
|
Loading…
Reference in a new issue