Convert legacy forms to TypeScript
This commit is contained in:
parent
cee9d45b9d
commit
283213b5ba
6 changed files with 326 additions and 384 deletions
|
@ -1,65 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import { FormPropTypes, InputContainer, LabelInputContainer } from 'soapbox/features/forms';
|
||||
|
||||
const messages = defineMessages({
|
||||
showPassword: { id: 'forms.show_password', defaultMessage: 'Show password' },
|
||||
hidePassword: { id: 'forms.hide_password', defaultMessage: 'Hide password' },
|
||||
});
|
||||
|
||||
export default @injectIntl
|
||||
class ShowablePassword extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
label: FormPropTypes.label,
|
||||
className: PropTypes.string,
|
||||
hint: PropTypes.node,
|
||||
error: PropTypes.bool,
|
||||
}
|
||||
|
||||
state = {
|
||||
revealed: false,
|
||||
}
|
||||
|
||||
toggleReveal = () => {
|
||||
if (this.props.onToggleVisibility) {
|
||||
this.props.onToggleVisibility();
|
||||
} else {
|
||||
this.setState({ revealed: !this.state.revealed });
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, hint, error, label, className, ...props } = this.props;
|
||||
const { revealed } = this.state;
|
||||
|
||||
const revealButton = (
|
||||
<IconButton
|
||||
src={revealed ? require('@tabler/icons/icons/eye-off.svg') : require('@tabler/icons/icons/eye.svg')}
|
||||
onClick={this.toggleReveal}
|
||||
title={intl.formatMessage(revealed ? messages.hidePassword : messages.showPassword)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<InputContainer {...this.props} extraClass={classNames('showable-password', className)}>
|
||||
{label ? (
|
||||
<LabelInputContainer label={label}>
|
||||
<input {...props} type={revealed ? 'text' : 'password'} />
|
||||
{revealButton}
|
||||
</LabelInputContainer>
|
||||
) : (<>
|
||||
<input {...props} type={revealed ? 'text' : 'password'} />
|
||||
{revealButton}
|
||||
</>)}
|
||||
</InputContainer>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
58
app/soapbox/components/showable_password.tsx
Normal file
58
app/soapbox/components/showable_password.tsx
Normal file
|
@ -0,0 +1,58 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import IconButton from 'soapbox/components/icon_button';
|
||||
import { InputContainer, LabelInputContainer } from 'soapbox/features/forms';
|
||||
|
||||
const messages = defineMessages({
|
||||
showPassword: { id: 'forms.show_password', defaultMessage: 'Show password' },
|
||||
hidePassword: { id: 'forms.hide_password', defaultMessage: 'Hide password' },
|
||||
});
|
||||
|
||||
interface IShowablePassword {
|
||||
label?: React.ReactNode,
|
||||
className?: string,
|
||||
hint?: React.ReactNode,
|
||||
error?: boolean,
|
||||
onToggleVisibility?: () => void,
|
||||
}
|
||||
|
||||
const ShowablePassword: React.FC<IShowablePassword> = (props) => {
|
||||
const intl = useIntl();
|
||||
const [revealed, setRevealed] = useState(false);
|
||||
|
||||
const { hint, error, label, className, ...rest } = props;
|
||||
|
||||
const toggleReveal = () => {
|
||||
if (props.onToggleVisibility) {
|
||||
props.onToggleVisibility();
|
||||
} else {
|
||||
setRevealed(!revealed);
|
||||
}
|
||||
};
|
||||
|
||||
const revealButton = (
|
||||
<IconButton
|
||||
src={revealed ? require('@tabler/icons/icons/eye-off.svg') : require('@tabler/icons/icons/eye.svg')}
|
||||
onClick={toggleReveal}
|
||||
title={intl.formatMessage(revealed ? messages.hidePassword : messages.showPassword)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<InputContainer {...props} extraClass={classNames('showable-password', className)}>
|
||||
{label ? (
|
||||
<LabelInputContainer label={label}>
|
||||
<input {...rest} type={revealed ? 'text' : 'password'} />
|
||||
{revealButton}
|
||||
</LabelInputContainer>
|
||||
) : (<>
|
||||
<input {...rest} type={revealed ? 'text' : 'password'} />
|
||||
{revealButton}
|
||||
</>)}
|
||||
</InputContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShowablePassword;
|
|
@ -1,314 +0,0 @@
|
|||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useState } from 'react';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { Text, Select } from '../../components/ui';
|
||||
|
||||
export const FormPropTypes = {
|
||||
label: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.object,
|
||||
PropTypes.node,
|
||||
]),
|
||||
};
|
||||
|
||||
export const InputContainer = (props) => {
|
||||
const containerClass = classNames('input', {
|
||||
'with_label': props.label,
|
||||
'required': props.required,
|
||||
'boolean': props.type === 'checkbox',
|
||||
'field_with_errors': props.error,
|
||||
}, props.extraClass);
|
||||
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
{props.children}
|
||||
{props.hint && <span className='hint'>{props.hint}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
InputContainer.propTypes = {
|
||||
label: FormPropTypes.label,
|
||||
hint: PropTypes.node,
|
||||
required: PropTypes.bool,
|
||||
type: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
extraClass: PropTypes.string,
|
||||
error: PropTypes.bool,
|
||||
};
|
||||
|
||||
export const LabelInputContainer = ({ label, hint, children, ...props }) => {
|
||||
const [id] = useState(uuidv4());
|
||||
const childrenWithProps = React.Children.map(children, child => (
|
||||
React.cloneElement(child, { id: id, key: id })
|
||||
));
|
||||
|
||||
return (
|
||||
<div className='label_input'>
|
||||
<label htmlFor={id}>{label}</label>
|
||||
<div className='label_input__wrapper'>
|
||||
{childrenWithProps}
|
||||
</div>
|
||||
{hint && <span className='hint'>{hint}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
LabelInputContainer.propTypes = {
|
||||
label: FormPropTypes.label.isRequired,
|
||||
hint: PropTypes.node,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export const LabelInput = ({ label, dispatch, ...props }) => (
|
||||
<LabelInputContainer label={label}>
|
||||
<input {...props} />
|
||||
</LabelInputContainer>
|
||||
);
|
||||
|
||||
LabelInput.propTypes = {
|
||||
label: FormPropTypes.label.isRequired,
|
||||
dispatch: PropTypes.func,
|
||||
};
|
||||
|
||||
export const LabelTextarea = ({ label, dispatch, ...props }) => (
|
||||
<LabelInputContainer label={label}>
|
||||
<textarea {...props} />
|
||||
</LabelInputContainer>
|
||||
);
|
||||
|
||||
LabelTextarea.propTypes = {
|
||||
label: FormPropTypes.label.isRequired,
|
||||
dispatch: PropTypes.func,
|
||||
};
|
||||
|
||||
export class SimpleInput extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
label: FormPropTypes.label,
|
||||
hint: PropTypes.node,
|
||||
error: PropTypes.bool,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hint, error, ...props } = this.props;
|
||||
const Input = this.props.label ? LabelInput : 'input';
|
||||
|
||||
return (
|
||||
<InputContainer {...this.props}>
|
||||
<Input {...props} />
|
||||
</InputContainer>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class SimpleTextarea extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
label: FormPropTypes.label,
|
||||
hint: PropTypes.node,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { hint, ...props } = this.props;
|
||||
const Input = this.props.label ? LabelTextarea : 'textarea';
|
||||
|
||||
return (
|
||||
<InputContainer {...this.props}>
|
||||
<Input {...props} />
|
||||
</InputContainer>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class SimpleForm extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
acceptCharset: 'UTF-8',
|
||||
onSubmit: e => {},
|
||||
};
|
||||
|
||||
onSubmit = e => {
|
||||
this.props.onSubmit(e);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, children, onSubmit, ...props } = this.props;
|
||||
return (
|
||||
<form
|
||||
className={classNames('simple_form', className)}
|
||||
method='post'
|
||||
onSubmit={this.onSubmit}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const FieldsGroup = ({ children }) => (
|
||||
<div className='fields-group'>{children}</div>
|
||||
);
|
||||
|
||||
FieldsGroup.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
export const Checkbox = props => (
|
||||
<SimpleInput type='checkbox' {...props} />
|
||||
);
|
||||
|
||||
export class RadioGroup extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
label: FormPropTypes.label,
|
||||
children: PropTypes.node,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { label, children, onChange } = this.props;
|
||||
|
||||
const childrenWithProps = React.Children.map(children, child =>
|
||||
React.cloneElement(child, { onChange }),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='input with_floating_label radio_buttons'>
|
||||
<div className='label_input'>
|
||||
<label>{label}</label>
|
||||
<ul>{childrenWithProps}</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class RadioItem extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
label: FormPropTypes.label,
|
||||
hint: PropTypes.node,
|
||||
value: PropTypes.string.isRequired,
|
||||
checked: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func,
|
||||
dispatch: PropTypes.func,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
checked: false,
|
||||
}
|
||||
|
||||
state = {
|
||||
id: uuidv4(),
|
||||
}
|
||||
|
||||
render() {
|
||||
const { label, hint, dispatch, ...props } = this.props;
|
||||
const { id } = this.state;
|
||||
|
||||
return (
|
||||
<li className='radio'>
|
||||
<label htmlFor={id}>
|
||||
<input id={id} type='radio' {...props} />
|
||||
<Text>{label}</Text>
|
||||
{hint && <span className='hint'>{hint}</span>}
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class SelectDropdown extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
label: FormPropTypes.label,
|
||||
hint: PropTypes.node,
|
||||
items: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { label, hint, items, ...props } = this.props;
|
||||
|
||||
const optionElems = Object.keys(items).map(item => (
|
||||
<option key={item} value={item}>{items[item]}</option>
|
||||
));
|
||||
|
||||
const selectElem = <Select {...props}>{optionElems}</Select>;
|
||||
|
||||
return label ? (
|
||||
<LabelInputContainer label={label} hint={hint}>{selectElem}</LabelInputContainer>
|
||||
) : selectElem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const TextInput = props => (
|
||||
<SimpleInput type='text' {...props} />
|
||||
);
|
||||
|
||||
export const FileChooser = props => (
|
||||
<SimpleInput type='file' {...props} />
|
||||
);
|
||||
|
||||
FileChooser.defaultProps = {
|
||||
accept: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
|
||||
};
|
||||
|
||||
export const FileChooserLogo = props => (
|
||||
<SimpleInput type='file' {...props} />
|
||||
);
|
||||
|
||||
FileChooserLogo.defaultProps = {
|
||||
accept: ['image/svg', 'image/png'],
|
||||
};
|
||||
|
||||
|
||||
export class CopyableInput extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
}
|
||||
|
||||
setInputRef = c => {
|
||||
this.input = c;
|
||||
}
|
||||
|
||||
handleCopyClick = e => {
|
||||
if (!this.input) return;
|
||||
|
||||
this.input.select();
|
||||
this.input.setSelectionRange(0, 99999);
|
||||
|
||||
document.execCommand('copy');
|
||||
}
|
||||
|
||||
render() {
|
||||
const { value } = this.props;
|
||||
|
||||
return (
|
||||
<div className='copyable-input'>
|
||||
<input ref={this.setInputRef} type='text' value={value} readOnly />
|
||||
<button className='p-2 text-white bg-primary-600' onClick={this.handleCopyClick}>
|
||||
<FormattedMessage id='forms.copy' defaultMessage='Copy' />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
264
app/soapbox/features/forms/index.tsx
Normal file
264
app/soapbox/features/forms/index.tsx
Normal file
|
@ -0,0 +1,264 @@
|
|||
import classNames from 'classnames';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { Text, Select } from '../../components/ui';
|
||||
|
||||
interface IInputContainer {
|
||||
label?: React.ReactNode,
|
||||
hint?: React.ReactNode,
|
||||
required?: boolean,
|
||||
type?: string,
|
||||
extraClass?: string,
|
||||
error?: boolean,
|
||||
}
|
||||
|
||||
export const InputContainer: React.FC<IInputContainer> = (props) => {
|
||||
const containerClass = classNames('input', {
|
||||
'with_label': props.label,
|
||||
'required': props.required,
|
||||
'boolean': props.type === 'checkbox',
|
||||
'field_with_errors': props.error,
|
||||
}, props.extraClass);
|
||||
|
||||
return (
|
||||
<div className={containerClass}>
|
||||
{props.children}
|
||||
{props.hint && <span className='hint'>{props.hint}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ILabelInputContainer {
|
||||
label?: React.ReactNode,
|
||||
hint?: React.ReactNode,
|
||||
}
|
||||
|
||||
export const LabelInputContainer: React.FC<ILabelInputContainer> = ({ label, hint, children }) => {
|
||||
const [id] = useState(uuidv4());
|
||||
const childrenWithProps = React.Children.map(children, child => (
|
||||
// @ts-ignore: not sure how to get the right type here
|
||||
React.cloneElement(child, { id: id, key: id })
|
||||
));
|
||||
|
||||
return (
|
||||
<div className='label_input'>
|
||||
<label htmlFor={id}>{label}</label>
|
||||
<div className='label_input__wrapper'>
|
||||
{childrenWithProps}
|
||||
</div>
|
||||
{hint && <span className='hint'>{hint}</span>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ILabelInput {
|
||||
label?: React.ReactNode,
|
||||
}
|
||||
|
||||
export const LabelInput: React.FC<ILabelInput> = ({ label, ...props }) => (
|
||||
<LabelInputContainer label={label}>
|
||||
<input {...props} />
|
||||
</LabelInputContainer>
|
||||
);
|
||||
|
||||
interface ILabelTextarea {
|
||||
label?: React.ReactNode,
|
||||
}
|
||||
|
||||
export const LabelTextarea: React.FC<ILabelTextarea> = ({ label, ...props }) => (
|
||||
<LabelInputContainer label={label}>
|
||||
<textarea {...props} />
|
||||
</LabelInputContainer>
|
||||
);
|
||||
|
||||
interface ISimpleInput {
|
||||
type: string,
|
||||
label?: React.ReactNode,
|
||||
hint?: React.ReactNode,
|
||||
error?: boolean,
|
||||
}
|
||||
|
||||
export const SimpleInput: React.FC<ISimpleInput> = (props) => {
|
||||
const { hint, label, error, ...rest } = props;
|
||||
const Input = label ? LabelInput : 'input';
|
||||
|
||||
return (
|
||||
<InputContainer {...props}>
|
||||
<Input {...rest} />
|
||||
</InputContainer>
|
||||
);
|
||||
};
|
||||
|
||||
interface ISimpleTextarea {
|
||||
label?: React.ReactNode,
|
||||
hint?: React.ReactNode,
|
||||
}
|
||||
|
||||
export const SimpleTextarea: React.FC<ISimpleTextarea> = (props) => {
|
||||
const { hint, label, ...rest } = props;
|
||||
const Input = label ? LabelTextarea : 'textarea';
|
||||
|
||||
return (
|
||||
<InputContainer {...props}>
|
||||
<Input {...rest} />
|
||||
</InputContainer>
|
||||
);
|
||||
};
|
||||
|
||||
interface ISimpleForm {
|
||||
className?: string,
|
||||
onSubmit?: React.FormEventHandler,
|
||||
acceptCharset?: string,
|
||||
}
|
||||
|
||||
export const SimpleForm: React.FC<ISimpleForm> = (props) => {
|
||||
const {
|
||||
className,
|
||||
children,
|
||||
onSubmit = () => {},
|
||||
acceptCharset = 'UTF-8',
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const handleSubmit: React.FormEventHandler = e => {
|
||||
onSubmit(e);
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
return (
|
||||
<form
|
||||
className={classNames('simple_form', className)}
|
||||
method='post'
|
||||
onSubmit={handleSubmit}
|
||||
acceptCharset={acceptCharset}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export const FieldsGroup: React.FC = ({ children }) => (
|
||||
<div className='fields-group'>{children}</div>
|
||||
);
|
||||
|
||||
export const Checkbox: React.FC = (props) => (
|
||||
<SimpleInput type='checkbox' {...props} />
|
||||
);
|
||||
|
||||
interface IRadioGroup {
|
||||
label?: React.ReactNode,
|
||||
onChange?: React.ChangeEventHandler,
|
||||
}
|
||||
|
||||
export const RadioGroup: React.FC<IRadioGroup> = (props) => {
|
||||
const { label, children, onChange } = props;
|
||||
|
||||
const childrenWithProps = React.Children.map(children, child =>
|
||||
// @ts-ignore
|
||||
React.cloneElement(child, { onChange }),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='input with_floating_label radio_buttons'>
|
||||
<div className='label_input'>
|
||||
<label>{label}</label>
|
||||
<ul>{childrenWithProps}</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface IRadioItem {
|
||||
label?: React.ReactNode,
|
||||
hint?: React.ReactNode,
|
||||
value: string,
|
||||
checked: boolean,
|
||||
onChange?: React.ChangeEventHandler,
|
||||
}
|
||||
|
||||
export const RadioItem: React.FC<IRadioItem> = (props) => {
|
||||
const { current: id } = useRef<string>(uuidv4());
|
||||
const { label, hint, checked = false, ...rest } = props;
|
||||
|
||||
return (
|
||||
<li className='radio'>
|
||||
<label htmlFor={id}>
|
||||
<input id={id} type='radio' checked={checked} {...rest} />
|
||||
<Text>{label}</Text>
|
||||
{hint && <span className='hint'>{hint}</span>}
|
||||
</label>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
interface ISelectDropdown {
|
||||
label?: React.ReactNode,
|
||||
hint?: React.ReactNode,
|
||||
items: Record<string, string>,
|
||||
defaultValue?: string,
|
||||
onChange?: React.ChangeEventHandler,
|
||||
}
|
||||
|
||||
export const SelectDropdown: React.FC<ISelectDropdown> = (props) => {
|
||||
const { label, hint, items, ...rest } = props;
|
||||
|
||||
const optionElems = Object.keys(items).map(item => (
|
||||
<option key={item} value={item}>{items[item]}</option>
|
||||
));
|
||||
|
||||
// @ts-ignore
|
||||
const selectElem = <Select {...rest}>{optionElems}</Select>;
|
||||
|
||||
return label ? (
|
||||
<LabelInputContainer label={label} hint={hint}>{selectElem}</LabelInputContainer>
|
||||
) : selectElem;
|
||||
};
|
||||
|
||||
export const TextInput: React.FC = props => (
|
||||
<SimpleInput type='text' {...props} />
|
||||
);
|
||||
|
||||
export const FileChooser : React.FC = (props) => (
|
||||
<SimpleInput type='file' {...props} />
|
||||
);
|
||||
|
||||
FileChooser.defaultProps = {
|
||||
accept: ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
|
||||
};
|
||||
|
||||
export const FileChooserLogo: React.FC = props => (
|
||||
<SimpleInput type='file' {...props} />
|
||||
);
|
||||
|
||||
FileChooserLogo.defaultProps = {
|
||||
accept: ['image/svg', 'image/png'],
|
||||
};
|
||||
|
||||
interface ICopyableInput {
|
||||
value: string,
|
||||
}
|
||||
|
||||
export const CopyableInput: React.FC<ICopyableInput> = ({ value }) => {
|
||||
const node = useRef<HTMLInputElement>(null);
|
||||
|
||||
const handleCopyClick: React.MouseEventHandler = () => {
|
||||
if (!node.current) return;
|
||||
|
||||
node.current.select();
|
||||
node.current.setSelectionRange(0, 99999);
|
||||
|
||||
document.execCommand('copy');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='copyable-input'>
|
||||
<input ref={node} type='text' value={value} readOnly />
|
||||
<button className='p-2 text-white bg-primary-600' onClick={handleCopyClick}>
|
||||
<FormattedMessage id='forms.copy' defaultMessage='Copy' />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -134,7 +134,7 @@ const Preferences = () => {
|
|||
<ListItem label={<FormattedMessage id='preferences.fields.language_label' defaultMessage='Language' />}>
|
||||
<SelectDropdown
|
||||
items={languages}
|
||||
defaultValue={settings.get('locale')}
|
||||
defaultValue={settings.get('locale') as string | undefined}
|
||||
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => onSelectChange(event, ['locale'])}
|
||||
/>
|
||||
</ListItem>
|
||||
|
@ -142,7 +142,7 @@ const Preferences = () => {
|
|||
<ListItem label={<FormattedMessage id='preferences.fields.media_display_label' defaultMessage='Media display' />}>
|
||||
<SelectDropdown
|
||||
items={displayMediaOptions}
|
||||
defaultValue={settings.get('displayMedia')}
|
||||
defaultValue={settings.get('displayMedia') as string | undefined}
|
||||
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => onSelectChange(event, ['displayMedia'])}
|
||||
/>
|
||||
</ListItem>
|
||||
|
|
|
@ -21,7 +21,6 @@ import {
|
|||
SimpleInput,
|
||||
SimpleTextarea,
|
||||
FileChooserLogo,
|
||||
FormPropTypes,
|
||||
Checkbox,
|
||||
} from 'soapbox/features/forms';
|
||||
import ThemeToggle from 'soapbox/features/ui/components/theme_toggle';
|
||||
|
@ -509,7 +508,7 @@ class ColorWithPicker extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
buttonId: PropTypes.string.isRequired,
|
||||
label: FormPropTypes.label,
|
||||
label: PropTypes.node,
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
@ -559,7 +558,7 @@ export class IconPicker extends ImmutablePureComponent {
|
|||
|
||||
static propTypes = {
|
||||
icons: PropTypes.object,
|
||||
label: FormPropTypes.label,
|
||||
label: PropTypes.node,
|
||||
value: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue