migrate ui-components from flow to typescript

This commit is contained in:
Sebastian Sdorra
2019-10-20 16:59:02 +02:00
parent c41efbdc4f
commit f49e17a3a7
151 changed files with 2039 additions and 25265 deletions

View File

@@ -1,7 +1,7 @@
import React from 'react';
import React, { MouseEvent } from "react";
import { AddButton } from '../buttons';
import InputField from './InputField';
import { AddButton } from "../buttons";
import InputField from "./InputField";
type Props = {
addEntry: (p: string) => void;
@@ -21,7 +21,7 @@ class AddEntryToTableField extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
entryToAdd: '',
entryToAdd: ""
};
}
@@ -29,7 +29,7 @@ class AddEntryToTableField extends React.Component<Props, State> {
const { validateEntry } = this.props;
if (
!this.state.entryToAdd ||
this.state.entryToAdd === '' ||
this.state.entryToAdd === "" ||
!validateEntry
) {
return true;
@@ -44,7 +44,7 @@ class AddEntryToTableField extends React.Component<Props, State> {
buttonLabel,
fieldLabel,
errorMessage,
helpText,
helpText
} = this.props;
return (
<div className="field">
@@ -61,13 +61,13 @@ class AddEntryToTableField extends React.Component<Props, State> {
<AddButton
label={buttonLabel}
action={this.addButtonClicked}
disabled={disabled || this.state.entryToAdd === '' || !this.isValid()}
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()}
/>
</div>
);
}
addButtonClicked = (event: Event) => {
addButtonClicked = (event: MouseEvent) => {
event.preventDefault();
this.appendEntry();
};
@@ -77,14 +77,14 @@ class AddEntryToTableField extends React.Component<Props, State> {
this.props.addEntry(entryToAdd);
this.setState({
...this.state,
entryToAdd: '',
entryToAdd: ""
});
};
handleAddEntryChange = (entryname: string) => {
this.setState({
...this.state,
entryToAdd: entryname,
entryToAdd: entryname
});
};
}

View File

@@ -1,8 +1,8 @@
import React from 'react';
import React, { MouseEvent } from "react";
import { AutocompleteObject, SelectValue } from '@scm-manager/ui-types';
import Autocomplete from '../Autocomplete';
import AddButton from '../buttons/AddButton';
import { AutocompleteObject, SelectValue } from "@scm-manager/ui-types";
import Autocomplete from "../Autocomplete";
import AddButton from "../buttons/AddButton";
type Props = {
addEntry: (p: SelectValue) => void;
@@ -10,7 +10,7 @@ type Props = {
buttonLabel: string;
fieldLabel: string;
helpText?: string;
loadSuggestions: (p: string) => Promise<AutocompleteObject>;
loadSuggestions: (p: string) => Promise<SelectValue[]>;
placeholder?: string;
loadingMessage?: string;
noOptionsMessage?: string;
@@ -24,7 +24,7 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
selectedValue: undefined,
selectedValue: undefined
};
}
render() {
@@ -36,7 +36,7 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
loadSuggestions,
placeholder,
loadingMessage,
noOptionsMessage,
noOptionsMessage
} = this.props;
const { selectedValue } = this.state;
@@ -63,7 +63,7 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
);
}
addButtonClicked = (event: Event) => {
addButtonClicked = (event: MouseEvent) => {
event.preventDefault();
this.appendEntry();
};
@@ -73,20 +73,20 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
if (!selectedValue) {
return;
}
// $FlowFixMe null is needed to clear the selection; undefined does not work
this.setState(
// @ts-ignore null is needed to clear the selection; undefined does not work
{
...this.state,
selectedValue: null,
selectedValue: null
},
() => this.props.addEntry(selectedValue),
() => this.props.addEntry(selectedValue)
);
};
handleAddEntryChange = (selection: SelectValue) => {
this.setState({
...this.state,
selectedValue: selection,
selectedValue: selection
});
};
}

