Merge branch 'showable-password' into 'develop'
Create ShowablePassword component See merge request soapbox-pub/soapbox-fe!916
This commit is contained in:
commit
7f3d7fbc29
|
@ -0,0 +1,64 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
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.svg') : require('@tabler/icons/icons/eye-off.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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -25,19 +25,48 @@ exports[`<LoginForm /> renders for Mastodon 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="input password user_password"
|
className="input required showable-password password user_password"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-label="Password"
|
aria-label="Password"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
className="password"
|
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
required={true}
|
required={true}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Show password"
|
||||||
|
className="icon-button"
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
onMouseDown={[Function]}
|
||||||
|
onMouseEnter={[Function]}
|
||||||
|
onMouseLeave={[Function]}
|
||||||
|
tabIndex="0"
|
||||||
|
title="Show password"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={Object {}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="svg-icon"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
id={
|
||||||
|
Object {
|
||||||
|
"process": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
className="hint subtle-hint"
|
className="hint subtle-hint"
|
||||||
|
@ -89,19 +118,48 @@ exports[`<LoginForm /> renders for Pleroma 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="input password user_password"
|
className="input required showable-password password user_password"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-label="Password"
|
aria-label="Password"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
className="password"
|
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
required={true}
|
required={true}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Show password"
|
||||||
|
className="icon-button"
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
onMouseDown={[Function]}
|
||||||
|
onMouseEnter={[Function]}
|
||||||
|
onMouseLeave={[Function]}
|
||||||
|
tabIndex="0"
|
||||||
|
title="Show password"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={Object {}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="svg-icon"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
id={
|
||||||
|
Object {
|
||||||
|
"process": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
className="hint subtle-hint"
|
className="hint subtle-hint"
|
||||||
|
|
|
@ -28,19 +28,48 @@ exports[`<LoginPage /> renders correctly on load 1`] = `
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="input password user_password"
|
className="input required showable-password password user_password"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-label="Password"
|
aria-label="Password"
|
||||||
autoCapitalize="off"
|
autoCapitalize="off"
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCorrect="off"
|
autoCorrect="off"
|
||||||
className="password"
|
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
required={true}
|
required={true}
|
||||||
type="password"
|
type="password"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
aria-label="Show password"
|
||||||
|
className="icon-button"
|
||||||
|
disabled={false}
|
||||||
|
onClick={[Function]}
|
||||||
|
onKeyDown={[Function]}
|
||||||
|
onKeyPress={[Function]}
|
||||||
|
onKeyUp={[Function]}
|
||||||
|
onMouseDown={[Function]}
|
||||||
|
onMouseEnter={[Function]}
|
||||||
|
onMouseLeave={[Function]}
|
||||||
|
tabIndex="0"
|
||||||
|
title="Show password"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={Object {}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="svg-icon"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
id={
|
||||||
|
Object {
|
||||||
|
"process": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p
|
<p
|
||||||
className="hint subtle-hint"
|
className="hint subtle-hint"
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { Link } from 'react-router-dom';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { getFeatures } from 'soapbox/utils/features';
|
import { getFeatures } from 'soapbox/utils/features';
|
||||||
import { getBaseURL } from 'soapbox/utils/state';
|
import { getBaseURL } from 'soapbox/utils/state';
|
||||||
|
import ShowablePassword from 'soapbox/components/showable_password';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
username: { id: 'login.fields.username_placeholder', defaultMessage: 'Username' },
|
username: { id: 'login.fields.username_placeholder', defaultMessage: 'Username' },
|
||||||
|
@ -45,19 +46,20 @@ class LoginForm extends ImmutablePureComponent {
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='input password user_password'>
|
<ShowablePassword
|
||||||
<input
|
|
||||||
aria-label={intl.formatMessage(messages.password)}
|
aria-label={intl.formatMessage(messages.password)}
|
||||||
className='password'
|
className='password user_password'
|
||||||
placeholder={intl.formatMessage(messages.password)}
|
placeholder={intl.formatMessage(messages.password)}
|
||||||
type='password'
|
|
||||||
name='password'
|
name='password'
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
autoCorrect='off'
|
autoCorrect='off'
|
||||||
autoCapitalize='off'
|
autoCapitalize='off'
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
{/* <div className='input password user_password'>
|
||||||
|
<input
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
<p className='hint subtle-hint'>
|
<p className='hint subtle-hint'>
|
||||||
{hasResetPasswordAPI ? (
|
{hasResetPasswordAPI ? (
|
||||||
<Link to='/auth/reset_password'>
|
<Link to='/auth/reset_password'>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import ShowablePassword from 'soapbox/components/showable_password';
|
||||||
import {
|
import {
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
SimpleInput,
|
SimpleInput,
|
||||||
|
@ -231,10 +232,9 @@ class RegistrationForm extends ImmutablePureComponent {
|
||||||
<FormattedMessage id='registration.password_mismatch' defaultMessage="Passwords don't match." />
|
<FormattedMessage id='registration.password_mismatch' defaultMessage="Passwords don't match." />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
placeholder={intl.formatMessage(messages.password)}
|
placeholder={intl.formatMessage(messages.password)}
|
||||||
name='password'
|
name='password'
|
||||||
type='password'
|
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
autoCorrect='off'
|
autoCorrect='off'
|
||||||
autoCapitalize='off'
|
autoCapitalize='off'
|
||||||
|
@ -243,10 +243,9 @@ class RegistrationForm extends ImmutablePureComponent {
|
||||||
error={passwordMismatch === true}
|
error={passwordMismatch === true}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
placeholder={intl.formatMessage(messages.confirm)}
|
placeholder={intl.formatMessage(messages.confirm)}
|
||||||
name='password_confirmation'
|
name='password_confirmation'
|
||||||
type='password'
|
|
||||||
autoComplete='off'
|
autoComplete='off'
|
||||||
autoCorrect='off'
|
autoCorrect='off'
|
||||||
autoCapitalize='off'
|
autoCapitalize='off'
|
||||||
|
|
|
@ -6,9 +6,9 @@ import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import Button from 'soapbox/components/button';
|
import Button from 'soapbox/components/button';
|
||||||
|
import ShowablePassword from 'soapbox/components/showable_password';
|
||||||
import {
|
import {
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
SimpleInput,
|
|
||||||
FieldsGroup,
|
FieldsGroup,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from 'soapbox/features/forms';
|
} from 'soapbox/features/forms';
|
||||||
|
@ -141,8 +141,7 @@ class ChangeEmailForm extends ImmutablePureComponent {
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
value={this.state.email}
|
value={this.state.email}
|
||||||
/>
|
/>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
label={intl.formatMessage(messages.passwordFieldLabel)}
|
label={intl.formatMessage(messages.passwordFieldLabel)}
|
||||||
name='password'
|
name='password'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
|
@ -208,22 +207,19 @@ class ChangePasswordForm extends ImmutablePureComponent {
|
||||||
<h2>{intl.formatMessage(messages.passwordHeader)}</h2>
|
<h2>{intl.formatMessage(messages.passwordHeader)}</h2>
|
||||||
<fieldset disabled={this.state.isLoading}>
|
<fieldset disabled={this.state.isLoading}>
|
||||||
<FieldsGroup>
|
<FieldsGroup>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
label={intl.formatMessage(messages.oldPasswordFieldLabel)}
|
label={intl.formatMessage(messages.oldPasswordFieldLabel)}
|
||||||
name='oldPassword'
|
name='oldPassword'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
value={this.state.oldPassword}
|
value={this.state.oldPassword}
|
||||||
/>
|
/>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
label={intl.formatMessage(messages.newPasswordFieldLabel)}
|
label={intl.formatMessage(messages.newPasswordFieldLabel)}
|
||||||
name='newPassword'
|
name='newPassword'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
value={this.state.newPassword}
|
value={this.state.newPassword}
|
||||||
/>
|
/>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
label={intl.formatMessage(messages.confirmationFieldLabel)}
|
label={intl.formatMessage(messages.confirmationFieldLabel)}
|
||||||
name='confirmation'
|
name='confirmation'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
|
@ -392,8 +388,7 @@ class DeactivateAccount extends ImmutablePureComponent {
|
||||||
</p>
|
</p>
|
||||||
<fieldset disabled={this.state.isLoading}>
|
<fieldset disabled={this.state.isLoading}>
|
||||||
<FieldsGroup>
|
<FieldsGroup>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
label={intl.formatMessage(messages.passwordFieldLabel)}
|
label={intl.formatMessage(messages.passwordFieldLabel)}
|
||||||
name='password'
|
name='password'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
|
|
|
@ -11,9 +11,9 @@ import LoadingIndicator from 'soapbox/components/loading_indicator';
|
||||||
import Button from 'soapbox/components/button';
|
import Button from 'soapbox/components/button';
|
||||||
import { changeSetting, getSettings } from 'soapbox/actions/settings';
|
import { changeSetting, getSettings } from 'soapbox/actions/settings';
|
||||||
import snackbar from 'soapbox/actions/snackbar';
|
import snackbar from 'soapbox/actions/snackbar';
|
||||||
|
import ShowablePassword from 'soapbox/components/showable_password';
|
||||||
import {
|
import {
|
||||||
SimpleForm,
|
SimpleForm,
|
||||||
SimpleInput,
|
|
||||||
FieldsGroup,
|
FieldsGroup,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from 'soapbox/features/forms';
|
} from 'soapbox/features/forms';
|
||||||
|
@ -144,8 +144,7 @@ class DisableOtpForm extends ImmutablePureComponent {
|
||||||
</h1>
|
</h1>
|
||||||
<div><FormattedMessage id='mfa.otp_enabled_description' defaultMessage='You have enabled two-factor authentication via OTP.' /></div>
|
<div><FormattedMessage id='mfa.otp_enabled_description' defaultMessage='You have enabled two-factor authentication via OTP.' /></div>
|
||||||
<div><FormattedMessage id='mfa.mfa_disable_enter_password' defaultMessage='Enter your current password to disable two-factor auth:' /></div>
|
<div><FormattedMessage id='mfa.mfa_disable_enter_password' defaultMessage='Enter your current password to disable two-factor auth:' /></div>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
name='password'
|
name='password'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
@ -313,8 +312,7 @@ class OtpConfirmForm extends ImmutablePureComponent {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div><FormattedMessage id='mfa.mfa_setup_enter_password' defaultMessage='Enter your current password to confirm your identity:' /></div>
|
<div><FormattedMessage id='mfa.mfa_setup_enter_password' defaultMessage='Enter your current password to confirm your identity:' /></div>
|
||||||
<SimpleInput
|
<ShowablePassword
|
||||||
type='password'
|
|
||||||
name='password'
|
name='password'
|
||||||
onChange={this.handleInputChange}
|
onChange={this.handleInputChange}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -424,6 +424,8 @@
|
||||||
"follow_request.authorize": "Autoryzuj",
|
"follow_request.authorize": "Autoryzuj",
|
||||||
"follow_request.reject": "Odrzuć",
|
"follow_request.reject": "Odrzuć",
|
||||||
"forms.copy": "Kopiuj",
|
"forms.copy": "Kopiuj",
|
||||||
|
"forms.hide_password": "Ukryj hasło",
|
||||||
|
"forms.show_password": "Pokaż hasło",
|
||||||
"getting_started.open_source_notice": "{code_name} jest oprogramowaniem o otwartym źródle. Możesz pomóc w rozwoju lub zgłaszać błędy na GitLabie tutaj: {code_link} (v{code_version}).",
|
"getting_started.open_source_notice": "{code_name} jest oprogramowaniem o otwartym źródle. Możesz pomóc w rozwoju lub zgłaszać błędy na GitLabie tutaj: {code_link} (v{code_version}).",
|
||||||
"group.detail.archived_group": "Zarchiwizowana grupa",
|
"group.detail.archived_group": "Zarchiwizowana grupa",
|
||||||
"group.members.empty": "Ta grupa nie ma żadnych członków.",
|
"group.members.empty": "Ta grupa nie ma żadnych członków.",
|
||||||
|
|
|
@ -97,6 +97,7 @@ $fluid-breakpoint: $maximum-width + 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input {
|
.input {
|
||||||
|
flex: 1;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
||||||
|
|
|
@ -608,6 +608,31 @@ code {
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.showable-password {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
padding-right: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 41px;
|
||||||
|
width: 36px;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
|
||||||
|
.svg-icon {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.block-icon {
|
.block-icon {
|
||||||
|
|
Loading…
Reference in New Issue