mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-13 17:05:43 +01:00
Harmonize FileInput component with styleguide (#1693)
This commit is contained in:
2
gradle/changelog/file_input_component.yaml
Normal file
2
gradle/changelog/file_input_component.yaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
- type: fixed
|
||||||
|
description: Harmonize FileInput component with styleguide ([#1693](https://github.com/scm-manager/scm-manager/pull/1693))
|
||||||
@@ -46984,6 +46984,91 @@ exports[`Storyshots Forms|DropDown With Translation 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`Storyshots Forms|FileInput Default 1`] = `
|
||||||
|
<div
|
||||||
|
className="FileInputstories__Decorator-sc-5svgf8-0 icfkjP"
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
onSubmit={[Function]}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="field"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
className="label"
|
||||||
|
>
|
||||||
|
Upload File
|
||||||
|
|
||||||
|
<span
|
||||||
|
className="tooltip has-tooltip-right Help__HelpTooltip-ykmmew-0 eRZLRi is-inline-block has-tooltip-multiline"
|
||||||
|
data-tooltip="Select your most loved file"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-question-circle has-text-blue-light"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className="file is-info has-name is-fullwidth"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
className="file-label"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="file-input"
|
||||||
|
name="uploadFile"
|
||||||
|
onBlur={[Function]}
|
||||||
|
onChange={[Function]}
|
||||||
|
type="file"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className="file-cta"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="file-icon"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
className="fas fa-arrow-circle-up"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="file-label has-text-weight-bold"
|
||||||
|
>
|
||||||
|
fileInput.label
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
className="file-name has-text-weight-light has-text-grey-light"
|
||||||
|
>
|
||||||
|
fileInput.noFileChosen
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="level"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="level-left"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="level-right"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="button is-primary"
|
||||||
|
data-testid="submit-button"
|
||||||
|
onClick={[Function]}
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`Storyshots Forms|InputField AutoFocus 1`] = `
|
exports[`Storyshots Forms|InputField AutoFocus 1`] = `
|
||||||
<div
|
<div
|
||||||
className="InputFieldstories__Decorator-kq2lhz-0 cURTRq"
|
className="InputFieldstories__Decorator-kq2lhz-0 cURTRq"
|
||||||
|
|||||||
69
scm-ui/ui-components/src/forms/FileInput.stories.tsx
Normal file
69
scm-ui/ui-components/src/forms/FileInput.stories.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
import React, { FC, useState } from "react";
|
||||||
|
import { MemoryRouter } from "react-router-dom";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { storiesOf } from "@storybook/react";
|
||||||
|
import { Level, SubmitButton } from "../index";
|
||||||
|
import FileInput from "./FileInput";
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const Decorator = styled.div`
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 40rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type Settings = {
|
||||||
|
uploadFile: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReactHookForm: FC = () => {
|
||||||
|
const { register, handleSubmit } = useForm<Settings>();
|
||||||
|
const [stored, setStored] = useState<Settings>();
|
||||||
|
|
||||||
|
const onSubmit = (settings: Settings) => {
|
||||||
|
setStored(settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<FileInput label="Upload File" helpText="Select your most loved file" {...register("uploadFile")} />
|
||||||
|
<Level right={<SubmitButton>Submit</SubmitButton>} />
|
||||||
|
</form>
|
||||||
|
{stored ? (
|
||||||
|
<div className="mt-5">
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(stored, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
storiesOf("Forms|FileInput", module)
|
||||||
|
.addDecorator((storyFn) => <Decorator>{storyFn()}</Decorator>)
|
||||||
|
.addDecorator((storyFn) => <MemoryRouter>{storyFn()}</MemoryRouter>)
|
||||||
|
.add("Default", () => <ReactHookForm />);
|
||||||
@@ -21,13 +21,16 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
import React, { ChangeEvent, FC, FocusEvent } from "react";
|
import React, { ChangeEvent, FC, FocusEvent, useState } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
import { File } from "@scm-manager/ui-types";
|
||||||
import { createAttributesForTesting } from "../devBuild";
|
import { createAttributesForTesting } from "../devBuild";
|
||||||
|
import LabelWithHelpIcon from "./LabelWithHelpIcon";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
filenamePlaceholder?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
label?: string;
|
label?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
@@ -41,6 +44,7 @@ type Props = {
|
|||||||
|
|
||||||
const FileInput: FC<Props> = ({
|
const FileInput: FC<Props> = ({
|
||||||
name,
|
name,
|
||||||
|
filenamePlaceholder,
|
||||||
testId,
|
testId,
|
||||||
helpText,
|
helpText,
|
||||||
placeholder,
|
placeholder,
|
||||||
@@ -49,9 +53,16 @@ const FileInput: FC<Props> = ({
|
|||||||
className,
|
className,
|
||||||
ref,
|
ref,
|
||||||
onBlur,
|
onBlur,
|
||||||
onChange
|
onChange,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [t] = useTranslation("commons");
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
|
||||||
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const uploadedFile = event?.target?.files![0];
|
||||||
|
// @ts-ignore the uploaded file doesn't match our types
|
||||||
|
setFile(uploadedFile);
|
||||||
|
|
||||||
if (onChange && event.target.files) {
|
if (onChange && event.target.files) {
|
||||||
onChange(event);
|
onChange(event);
|
||||||
}
|
}
|
||||||
@@ -66,18 +77,33 @@ const FileInput: FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={classNames("field", className)}>
|
<div className={classNames("field", className)}>
|
||||||
<LabelWithHelpIcon label={label} helpText={helpText} />
|
<LabelWithHelpIcon label={label} helpText={helpText} />
|
||||||
<div className="control">
|
<div className="file is-info has-name is-fullwidth">
|
||||||
<input
|
<label className="file-label">
|
||||||
ref={ref}
|
<input
|
||||||
name={name}
|
ref={ref}
|
||||||
className={classNames("input", "p-1", className)}
|
name={name}
|
||||||
type="file"
|
className="file-input"
|
||||||
placeholder={placeholder}
|
type="file"
|
||||||
disabled={disabled}
|
placeholder={placeholder}
|
||||||
onChange={handleChange}
|
disabled={disabled}
|
||||||
onBlur={handleBlur}
|
onChange={handleChange}
|
||||||
{...createAttributesForTesting(testId)}
|
onBlur={handleBlur}
|
||||||
/>
|
{...createAttributesForTesting(testId)}
|
||||||
|
/>
|
||||||
|
<span className="file-cta">
|
||||||
|
<span className="file-icon">
|
||||||
|
<i className="fas fa-arrow-circle-up" />
|
||||||
|
</span>
|
||||||
|
<span className="file-label has-text-weight-bold">{t("fileInput.label")}</span>
|
||||||
|
</span>
|
||||||
|
{file?.name ? (
|
||||||
|
<span className="file-name">{file?.name}</span>
|
||||||
|
) : (
|
||||||
|
<span className="file-name has-text-weight-light has-text-grey-light">
|
||||||
|
{filenamePlaceholder || t("fileInput.noFileChosen")}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -664,8 +664,9 @@ form .field:not(.is-grouped) {
|
|||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
.input,
|
.input,
|
||||||
.textarea {
|
.select select,
|
||||||
/*background-color: whitesmoke;*/
|
.textarea,
|
||||||
|
.file-name {
|
||||||
border-color: $blue-light;
|
border-color: $blue-light;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,6 +122,10 @@
|
|||||||
"fileUpload": {
|
"fileUpload": {
|
||||||
"label": "Datei auswählen"
|
"label": "Datei auswählen"
|
||||||
},
|
},
|
||||||
|
"fileInput": {
|
||||||
|
"label": "Datei auswählen",
|
||||||
|
"noFileChosen": "Keine Datei ausgewählt"
|
||||||
|
},
|
||||||
"importLog": {
|
"importLog": {
|
||||||
"title": "Importprotokoll"
|
"title": "Importprotokoll"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -123,6 +123,10 @@
|
|||||||
"fileUpload": {
|
"fileUpload": {
|
||||||
"label": "Select File"
|
"label": "Select File"
|
||||||
},
|
},
|
||||||
|
"fileInput": {
|
||||||
|
"label": "Select File",
|
||||||
|
"noFileChosen": "No file chosen"
|
||||||
|
},
|
||||||
"importLog": {
|
"importLog": {
|
||||||
"title": "Import Log"
|
"title": "Import Log"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user