mirror of
https://github.com/portainer/portainer.git
synced 2025-08-04 13:25:26 +02:00
refactor(app): move react components to react codebase [EE-3179] (#6971)
This commit is contained in:
parent
212400c283
commit
18252ab854
346 changed files with 642 additions and 644 deletions
|
@ -0,0 +1,7 @@
|
|||
.file-input {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.file-button {
|
||||
margin-left: 0 !important;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { FileUploadField } from './FileUploadField';
|
||||
|
||||
export default {
|
||||
component: FileUploadField,
|
||||
title: 'Components/Buttons/FileUploadField',
|
||||
} as Meta;
|
||||
|
||||
interface Args {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export { Example };
|
||||
|
||||
function Example({ title }: Args) {
|
||||
const [value, setValue] = useState<File>();
|
||||
function onChange(value: File) {
|
||||
if (value) {
|
||||
setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<FileUploadField
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
title={title}
|
||||
inputId="file-field"
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { fireEvent, render } from '@/react-tools/test-utils';
|
||||
|
||||
import { FileUploadField } from './FileUploadField';
|
||||
|
||||
test('render should make the file button clickable and fire onChange event after click', async () => {
|
||||
const onClick = jest.fn();
|
||||
const { findByText, findByLabelText } = render(
|
||||
<FileUploadField
|
||||
title="test button"
|
||||
onChange={onClick}
|
||||
inputId="file-field"
|
||||
/>
|
||||
);
|
||||
|
||||
const button = await findByText('test button');
|
||||
expect(button).toBeVisible();
|
||||
|
||||
const input = await findByLabelText('file-input');
|
||||
expect(input).not.toBeNull();
|
||||
|
||||
const mockFile = new File([], 'file.txt');
|
||||
if (input) {
|
||||
fireEvent.change(input, {
|
||||
target: { files: [mockFile] },
|
||||
});
|
||||
}
|
||||
expect(onClick).toHaveBeenCalledWith(mockFile);
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
import { ChangeEvent, createRef } from 'react';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
|
||||
import styles from './FileUploadField.module.css';
|
||||
|
||||
export interface Props {
|
||||
onChange(value: File): void;
|
||||
value?: File;
|
||||
accept?: string;
|
||||
title?: string;
|
||||
required?: boolean;
|
||||
inputId: string;
|
||||
}
|
||||
|
||||
export function FileUploadField({
|
||||
onChange,
|
||||
value,
|
||||
accept,
|
||||
title = 'Select a file',
|
||||
required = false,
|
||||
inputId,
|
||||
}: Props) {
|
||||
const fileRef = createRef<HTMLInputElement>();
|
||||
|
||||
return (
|
||||
<div className="file-upload-field">
|
||||
<input
|
||||
id={inputId}
|
||||
ref={fileRef}
|
||||
type="file"
|
||||
accept={accept}
|
||||
required={required}
|
||||
className={styles.fileInput}
|
||||
onChange={changeHandler}
|
||||
aria-label="file-input"
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
color="primary"
|
||||
onClick={handleButtonClick}
|
||||
className={styles.fileButton}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
|
||||
<span className="space-left">
|
||||
{value ? (
|
||||
value.name
|
||||
) : (
|
||||
<i className="fa fa-times red-icon" aria-hidden="true" />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
function handleButtonClick() {
|
||||
if (fileRef && fileRef.current) {
|
||||
fileRef.current.click();
|
||||
}
|
||||
}
|
||||
|
||||
function changeHandler(event: ChangeEvent<HTMLInputElement>) {
|
||||
if (event.target && event.target.files && event.target.files.length > 0) {
|
||||
onChange(event.target.files[0]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { FileUploadForm } from './FileUploadForm';
|
||||
|
||||
export default {
|
||||
component: FileUploadForm,
|
||||
title: 'Components/Form/FileUploadForm',
|
||||
} as Meta;
|
||||
|
||||
interface Args {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export { Example };
|
||||
|
||||
function Example({ title }: Args) {
|
||||
const [value, setValue] = useState<File>();
|
||||
function onChange(value: File) {
|
||||
if (value) {
|
||||
setValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="form-horizontal">
|
||||
<FileUploadForm
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
title={title}
|
||||
description={
|
||||
<span>You can upload a Compose file from your computer.</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import { render } from '@/react-tools/test-utils';
|
||||
|
||||
import { FileUploadForm } from './FileUploadForm';
|
||||
|
||||
test('render should include description', async () => {
|
||||
const onClick = jest.fn();
|
||||
const { findByText } = render(
|
||||
<FileUploadForm
|
||||
title="test button"
|
||||
onChange={onClick}
|
||||
description={<span>test description</span>}
|
||||
/>
|
||||
);
|
||||
|
||||
const button = await findByText('test button');
|
||||
expect(button).toBeVisible();
|
||||
|
||||
const description = await findByText('test description');
|
||||
expect(description).toBeVisible();
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
|
||||
import { FormSectionTitle } from '@@/form-components/FormSectionTitle';
|
||||
import { FileUploadField } from '@@/form-components/FileUpload/FileUploadField';
|
||||
|
||||
export interface Props {
|
||||
onChange(value: unknown): void;
|
||||
value?: File;
|
||||
title?: string;
|
||||
required?: boolean;
|
||||
description: ReactNode;
|
||||
}
|
||||
|
||||
export function FileUploadForm({
|
||||
onChange,
|
||||
value,
|
||||
title = 'Select a file',
|
||||
required = false,
|
||||
description,
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<div className="file-upload-form">
|
||||
<FormSectionTitle>Upload</FormSectionTitle>
|
||||
<div className="form-group">
|
||||
<span className="col-sm-12 text-muted small">{description}</span>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<div className="col-sm-12">
|
||||
<FileUploadField
|
||||
inputId="file-upload-field"
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
title={title}
|
||||
required={required}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
2
app/react/components/form-components/FileUpload/index.ts
Normal file
2
app/react/components/form-components/FileUpload/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { FileUploadField } from './FileUploadField';
|
||||
export { FileUploadForm } from './FileUploadForm';
|
Loading…
Add table
Add a link
Reference in a new issue