Improved UX of signing pages (#5295)

main
Kirill Lakhov 3 years ago committed by GitHub
parent e2d9860259
commit 7706eee504
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Mask tools are supported now (brush, eraser, polygon-plus, polygon-minus, returning masks
from online detectors & interactors) (<https://github.com/opencv/cvat/pull/4543>)
- Added Webhooks (<https://github.com/opencv/cvat/pull/4863>)
- Authentication with social accounts google & github (<https://github.com/opencv/cvat/pull/5147>, <https://github.com/opencv/cvat/pull/5181>)
- Authentication with social accounts google & github (<https://github.com/opencv/cvat/pull/5147>, <https://github.com/opencv/cvat/pull/5181>, <https://github.com/opencv/cvat/pull/5295>)
- REST API tests to export job datasets & annotations and validate their structure (<https://github.com/opencv/cvat/pull/5160>)
### Changed

@ -1,6 +1,6 @@
{
"name": "cvat-ui",
"version": "1.44.0",
"version": "1.44.1",
"description": "CVAT single-page application",
"main": "src/index.tsx",
"scripts": {

@ -1,4 +1,4 @@
<svg width="80" height="15" viewBox="0 0 80 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="64" height="12" viewBox="0 0 80 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.63399 15C2.37083 15 0 12.5333 0 7.48889C0 2.46667 2.34908 0 6.63399 0H10.6576V2.62222H6.63399C3.78464 2.62222 2.56659 4.06667 2.56659 7.48889C2.56659 10.9333 3.80639 12.3778 6.63399 12.3778H15.0638V15H6.63399Z" fill="white"/>
<path d="M22.412 0L27.2466 13.1004C27.7814 14.5415 28.8724 15 30.2842 15C31.7817 15 32.7657 14.476 33.3005 13.1004L38.3918 0H35.5895L30.9902 11.8559C30.8404 12.2052 30.6265 12.3581 30.2842 12.3581C29.942 12.3581 29.7067 12.2052 29.5783 11.8559L25.1929 0H22.412Z" fill="white"/>
<path d="M53.0989 3.1441C53.2487 2.79476 53.484 2.64192 53.8262 2.64192C54.1685 2.64192 54.4038 2.79476 54.5322 3.1441L58.9176 15H61.7199L56.8425 1.89956C56.3291 0.524017 55.3451 0 53.8476 0C52.3288 0 51.3448 0.524017 50.81 1.89956L45.74 15H48.521L53.0989 3.1441Z" fill="white"/>

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 981 B

@ -29,6 +29,12 @@
}
}
.anticon.cvat-logo-icon {
> svg {
transform: scale(0.7);
}
}
width: 50%;
display: flex;
justify-content: flex-start;
@ -49,12 +55,6 @@
}
}
.anticon.cvat-logo-icon {
> svg {
transform: scale(0.7);
}
}
.cvat-header-menu-user-dropdown {
display: flex;
align-items: center;

@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT
import React, { useCallback, useState } from 'react';
import React, { useState } from 'react';
import Form from 'antd/lib/form';
import Button from 'antd/lib/button';
import Input from 'antd/lib/input';
@ -15,6 +15,7 @@ import { Col, Row } from 'antd/lib/grid';
import Title from 'antd/lib/typography/Title';
import Text from 'antd/lib/typography/Text';
import { Link } from 'react-router-dom';
import CVATSigningInput, { CVATInputType } from 'components/signing-common/cvat-signing-input';
export interface LoginData {
credential: string;
@ -35,9 +36,6 @@ function LoginFormComponent(props: Props): JSX.Element {
const [form] = Form.useForm();
const [credential, setCredential] = useState('');
const inputReset = useCallback((name: string):void => {
form.setFieldsValue({ [name]: '' });
}, [form]);
const forgotPasswordLink = (
<Col className='cvat-credentials-link'>
<Text strong>
@ -97,23 +95,21 @@ function LoginFormComponent(props: Props): JSX.Element {
>
<Input
autoComplete='credential'
placeholder='enter your email or username'
prefix={<Text>Email or username</Text>}
suffix={(
credential && (
<Icon
component={ClearIcon}
onClick={() => {
setCredential('');
inputReset('credential');
}}
/>
)
className={credential ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
suffix={credential && (
<Icon
component={ClearIcon}
onClick={() => {
setCredential('');
form.setFieldsValue({ credential: '', password: '' });
}}
/>
)}
onChange={(event) => {
const { value } = event.target;
setCredential(value);
if (!value) inputReset('credential');
if (!value) form.setFieldsValue({ credential: '', password: '' });
}}
/>
</Form.Item>
@ -129,12 +125,11 @@ function LoginFormComponent(props: Props): JSX.Element {
},
]}
>
<Input.Password
autoComplete='current-password'
placeholder='enter your password'
prefix={
<Text>Password</Text>
}
<CVATSigningInput
type={CVATInputType.PASSWORD}
id='password'
placeholder='Password'
autoComplete='password'
/>
</Form.Item>
)

@ -3,12 +3,10 @@
//
// SPDX-License-Identifier: MIT
import React, { useCallback, useState } from 'react';
import React, { useState } from 'react';
import Icon from '@ant-design/icons';
import Form, { RuleRender, RuleObject } from 'antd/lib/form';
import Button from 'antd/lib/button';
import Input from 'antd/lib/input';
import Text from 'antd/lib/typography/Text';
import Checkbox from 'antd/lib/checkbox';
import { Link } from 'react-router-dom';
import { BackArrowIcon } from 'icons';
@ -17,7 +15,7 @@ import patterns from 'utils/validation-patterns';
import { UserAgreement } from 'reducers';
import { Row, Col } from 'antd/lib/grid';
import CVATSigningInput from 'components/signing-common/cvat-signing-input';
import CVATSigningInput, { CVATInputType } from 'components/signing-common/cvat-signing-input';
export interface UserConfirmation {
name: string;
@ -104,9 +102,6 @@ function RegisterFormComponent(props: Props): JSX.Element {
const { fetching, onSubmit, userAgreements } = props;
const [form] = Form.useForm();
const [usernameEdited, setUsernameEdited] = useState(false);
const inputReset = useCallback((name: string):void => {
form.setFieldsValue({ [name]: '' });
}, [form]);
return (
<div className={`cvat-register-form-wrapper ${userAgreements.length ? 'cvat-register-form-wrapper-extended' : ''}`}>
<Row justify='space-between' className='cvat-credentials-navigation'>
@ -146,9 +141,9 @@ function RegisterFormComponent(props: Props): JSX.Element {
>
<CVATSigningInput
id='firstName'
placeholder='enter your first name'
prefix='First name'
onReset={() => inputReset('firstName')}
placeholder='First name'
autoComplete='given-name'
onReset={() => form.setFieldsValue({ firstName: '' })}
/>
</Form.Item>
</Col>
@ -166,9 +161,9 @@ function RegisterFormComponent(props: Props): JSX.Element {
>
<CVATSigningInput
id='lastName'
placeholder='enter your last name'
prefix='Last name'
onReset={() => inputReset('lastName')}
placeholder='Last name'
autoComplete='family-name'
onReset={() => form.setFieldsValue({ lastName: '' })}
/>
</Form.Item>
</Col>
@ -190,9 +185,8 @@ function RegisterFormComponent(props: Props): JSX.Element {
<CVATSigningInput
id='email'
autoComplete='email'
placeholder='enter your email'
prefix='Email'
onReset={() => inputReset('email')}
placeholder='Email'
onReset={() => form.setFieldsValue({ email: '', username: '' })}
onChange={(event) => {
const { value } = event.target;
if (!usernameEdited) {
@ -217,9 +211,9 @@ function RegisterFormComponent(props: Props): JSX.Element {
>
<CVATSigningInput
id='username'
placeholder='enter your username'
prefix='Username'
onReset={() => inputReset('username')}
placeholder='Username'
autoComplete='username'
onReset={() => form.setFieldsValue({ username: '' })}
onChange={() => setUsernameEdited(true)}
/>
</Form.Item>
@ -233,12 +227,13 @@ function RegisterFormComponent(props: Props): JSX.Element {
}, validatePassword,
]}
>
<Input.Password
placeholder='enter your password'
prefix={<Text>Password</Text>}
<CVATSigningInput
type={CVATInputType.PASSWORD}
id='password1'
placeholder='Password'
autoComplete='new-password'
/>
</Form.Item>
{userAgreements.map((userAgreement: UserAgreement): JSX.Element => (
<Form.Item
className='cvat-agreements-form-item'

@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT
import React, { useCallback } from 'react';
import React from 'react';
import Form from 'antd/lib/form';
import Button from 'antd/lib/button';
import Icon from '@ant-design/icons';
@ -28,9 +28,6 @@ function ResetPasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element
const location = useLocation();
const params = new URLSearchParams(location.search);
const defaultCredential = params.get('credential');
const inputReset = useCallback((name: string):void => {
form.setFieldsValue({ [name]: '' });
}, [form]);
return (
<div className='cvat-password-reset-form-wrapper'>
<Row justify='space-between' className='cvat-credentials-navigation'>
@ -74,9 +71,8 @@ function ResetPasswordFormComponent({ fetching, onSubmit }: Props): JSX.Element
>
<CVATSigningInput
autoComplete='email'
placeholder='enter your email'
prefix='Email'
onReset={() => inputReset('email')}
placeholder='Email'
onReset={() => form.setFieldsValue({ email: '' })}
/>
</Form.Item>
<Row>

@ -1,50 +1,65 @@
// Copyright (C) 2022 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import Icon from '@ant-design/icons';
import Text from 'antd/lib/typography/Text';
import { ClearIcon } from 'icons';
import { Input } from 'antd';
import Text from 'antd/lib/typography/Text';
interface SocialAccountLinkProps {
id?: string;
prefix: string;
autoComplete?: string;
placeholder: string;
value?: string;
onReset: () => void;
type?: CVATInputType;
onReset?: () => void;
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
export enum CVATInputType {
TEXT = 'text',
PASSWORD = 'passord',
}
function CVATSigningInput(props: SocialAccountLinkProps): JSX.Element {
const {
id, prefix, autoComplete, onReset, placeholder, value, onChange,
id, autoComplete, type, onReset, placeholder, value, onChange,
} = props;
const [valueNonEmpty, setValueNonEmpty] = useState(false);
useEffect((): void => {
setValueNonEmpty(!!value);
}, [value]);
if (type === CVATInputType.PASSWORD) {
return (
<Input.Password
value={value}
autoComplete={autoComplete}
className={valueNonEmpty ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
prefix={<Text>{placeholder}</Text>}
id={id}
onChange={onChange}
/>
);
}
return (
<Input
value={value}
autoComplete={autoComplete}
placeholder={placeholder}
prefix={<Text>{prefix}</Text>}
className={valueNonEmpty ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
prefix={<Text>{placeholder}</Text>}
id={id}
suffix={(
valueNonEmpty ? (
<Icon
component={ClearIcon}
onClick={() => {
setValueNonEmpty(false);
onReset();
}}
/>
) : null
suffix={valueNonEmpty && (
<Icon
component={ClearIcon}
onClick={() => {
setValueNonEmpty(false);
if (onReset) onReset();
}}
/>
)}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
const { value: inputValue } = event.target;
setValueNonEmpty(!!inputValue);
if (onChange) onChange(event);
}}
onChange={onChange}
/>
);
}

@ -59,11 +59,23 @@ function SignInLayout(props: SignInLayoutComponentProps): JSX.Element {
xl: { span: 8 },
xxl: { span: 10 },
};
const logoSizes = {
xs: { span: 21 },
sm: { span: 21 },
md: { span: 21 },
lg: { span: 21 },
xl: { span: 21 },
xxl: { span: 22 },
};
return (
<Layout>
<SVGSigningBackground className='cvat-signing-background' />
<Header className='cvat-signing-header'>
<Icon className='cvat-logo-icon' component={CVATMinimalisticLogo} />
<Row justify='center' align='middle'>
<Col {...logoSizes}>
<Icon className='cvat-logo-icon' component={CVATMinimalisticLogo} />
</Col>
</Row>
</Header>
<Layout className='cvat-signing-layout'>
<Content>

@ -11,8 +11,10 @@ $error-color: #ff4d4f;
$action-button-color-1: black;
$action-button-color-2: gray;
$action-button-color-3: #d4d4d4;
$placeholder-color: #c5bfbf;
$base-transition: all 0.8s ease;
$input-transition: all 0.3s ease;
$social-google-background: #4286f5;
@ -36,7 +38,13 @@ $social-google-background: #4286f5;
.cvat-signing-header {
background: transparent;
position: absolute;
position: fixed;
padding: $grid-unit-size*2 0 0 0;
width: 100%;
svg {
transform: none;
}
}
.cvat-signing-background {
@ -60,6 +68,31 @@ $social-google-background: #4286f5;
}
}
.cvat-input-floating-label {
.ant-input-prefix {
position: absolute;
top: 50%;
transform: translateY(-50%);
z-index: 1;
font-size: 20px;
transition: $input-transition;
transform-origin: left top;
.ant-typography {
color: $placeholder-color;
}
}
}
.cvat-input-floating-label-above {
@extend .cvat-input-floating-label;
.ant-input-prefix {
transition: $input-transition;
transform: translateY(-100%) scale(0.7);
}
}
.cvat-credentials-form-item {
.ant-input-affix-wrapper {
border: none;
@ -69,12 +102,9 @@ $social-google-background: #4286f5;
transition: $base-transition;
}
.ant-input-prefix {
position: absolute;
z-index: 1;
}
.ant-input-affix-wrapper-focused {
@extend .cvat-input-floating-label-above;
box-shadow: 0 2px 0 0 black;
}
@ -94,11 +124,8 @@ $social-google-background: #4286f5;
}
}
.ant-typography {
text-transform: uppercase;
color: $border-color-1;
font-size: 12px;
letter-spacing: 1px;
.cvat-signing-input-not-empty {
@extend .cvat-input-floating-label;
}
}
@ -322,8 +349,8 @@ $social-google-background: #4286f5;
}
letter-spacing: 1px;
padding: 0 $grid-unit-size*4 !important;
margin-bottom: $grid-unit-size;
padding: $grid-unit-size $grid-unit-size*4;
display: flex;
}

@ -12,8 +12,8 @@ context('When clicking on the Logout button, get the user session closed.', () =
let taskId;
function login(credential, password) {
cy.get('[placeholder="enter your email or username"]').clear().type(credential);
cy.get('[placeholder="enter your password"]').clear().type(password);
cy.get('#credential').clear().type(credential);
cy.get('#password').clear().type(password);
cy.get('[type="submit"]').click();
}

@ -25,23 +25,23 @@ context('Issue 1599 (Chinese alphabet).', () => {
describe('User registration using the Chinese alphabet.', () => {
it('Filling in the placeholder "First name"', () => {
cy.get('[placeholder="enter your first name"]').type(firstName).should('not.have.class', 'has-error');
cy.get('#firstName').type(firstName).should('not.have.class', 'has-error');
});
it('Filling in the placeholder "Last name"', () => {
cy.get('[placeholder="enter your last name"]').type(lastName).should('not.have.class', 'has-error');
cy.get('#lastName').type(lastName).should('not.have.class', 'has-error');
});
it('Filling in the placeholder "Username"', () => {
cy.get('[placeholder="enter your username"]').type(userName);
cy.get('#username').type(userName);
});
it('Filling in the placeholder "Email address"', () => {
cy.get('[placeholder="enter your email"]').type(email);
cy.get('#email').type(email);
});
it('Filling in the placeholder "Password"', () => {
cy.get('[placeholder="enter your password"]').type(password);
cy.get('#password1').type(password);
});
it('Click to "Submit" button', () => {

@ -25,23 +25,23 @@ context('Issue 1599 (Polish alphabet).', () => {
describe('User registration using the Polish alphabet.', () => {
it('Filling in the placeholder "First name"', () => {
cy.get('[placeholder="enter your first name"]').type(firstName).should('not.have.class', 'has-error');
cy.get('#firstName').type(firstName).should('not.have.class', 'has-error');
});
it('Filling in the placeholder "Last name"', () => {
cy.get('[placeholder="enter your last name"]').type(lastName).should('not.have.class', 'has-error');
cy.get('#lastName').type(lastName).should('not.have.class', 'has-error');
});
it('Filling in the placeholder "Username"', () => {
cy.get('[placeholder="enter your username"]').type(userName);
cy.get('#username').type(userName);
});
it('Filling in the placeholder "Email address"', () => {
cy.get('[placeholder="enter your email"]').type(email);
cy.get('#email').type(email);
});
it('Filling in the placeholder "Password"', () => {
cy.get('[placeholder="enter your password"]').type(password);
cy.get('#password1').type(password);
});
it('Click to "Submit" button', () => {

@ -20,8 +20,8 @@ require('cy-verify-downloads').addCustomCommand();
let selectedValueGlobal = '';
Cypress.Commands.add('login', (username = Cypress.env('user'), password = Cypress.env('password'), page = 'tasks') => {
cy.get('[placeholder="enter your email or username"]').type(username);
cy.get('[placeholder="enter your password"]').type(password);
cy.get('#credential').type(username);
cy.get('#password').type(password);
cy.get('.cvat-credentials-action-button').click();
cy.url().should('contain', `/${page}`);
cy.document().then((doc) => {
@ -48,7 +48,7 @@ Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAd
cy.get('#lastName').type(lastName);
cy.get('#username').type(userName);
cy.get('#email').type(emailAddr);
cy.get('#password').type(password);
cy.get('#password1').type(password);
cy.get('.cvat-credentials-action-button').click();
if (Cypress.browser.family === 'chromium') {
cy.url().should('include', '/tasks');

Loading…
Cancel
Save