sforkowany z mirror/soapbox
Merge branch 'showable-password' into 'develop'
Create ShowablePassword component See merge request soapbox-pub/soapbox-fe!916strip-front-mentions
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
|
||||
className="input password user_password"
|
||||
className="input required showable-password password user_password"
|
||||
>
|
||||
<input
|
||||
aria-label="Password"
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
className="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
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>
|
||||
<p
|
||||
className="hint subtle-hint"
|
||||
|
@ -89,19 +118,48 @@ exports[`<LoginForm /> renders for Pleroma 1`] = `
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
className="input password user_password"
|
||||
className="input required showable-password password user_password"
|
||||
>
|
||||
<input
|
||||
aria-label="Password"
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
className="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
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>
|
||||
<p
|
||||
className="hint subtle-hint"
|
||||
|
|
|
@ -28,19 +28,48 @@ exports[`<LoginPage /> renders correctly on load 1`] = `
|
|||
/>
|
||||
</div>
|
||||
<div
|
||||
className="input password user_password"
|
||||
className="input required showable-password password user_password"
|
||||
>
|
||||
<input
|
||||
aria-label="Password"
|
||||
autoCapitalize="off"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
className="password"
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
required={true}
|
||||
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>
|
||||
<p
|
||||
className="hint subtle-hint"
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Link } from 'react-router-dom';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { getFeatures } from 'soapbox/utils/features';
|
||||
import { getBaseURL } from 'soapbox/utils/state';
|
||||
import ShowablePassword from 'soapbox/components/showable_password';
|
||||
|
||||
const messages = defineMessages({
|
||||
username: { id: 'login.fields.username_placeholder', defaultMessage: 'Username' },
|
||||
|
@ -45,19 +46,20 @@ class LoginForm extends ImmutablePureComponent {
|
|||
required
|
||||
/>
|
||||
</div>
|
||||
<div className='input password user_password'>
|
||||
<ShowablePassword
|
||||
aria-label={intl.formatMessage(messages.password)}
|
||||
className='password user_password'
|
||||
placeholder={intl.formatMessage(messages.password)}
|
||||
name='password'
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
autoCapitalize='off'
|
||||
required
|
||||
/>
|
||||
{/* <div className='input password user_password'>
|
||||
<input
|
||||
aria-label={intl.formatMessage(messages.password)}
|
||||
className='password'
|
||||
placeholder={intl.formatMessage(messages.password)}
|
||||
type='password'
|
||||
name='password'
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
autoCapitalize='off'
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
<p className='hint subtle-hint'>
|
||||
{hasResetPasswordAPI ? (
|
||||
<Link to='/auth/reset_password'>
|
||||
|
|
|
@ -5,6 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import { connect } from 'react-redux';
|
||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ShowablePassword from 'soapbox/components/showable_password';
|
||||
import {
|
||||
SimpleForm,
|
||||
SimpleInput,
|
||||
|
@ -231,10 +232,9 @@ class RegistrationForm extends ImmutablePureComponent {
|
|||
<FormattedMessage id='registration.password_mismatch' defaultMessage="Passwords don't match." />
|
||||
</div>
|
||||
)}
|
||||
<SimpleInput
|
||||
<ShowablePassword
|
||||
placeholder={intl.formatMessage(messages.password)}
|
||||
name='password'
|
||||
type='password'
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
autoCapitalize='off'
|
||||
|
@ -243,10 +243,9 @@ class RegistrationForm extends ImmutablePureComponent {
|
|||
error={passwordMismatch === true}
|
||||
required
|
||||
/>
|
||||
<SimpleInput
|
||||
<ShowablePassword
|
||||
placeholder={intl.formatMessage(messages.confirm)}
|
||||
name='password_confirmation'
|
||||
type='password'
|
||||
autoComplete='off'
|
||||
autoCorrect='off'
|
||||
autoCapitalize='off'
|
||||
|
|
|
@ -6,9 +6,9 @@ import PropTypes from 'prop-types';
|
|||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Column from '../ui/components/column';
|
||||
import Button from 'soapbox/components/button';
|
||||
import ShowablePassword from 'soapbox/components/showable_password';
|
||||
import {
|
||||
SimpleForm,
|
||||
SimpleInput,
|
||||
FieldsGroup,
|
||||
TextInput,
|
||||
} from 'soapbox/features/forms';
|
||||
|
@ -141,8 +141,7 @@ class ChangeEmailForm extends ImmutablePureComponent {
|
|||
onChange={this.handleInputChange}
|
||||
value={this.state.email}
|
||||
/>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
label={intl.formatMessage(messages.passwordFieldLabel)}
|
||||
name='password'
|
||||
onChange={this.handleInputChange}
|
||||
|
@ -208,22 +207,19 @@ class ChangePasswordForm extends ImmutablePureComponent {
|
|||
<h2>{intl.formatMessage(messages.passwordHeader)}</h2>
|
||||
<fieldset disabled={this.state.isLoading}>
|
||||
<FieldsGroup>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
label={intl.formatMessage(messages.oldPasswordFieldLabel)}
|
||||
name='oldPassword'
|
||||
onChange={this.handleInputChange}
|
||||
value={this.state.oldPassword}
|
||||
/>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
label={intl.formatMessage(messages.newPasswordFieldLabel)}
|
||||
name='newPassword'
|
||||
onChange={this.handleInputChange}
|
||||
value={this.state.newPassword}
|
||||
/>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
label={intl.formatMessage(messages.confirmationFieldLabel)}
|
||||
name='confirmation'
|
||||
onChange={this.handleInputChange}
|
||||
|
@ -392,8 +388,7 @@ class DeactivateAccount extends ImmutablePureComponent {
|
|||
</p>
|
||||
<fieldset disabled={this.state.isLoading}>
|
||||
<FieldsGroup>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
label={intl.formatMessage(messages.passwordFieldLabel)}
|
||||
name='password'
|
||||
onChange={this.handleInputChange}
|
||||
|
|
|
@ -11,9 +11,9 @@ import LoadingIndicator from 'soapbox/components/loading_indicator';
|
|||
import Button from 'soapbox/components/button';
|
||||
import { changeSetting, getSettings } from 'soapbox/actions/settings';
|
||||
import snackbar from 'soapbox/actions/snackbar';
|
||||
import ShowablePassword from 'soapbox/components/showable_password';
|
||||
import {
|
||||
SimpleForm,
|
||||
SimpleInput,
|
||||
FieldsGroup,
|
||||
TextInput,
|
||||
} from 'soapbox/features/forms';
|
||||
|
@ -144,8 +144,7 @@ class DisableOtpForm extends ImmutablePureComponent {
|
|||
</h1>
|
||||
<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>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
name='password'
|
||||
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>
|
||||
<SimpleInput
|
||||
type='password'
|
||||
<ShowablePassword
|
||||
name='password'
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
|
|
|
@ -424,6 +424,8 @@
|
|||
"follow_request.authorize": "Autoryzuj",
|
||||
"follow_request.reject": "Odrzuć",
|
||||
"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}).",
|
||||
"group.detail.archived_group": "Zarchiwizowana grupa",
|
||||
"group.members.empty": "Ta grupa nie ma żadnych członków.",
|
||||
|
|
|
@ -97,6 +97,7 @@ $fluid-breakpoint: $maximum-width + 20px;
|
|||
}
|
||||
|
||||
.input {
|
||||
flex: 1;
|
||||
margin-bottom: 0;
|
||||
margin-right: 10px;
|
||||
|
||||
|
|
|
@ -608,6 +608,31 @@ code {
|
|||
margin-bottom: 14px;
|
||||
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 {
|
||||
|
|
Ładowanie…
Reference in New Issue