mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-16 02:06:18 +01:00
Improve modal accessibility
Implement initial focus for modals. Change all modals including forms to put initial focus on the first input. When Enter is pressed on any input (CTRL + Enter for Textareas), the form is submitted if it is valid. Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com> Co-authored-by: Eduard Heimbuch <eduard.heimbuch@cloudogu.com>
This commit is contained in:
committed by
GitHub
parent
d8fcb12402
commit
d0cf976a54
@@ -21,112 +21,78 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
import React from "react";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
import React, { FC, useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import InputField from "./InputField";
|
||||
|
||||
type State = {
|
||||
password: string;
|
||||
confirmedPassword: string;
|
||||
passwordValid: boolean;
|
||||
passwordConfirmationFailed: boolean;
|
||||
};
|
||||
type Props = WithTranslation & {
|
||||
type BaseProps = {
|
||||
passwordChanged: (p1: string, p2: boolean) => void;
|
||||
passwordValidator?: (p: string) => boolean;
|
||||
onReturnPressed?: () => void;
|
||||
};
|
||||
|
||||
class PasswordConfirmation extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
password: "",
|
||||
confirmedPassword: "",
|
||||
passwordValid: true,
|
||||
passwordConfirmationFailed: false,
|
||||
};
|
||||
}
|
||||
type InnerProps = BaseProps & {
|
||||
innerRef: React.Ref<HTMLInputElement>;
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({
|
||||
password: "",
|
||||
confirmedPassword: "",
|
||||
passwordValid: true,
|
||||
passwordConfirmationFailed: false,
|
||||
});
|
||||
}
|
||||
const PasswordConfirmation: FC<InnerProps> = ({ passwordChanged, passwordValidator, onReturnPressed, innerRef }) => {
|
||||
const [t] = useTranslation("commons");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmedPassword, setConfirmedPassword] = useState("");
|
||||
const [passwordValid, setPasswordValid] = useState(true);
|
||||
const [passwordConfirmationFailed, setPasswordConfirmationFailed] = useState(false);
|
||||
const isValid = passwordValid && !passwordConfirmationFailed;
|
||||
|
||||
render() {
|
||||
const { t, onReturnPressed } = this.props;
|
||||
return (
|
||||
<div className="columns is-multiline">
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.newPassword")}
|
||||
type="password"
|
||||
onChange={this.handlePasswordChange}
|
||||
value={this.state.password ? this.state.password : ""}
|
||||
validationError={!this.state.passwordValid}
|
||||
errorMessage={t("password.passwordInvalid")}
|
||||
/>
|
||||
</div>
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.confirmPassword")}
|
||||
type="password"
|
||||
onChange={this.handlePasswordValidationChange}
|
||||
value={this.state ? this.state.confirmedPassword : ""}
|
||||
validationError={this.state.passwordConfirmationFailed}
|
||||
errorMessage={t("password.passwordConfirmFailed")}
|
||||
onReturnPressed={onReturnPressed}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
useEffect(() => passwordChanged(password, isValid), [password, isValid]);
|
||||
|
||||
validatePassword = (password: string) => {
|
||||
const { passwordValidator } = this.props;
|
||||
const validatePassword = (newPassword: string) => {
|
||||
if (passwordValidator) {
|
||||
return passwordValidator(password);
|
||||
return passwordValidator(newPassword);
|
||||
}
|
||||
|
||||
return password.length >= 6 && password.length < 32;
|
||||
return newPassword.length >= 6 && newPassword.length < 32;
|
||||
};
|
||||
|
||||
handlePasswordValidationChange = (confirmedPassword: string) => {
|
||||
const passwordConfirmed = this.state.password === confirmedPassword;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
confirmedPassword,
|
||||
passwordConfirmationFailed: !passwordConfirmed,
|
||||
},
|
||||
this.propagateChange
|
||||
);
|
||||
const handlePasswordValidationChange = (newConfirmedPassword: string) => {
|
||||
setConfirmedPassword(newConfirmedPassword);
|
||||
setPasswordConfirmationFailed(password !== newConfirmedPassword);
|
||||
};
|
||||
|
||||
handlePasswordChange = (password: string) => {
|
||||
const passwordConfirmationFailed = password !== this.state.confirmedPassword;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
passwordValid: this.validatePassword(password),
|
||||
passwordConfirmationFailed,
|
||||
password: password,
|
||||
},
|
||||
this.propagateChange
|
||||
);
|
||||
const handlePasswordChange = (newPassword: string) => {
|
||||
setPasswordConfirmationFailed(newPassword !== confirmedPassword);
|
||||
setPassword(newPassword);
|
||||
setPasswordValid(validatePassword(newPassword));
|
||||
};
|
||||
|
||||
isValid = () => {
|
||||
return this.state.passwordValid && !this.state.passwordConfirmationFailed;
|
||||
};
|
||||
return (
|
||||
<div className="columns is-multiline">
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.newPassword")}
|
||||
type="password"
|
||||
onChange={event => handlePasswordChange(event.target.value)}
|
||||
value={password}
|
||||
validationError={!passwordValid}
|
||||
errorMessage={t("password.passwordInvalid")}
|
||||
ref={innerRef}
|
||||
onReturnPressed={onReturnPressed}
|
||||
/>
|
||||
</div>
|
||||
<div className="column is-half">
|
||||
<InputField
|
||||
label={t("password.confirmPassword")}
|
||||
type="password"
|
||||
onChange={handlePasswordValidationChange}
|
||||
value={confirmedPassword}
|
||||
validationError={passwordConfirmationFailed}
|
||||
errorMessage={t("password.passwordConfirmFailed")}
|
||||
onReturnPressed={onReturnPressed}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
propagateChange = () => {
|
||||
this.props.passwordChanged(this.state.password, this.isValid());
|
||||
};
|
||||
}
|
||||
|
||||
export default withTranslation("commons")(PasswordConfirmation);
|
||||
export default React.forwardRef<HTMLInputElement, BaseProps>((props, ref) => (
|
||||
<PasswordConfirmation {...props} innerRef={ref} />
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user