View File

@@ -0,0 +1,21 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import Checkbox from "./Checkbox";
import styled from "styled-components";
const Spacing = styled.div`
padding: 2em;
`;
storiesOf("Forms|Checkbox", module)
.add("Default", () => (
<Spacing>
<Checkbox label="Not checked" checked={false} />
<Checkbox label="Checked" checked={true} />
</Spacing>
))
.add("Disabled", () => (
<Spacing>
<Checkbox label="Checked but disabled" checked={true} disabled={true} />
</Spacing>
));

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Help } from '../index';
import React, { ChangeEvent } from "react";
import { Help } from "../index";
type Props = {
label?: string;
@@ -11,7 +11,7 @@ type Props = {
};
class Checkbox extends React.Component<Props> {
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
onCheckboxChange = (event: ChangeEvent<HTMLInputElement>) => {
if (this.props.onChange) {
this.props.onChange(event.target.checked, this.props.name);
}
@@ -28,13 +28,18 @@ class Checkbox extends React.Component<Props> {
return (
<div className="field is-grouped">
<div className="control">
{/*
we have to ignore the next line,
because jsx label does not the custom disabled attribute
but bulma does.
// @ts-ignore */}
<label className="checkbox" disabled={this.props.disabled}>
<input
type="checkbox"
checked={this.props.checked}
onChange={this.onCheckboxChange}
disabled={this.props.disabled}
/>{' '}
/>{" "}
{this.props.label}
{this.renderHelp()}
</label>

View File

@@ -1,5 +1,5 @@
import React from 'react';
import classNames from 'classnames';
import React, { ChangeEvent } from "react";
import classNames from "classnames";
type Props = {
options: string[];
@@ -17,14 +17,14 @@ class DropDown extends React.Component<Props> {
optionValues,
preselectedOption,
className,
disabled,
disabled
} = this.props;
return (
<div
className={classNames(className, 'select', disabled ? 'disabled' : '')}
className={classNames(className, "select", disabled ? "disabled" : "")}
>
<select
value={preselectedOption ? preselectedOption : ''}
value={preselectedOption ? preselectedOption : ""}
onChange={this.change}
disabled={disabled}
>
@@ -48,7 +48,7 @@ class DropDown extends React.Component<Props> {
);
}
change = (event: SyntheticInputEvent<HTMLSelectElement>) => {
change = (event: ChangeEvent<HTMLSelectElement>) => {
this.props.optionSelected(event.target.value);
};
}

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { translate } from 'react-i18next';
import styled from 'styled-components';
import React, { ChangeEvent } from "react";
import { translate } from "react-i18next";
import styled from "styled-components";
import { FormEvent } from "react";
type Props = {
filter: (p: string) => void;
@@ -19,20 +20,20 @@ const FixedHeightInput = styled.input`
`;
class FilterInput extends React.Component<Props, State> {
constructor(props) {
constructor(props: Props) {
super(props);
this.state = {
value: this.props.value ? this.props.value : '',
value: this.props.value ? this.props.value : ""
};
}
handleChange = event => {
handleChange = (event: ChangeEvent<HTMLInputElement>) => {
this.setState({
value: event.target.value,
value: event.target.value
});
};
handleSubmit = event => {
handleSubmit = (event: FormEvent) => {
this.props.filter(this.state.value);
event.preventDefault();
};
@@ -45,7 +46,7 @@ class FilterInput extends React.Component<Props, State> {
<FixedHeightInput
className="input"
type="search"
placeholder={t('filterEntries')}
placeholder={t("filterEntries")}
value={this.state.value}
onChange={this.handleChange}
/>
@@ -58,4 +59,4 @@ class FilterInput extends React.Component<Props, State> {
}
}
export default translate('commons')(FilterInput);
export default translate("commons")(FilterInput);

View File

@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import LabelWithHelpIcon from './LabelWithHelpIcon';
import React, { ChangeEvent, KeyboardEvent } from "react";
import classNames from "classnames";
import LabelWithHelpIcon from "./LabelWithHelpIcon";
type Props = {
label?: string;
@@ -19,8 +19,8 @@ type Props = {
class InputField extends React.Component<Props> {
static defaultProps = {
type: 'text',
placeholder: '',
type: "text",
placeholder: ""
};
field: HTMLInputElement | null | undefined;
@@ -31,16 +31,16 @@ class InputField extends React.Component<Props> {
}
}
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
handleInput = (event: ChangeEvent<HTMLInputElement>) => {
this.props.onChange(event.target.value, this.props.name);
};
handleKeyPress = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
handleKeyPress = (event: KeyboardEvent<HTMLInputElement>) => {
const onReturnPressed = this.props.onReturnPressed;
if (!onReturnPressed) {
return;
}
if (event.key === 'Enter') {
if (event.key === "Enter") {
event.preventDefault();
onReturnPressed();
}
@@ -55,13 +55,13 @@ class InputField extends React.Component<Props> {
errorMessage,
disabled,
label,
helpText,
helpText
} = this.props;
const errorView = validationError ? 'is-danger' : '';
const errorView = validationError ? "is-danger" : "";
const helper = validationError ? (
<p className="help is-danger">{errorMessage}</p>
) : (
''
""
);
return (
<div className="field">
@@ -71,7 +71,7 @@ class InputField extends React.Component<Props> {
ref={input => {
this.field = input;
}}
className={classNames('input', errorView)}
className={classNames("input", errorView)}
type={type}
placeholder={placeholder}
value={value}

View File

@@ -1,5 +1,5 @@
import React from 'react';
import Help from '../Help';
import React from "react";
import Help from "../Help";
type Props = {
label?: string;
@@ -26,7 +26,7 @@ class LabelWithHelpIcon extends React.Component<Props> {
);
}
return '';
return "";
}
}

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { translate } from 'react-i18next';
import { DisplayedUser } from '@scm-manager/ui-types';
import TagGroup from './TagGroup';
import React from "react";
import { translate } from "react-i18next";
import { DisplayedUser } from "@scm-manager/ui-types";
import TagGroup from "./TagGroup";
type Props = {
members: string[];
@@ -18,14 +18,14 @@ class MemberNameTagGroup extends React.Component<Props> {
return {
id,
displayName: id,
mail: '',
mail: ""
};
});
return (
<TagGroup
items={membersExtended}
label={label ? label : t('group.members')}
helpText={helpText ? helpText : t('groupForm.help.memberHelpText')}
label={label ? label : t("group.members")}
helpText={helpText ? helpText : t("groupForm.help.memberHelpText")}
onRemove={this.removeEntry}
/>
);
@@ -33,10 +33,10 @@ class MemberNameTagGroup extends React.Component<Props> {
removeEntry = (membersExtended: DisplayedUser[]) => {
const members = membersExtended.map(function(item) {
return item['id'];
return item["id"];
});
this.props.memberListChanged(members);
};
}
export default translate('groups')(MemberNameTagGroup);
export default translate("groups")(MemberNameTagGroup);

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { translate } from 'react-i18next';
import InputField from './InputField';
import React from "react";
import { translate } from "react-i18next";
import InputField from "./InputField";
type State = {
password: string;
@@ -19,19 +19,19 @@ class PasswordConfirmation extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
password: '',
confirmedPassword: '',
password: "",
confirmedPassword: "",
passwordValid: true,
passwordConfirmationFailed: false,
passwordConfirmationFailed: false
};
}
componentDidMount() {
this.setState({
password: '',
confirmedPassword: '',
password: "",
confirmedPassword: "",
passwordValid: true,
passwordConfirmationFailed: false,
passwordConfirmationFailed: false
});
}
@@ -41,29 +41,29 @@ class PasswordConfirmation extends React.Component<Props, State> {
<div className="columns is-multiline">
<div className="column is-half">
<InputField
label={t('password.newPassword')}
label={t("password.newPassword")}
type="password"
onChange={this.handlePasswordChange}
value={this.state.password ? this.state.password : ''}
value={this.state.password ? this.state.password : ""}
validationError={!this.state.passwordValid}
errorMessage={t('password.passwordInvalid')}
errorMessage={t("password.passwordInvalid")}
/>
</div>
<div className="column is-half">
<InputField
label={t('password.confirmPassword')}
label={t("password.confirmPassword")}
type="password"
onChange={this.handlePasswordValidationChange}
value={this.state ? this.state.confirmedPassword : ''}
value={this.state ? this.state.confirmedPassword : ""}
validationError={this.state.passwordConfirmationFailed}
errorMessage={t('password.passwordConfirmFailed')}
errorMessage={t("password.passwordConfirmFailed")}
/>
</div>
</div>
);
}
validatePassword = password => {
validatePassword = (password: string) => {
const { passwordValidator } = this.props;
if (passwordValidator) {
return passwordValidator(password);
@@ -78,9 +78,9 @@ class PasswordConfirmation extends React.Component<Props, State> {
this.setState(
{
confirmedPassword,
passwordConfirmationFailed: !passwordConfirmed,
passwordConfirmationFailed: !passwordConfirmed
},
this.propagateChange,
this.propagateChange
);
};
@@ -92,9 +92,9 @@ class PasswordConfirmation extends React.Component<Props, State> {
{
passwordValid: this.validatePassword(password),
passwordConfirmationFailed,
password: password,
password: password
},
this.propagateChange,
this.propagateChange
);
};
@@ -107,4 +107,4 @@ class PasswordConfirmation extends React.Component<Props, State> {
};
}
export default translate('commons')(PasswordConfirmation);
export default translate("commons")(PasswordConfirmation);

View File

@@ -0,0 +1,21 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import Radio from "./Radio";
import styled from "styled-components";
const Spacing = styled.div`
padding: 2em;
`;
storiesOf("Forms|Radio", module)
.add("Default", () => (
<Spacing>
<Radio label="Not checked" checked={false} />
<Radio label="Checked" checked={true} />
</Spacing>
))
.add("Disabled", () => (
<Spacing>
<Radio label="Checked but disabled" checked={true} disabled={true} />
</Spacing>
));

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Help } from '../index';
import React, { ChangeEvent } from "react";
import { Help } from "../index";
type Props = {
label?: string;
@@ -19,20 +19,33 @@ class Radio extends React.Component<Props> {
}
};
onValueChange = (event: ChangeEvent<HTMLInputElement>) => {
if (this.props.onChange) {
this.props.onChange(event.target.checked, this.props.name);
}
};
render() {
return (
<label className="radio" disabled={this.props.disabled}>
<input
type="radio"
name={this.props.name}
value={this.props.value}
checked={this.props.checked}
onChange={this.props.onChange}
disabled={this.props.disabled}
/>{' '}
{this.props.label}
{this.renderHelp()}
</label>
<>
{/*
we have to ignore the next line,
because jsx label does not the custom disabled attribute
but bulma does.
// @ts-ignore */}
<label className="radio" disabled={this.props.disabled}>
<input
type="radio"
name={this.props.name}
value={this.props.value}
checked={this.props.checked}
onChange={this.onValueChange}
disabled={this.props.disabled}
/>{" "}
{this.props.label}
{this.renderHelp()}
</label>
</>
);
}
}

View File

@@ -1,6 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import LabelWithHelpIcon from './LabelWithHelpIcon';
import React, { ChangeEvent } from "react";
import classNames from "classnames";
import LabelWithHelpIcon from "./LabelWithHelpIcon";
export type SelectItem = {
value: string;
@@ -29,18 +29,18 @@ class Select extends React.Component<Props> {
}
}
handleInput = (event: SyntheticInputEvent<HTMLSelectElement>) => {
handleInput = (event: ChangeEvent<HTMLSelectElement>) => {
this.props.onChange(event.target.value, this.props.name);
};
render() {
const { options, value, label, helpText, loading, disabled } = this.props;
const loadingClass = loading ? 'is-loading' : '';
const loadingClass = loading ? "is-loading" : "";
return (
<div className="field">
<LabelWithHelpIcon label={label} helpText={helpText} />
<div className={classNames('control select', loadingClass)}>
<div className={classNames("control select", loadingClass)}>
<select
ref={input => {
this.field = input;
@@ -51,7 +51,7 @@ class Select extends React.Component<Props> {
>
{options.map(opt => {
return (
<option value={opt.value} key={'KEY_' + opt.value}>
<option value={opt.value} key={"KEY_" + opt.value}>
{opt.label}
</option>
);

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { DisplayedUser } from '@scm-manager/ui-types';
import { Help, Tag } from '../index';
import * as React from "react";
import { DisplayedUser } from "@scm-manager/ui-types";
import { Help, Tag } from "../index";
type Props = {
items: DisplayedUser[];
@@ -23,11 +23,11 @@ export default class TagGroup extends React.Component<Props> {
<strong>
{label}
{help}
{items.length > 0 ? ':' : ''}
{items.length > 0 ? ":" : ""}
</strong>
</div>
) : (
''
""
)}
{items.map((item, key) => {
return (
@@ -46,7 +46,7 @@ export default class TagGroup extends React.Component<Props> {
);
}
removeEntry = item => {
removeEntry = (item: DisplayedUser) => {
const newItems = this.props.items.filter(name => name !== item);
this.props.onRemove(newItems);
};

View File

@@ -1,15 +1,10 @@
import React from 'react';
import LabelWithHelpIcon from './LabelWithHelpIcon';
export type SelectItem = {
value: string;
label: string;
};
import React, { ChangeEvent } from "react";
import LabelWithHelpIcon from "./LabelWithHelpIcon";
type Props = {
name?: string;
label?: string;
placeholder?: SelectItem[];
placeholder?: string;
value?: string;
autofocus?: boolean;
onChange: (value: string, name?: string) => void;
@@ -26,7 +21,7 @@ class Textarea extends React.Component<Props> {
}
}
handleInput = (event: SyntheticInputEvent<HTMLTextAreaElement>) => {
handleInput = (event: ChangeEvent<HTMLTextAreaElement>) => {
this.props.onChange(event.target.value, this.props.name);
};

View File

@@ -1,17 +1,17 @@
// @create-index
export { default as AddEntryToTableField } from './AddEntryToTableField';
export { default as AddEntryToTableField } from "./AddEntryToTableField";
export {
default as AutocompleteAddEntryToTableField,
} from './AutocompleteAddEntryToTableField';
export { default as TagGroup } from './TagGroup';
export { default as MemberNameTagGroup } from './MemberNameTagGroup';
export { default as Checkbox } from './Checkbox';
export { default as Radio } from './Radio';
export { default as FilterInput } from './FilterInput';
export { default as InputField } from './InputField';
export { default as Select } from './Select';
export { default as Textarea } from './Textarea';
export { default as PasswordConfirmation } from './PasswordConfirmation';
export { default as LabelWithHelpIcon } from './LabelWithHelpIcon';
export { default as DropDown } from './DropDown';
default as AutocompleteAddEntryToTableField
} from "./AutocompleteAddEntryToTableField";
export { default as TagGroup } from "./TagGroup";
export { default as MemberNameTagGroup } from "./MemberNameTagGroup";
export { default as Checkbox } from "./Checkbox";
export { default as Radio } from "./Radio";
export { default as FilterInput } from "./FilterInput";
export { default as InputField } from "./InputField";
export { default as Select } from "./Select";
export { default as Textarea } from "./Textarea";
export { default as PasswordConfirmation } from "./PasswordConfirmation";
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon";
export { default as DropDown } from "./DropDown";