chore(ui): fix signup page styling (#13086)

* chore(ui): fix signup page styling

* fix the file naming convention

* optimize signup form
This commit is contained in:
Ashish Gupta 2023-09-06 15:54:24 +05:30 committed by GitHub
parent de7e06d024
commit 6ef003798b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 443 additions and 521 deletions

View File

@ -64,7 +64,9 @@ jest.mock('components/AppBar/Appbar', () =>
jest.fn().mockReturnValue(<p>Appbar</p>)
);
jest.mock('pages/signup', () => jest.fn().mockReturnValue(<p>SignUpPage</p>));
jest.mock('pages/SignUp/SignUpPage', () =>
jest.fn().mockReturnValue(<p>SignUpPage</p>)
);
jest.mock('components/router/AuthenticatedAppRouter', () =>
jest.fn().mockReturnValue(<p>AuthenticatedAppRouter</p>)

View File

@ -19,7 +19,7 @@ import LeftSidebar from 'components/MyData/LeftSidebar/LeftSidebar.component';
import AuthenticatedAppRouter from 'components/router/AuthenticatedAppRouter';
import { ROUTES } from 'constants/constants';
import { isEmpty } from 'lodash';
import SignupPage from 'pages/signup';
import SignUpPage from 'pages/SignUp/SignUpPage';
import React from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';
import './app-container.less';
@ -27,7 +27,7 @@ import './app-container.less';
const AppContainer = () => {
return (
<Switch>
<Route exact component={SignupPage} path={ROUTES.SIGNUP}>
<Route exact component={SignUpPage} path={ROUTES.SIGNUP}>
{!isEmpty(AppState.userDetails) && <Redirect to={ROUTES.HOME} />}
</Route>

View File

@ -12,7 +12,7 @@
*/
export interface TeamsSelectableProps {
showTeamsAlert?: boolean;
onSelectionChange: (teams: string[]) => void;
onSelectionChange?: (teams: string[]) => void;
filterJoinable?: boolean;
placeholder?: string;
selectedTeams?: string[];

View File

@ -36,7 +36,7 @@ const TeamsSelectable = ({
const [teams, setTeams] = useState<Array<TeamHierarchy>>([]);
const onChange = (newValue: string[]) => {
onSelectionChange(newValue);
onSelectionChange && onSelectionChange(newValue);
setValue(newValue);
};

View File

@ -14,7 +14,7 @@
import AppContainer from 'components/AppContainer/AppContainer';
import { CustomEventTypes } from 'generated/analytics/webAnalyticEventData';
import { AuthProvider } from 'generated/settings/settings';
import AccountActivationConfirmation from 'pages/signup/account-activation-confirmation.component';
import AccountActivationConfirmation from 'pages/SignUp/account-activation-confirmation.component';
import React, { useCallback, useEffect } from 'react';
import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
import { useAnalytics } from 'use-analytics';
@ -41,7 +41,7 @@ const ResetPassword = withSuspenseFallback(
);
const BasicSignupPage = withSuspenseFallback(
React.lazy(() => import('pages/signup/BasicSignup.component'))
React.lazy(() => import('pages/SignUp/BasicSignup.component'))
);
const AppRouter = () => {

View File

@ -121,7 +121,7 @@ export const PAGE_HEADERS = {
STORED_PROCEDURE_CUSTOM_ATTRIBUTES: {
header: i18n.t('label.stored-procedure'),
subHeader: i18n.t('message.define-custom-property-for-entity', {
entity: i18n.t('label.label.stored-procedure'),
entity: i18n.t('label.stored-procedure'),
}),
},
BOTS: {

View File

@ -0,0 +1,227 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, fireEvent, render, screen } from '@testing-library/react';
import AppState from 'AppState';
import React from 'react';
import { createUser } from 'rest/userAPI';
import { getImages } from 'utils/CommonUtils';
import {
mockChangedFormData,
mockCreateUser,
mockFormData,
} from './mocks/signup.mock';
import SignUp from './SignUpPage';
let letExpectedUserName = {
name: 'sample123',
email: 'sample123@sample.com',
};
const mockShowErrorToast = jest.fn();
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockReturnValue({
push: jest.fn(),
}),
}));
jest.mock('components/authentication/auth-provider/AuthProvider', () => ({
useAuthContext: jest.fn(() => ({
setIsSigningIn: jest.fn(),
})),
}));
jest.mock('components/TeamsSelectable/TeamsSelectable', () =>
jest.fn().mockImplementation(() => <div>TeamSelectable</div>)
);
jest.mock('rest/userAPI', () => ({
createUser: jest
.fn()
.mockImplementation(() => Promise.resolve(mockCreateUser)),
}));
jest.mock('../../utils/ToastUtils', () => ({
showErrorToast: jest.fn().mockImplementation(() => mockShowErrorToast),
}));
jest.mock('../../AppState', () => ({
...jest.requireActual('../../AppState'),
newUser: {
name: 'Sample Name',
email: 'sample123@sample.com',
picture: 'Profile Picture',
},
updateUserDetails: jest.fn(),
updateUserPermissions: jest.fn(),
}));
jest.mock('../../utils/CommonUtils', () => ({
getImages: jest
.fn()
.mockResolvedValue(
'https://lh3.googleusercontent.com/a/ALm5wu0HwEPhAbyRha16cUHrEum-zxTDzj6KZiqYsT5Y=s96-c'
),
Transi18next: jest.fn().mockReturnValue('text'),
}));
jest.mock('utils/AuthProvider.util', () => ({
getNameFromUserData: jest.fn().mockImplementation(() => letExpectedUserName),
}));
describe('SignUp page', () => {
it('Component should render properly', async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({ data: {} })
);
render(<SignUp />);
const logo = screen.getByTestId('om-logo');
const heading = screen.getByTestId('om-heading');
const form = screen.getByTestId('create-user-form');
const fullNameLabel = screen.getByTestId('full-name-label');
const fullNameInput = screen.getByTestId('full-name-input');
const usernameLabel = screen.getByTestId('username-label');
const usernameInput = screen.getByTestId('username-input');
const emailLabel = screen.getByTestId('email-label');
const emailInput = screen.getByTestId('email-input');
const selectTeamLabel = screen.getByTestId('select-team-label');
const createButton = screen.getByTestId('create-button');
const loadingContent = await screen.queryByTestId('loading-content');
const submitButton = screen.getByTestId('create-button');
expect(logo).toBeInTheDocument();
expect(heading).toBeInTheDocument();
expect(form).toBeInTheDocument();
expect(fullNameLabel).toBeInTheDocument();
expect(fullNameInput).toBeInTheDocument();
expect(usernameLabel).toBeInTheDocument();
expect(usernameInput).toBeInTheDocument();
expect(emailLabel).toBeInTheDocument();
expect(emailInput).toBeInTheDocument();
expect(selectTeamLabel).toBeInTheDocument();
expect(createButton).toBeInTheDocument();
expect(loadingContent).toBeNull();
expect(submitButton).toBeInTheDocument();
});
it('Handlers in forms for change and submit should work properly', async () => {
render(<SignUp />);
const form = screen.getByTestId('create-user-form');
const fullNameInput = screen.getByTestId(
'full-name-input'
) as HTMLInputElement;
const userNameInput = screen.getByTestId(
'username-input'
) as HTMLInputElement;
const emailInput = screen.getByTestId('email-input') as HTMLInputElement;
const submitButton = screen.getByTestId('create-button');
expect(form).toBeInTheDocument();
expect(fullNameInput).toHaveValue(mockFormData.name);
expect(userNameInput).toHaveValue(mockFormData.userName);
expect(emailInput).toHaveValue(mockFormData.email);
await act(async () => {
fireEvent.change(fullNameInput, {
target: { name: 'displayName', value: mockChangedFormData.fullName },
});
fireEvent.change(userNameInput, {
target: { name: 'name', value: mockChangedFormData.userName },
});
fireEvent.change(emailInput, {
target: { name: 'email', value: mockChangedFormData.email },
});
});
expect(fullNameInput).toHaveValue(mockChangedFormData.fullName);
expect(userNameInput).toHaveValue(mockChangedFormData.userName);
expect(emailInput).toHaveValue(mockChangedFormData.email);
fireEvent.click(submitButton);
await act(async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.resolve(undefined)
);
});
expect(createUser as jest.Mock).toHaveBeenCalledTimes(1);
});
it('Error should be thrown if createUser API fails', async () => {
render(<SignUp />);
const form = screen.getByTestId('create-user-form');
const fullNameInput = screen.getByTestId('full-name-input');
const userNameInput = screen.getByTestId('username-input');
const emailInput = screen.getByTestId('email-input');
const submitButton = screen.getByTestId('create-button');
expect(form).toBeInTheDocument();
expect(fullNameInput).toHaveValue(mockFormData.name);
expect(userNameInput).toHaveValue(mockFormData.userName);
expect(emailInput).toHaveValue(mockFormData.email);
await act(async () => {
fireEvent.change(fullNameInput, {
target: { name: 'displayName', value: mockChangedFormData.fullName },
});
fireEvent.change(userNameInput, {
target: { name: 'name', value: mockChangedFormData.userName },
});
fireEvent.change(emailInput, {
target: { name: 'email', value: mockChangedFormData.email },
});
});
expect(fullNameInput).toHaveValue(mockChangedFormData.fullName);
expect(userNameInput).toHaveValue(mockChangedFormData.userName);
expect(emailInput).toHaveValue(mockChangedFormData.email);
fireEvent.click(submitButton);
await act(async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.reject({
response: { data: { message: 'error' } },
})
);
});
expect(createUser as jest.Mock).toHaveBeenCalledTimes(1);
});
it('Handlers in form should work if data is empty', async () => {
(getImages as jest.Mock).mockImplementationOnce(() => Promise.reject(''));
letExpectedUserName = { name: '', email: '' };
AppState.newUser = {
name: '',
email: '',
picture: '',
};
render(<SignUp />);
const form = screen.getByTestId('create-user-form');
const fullNameInput = screen.getByTestId('full-name-input');
const usernameInput = screen.getByTestId('username-input');
const emailInput = screen.getByTestId('email-input');
expect(form).toBeInTheDocument();
expect(fullNameInput).toHaveValue('');
expect(usernameInput).toHaveValue('');
expect(emailInput).toHaveValue('');
const submitButton = screen.getByTestId('create-button');
fireEvent.click(submitButton);
expect(createUser as jest.Mock).toHaveBeenCalledTimes(0);
});
});

View File

@ -0,0 +1,193 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Button, Card, Form, FormProps, Input, Space, Typography } from 'antd';
import { AxiosError } from 'axios';
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
import { UserProfile } from 'components/authentication/auth-provider/AuthProvider.interface';
import TeamsSelectable from 'components/TeamsSelectable/TeamsSelectable';
import { CookieStorage } from 'cookie-storage';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { createUser } from 'rest/userAPI';
import { getNameFromUserData } from 'utils/AuthProvider.util';
import appState from '../../AppState';
import { ReactComponent as OMDLogo } from '../../assets/svg/logo-monogram.svg';
import {
REDIRECT_PATHNAME,
ROUTES,
VALIDATION_MESSAGES,
} from '../../constants/constants';
import { getImages, Transi18next } from '../../utils/CommonUtils';
import { showErrorToast } from '../../utils/ToastUtils';
const cookieStorage = new CookieStorage();
const SignUp = () => {
const { t } = useTranslation();
const history = useHistory();
const {
setIsSigningIn,
jwtPrincipalClaims = [],
authorizerConfig,
} = useAuthContext();
const [loading, setLoading] = useState<boolean>(false);
const handleCreateNewUser: FormProps['onFinish'] = async (data) => {
setLoading(true);
try {
const res = await createUser({
...data,
profile: {
images: getImages(appState.newUser.picture ?? ''),
},
});
appState.updateUserDetails(res);
cookieStorage.removeItem(REDIRECT_PATHNAME);
setIsSigningIn(false);
history.push(ROUTES.HOME);
} catch (error) {
showErrorToast(
error as AxiosError,
t('server.create-entity-error', {
entity: t('label.user'),
})
);
} finally {
setLoading(false);
}
};
return (
<div className="flex-center w-full h-full">
<Card className="p-x-md p-y-md w-500">
<Space
align="center"
className="w-full m-b-lg"
direction="vertical"
size="middle">
<OMDLogo
data-testid="om-logo"
height={50}
name={t('label.open-metadata-logo')}
width={50}
/>
<Typography.Title
className="text-center"
data-testid="om-heading"
level={3}>
<Transi18next
i18nKey="label.join-entity"
renderElement={<span className="text-primary" />}
values={{
entity: t('label.open-metadata'),
}}
/>
</Typography.Title>
</Space>
<Form
data-testid="create-user-form"
initialValues={{
displayName: appState.newUser.name || '',
...getNameFromUserData(
appState.newUser as UserProfile,
jwtPrincipalClaims,
authorizerConfig?.principalDomain
),
}}
layout="vertical"
validateMessages={VALIDATION_MESSAGES}
onFinish={handleCreateNewUser}>
<Form.Item
data-testid="full-name-label"
label={t('label.full-name')}
name="displayName"
rules={[
{
required: true,
},
]}>
<Input
data-testid="full-name-input"
placeholder={t('label.your-entity', {
entity: t('label.full-name'),
})}
/>
</Form.Item>
<Form.Item
data-testid="username-label"
label={t('label.username')}
name="name"
rules={[
{
required: true,
},
]}>
<Input
disabled
data-testid="username-input"
placeholder={t('label.username')}
/>
</Form.Item>
<Form.Item
data-testid="email-label"
label={t('label.email')}
name="email"
rules={[
{
required: true,
},
]}>
<Input
disabled
data-testid="email-input"
placeholder={t('label.your-entity', {
entity: `${t('label.email')} ${t('label.address')}`,
})}
type="email"
/>
</Form.Item>
<Form.Item
data-testid="select-team-label"
label={t('label.select-field', {
field: t('label.team-plural-lowercase'),
})}
name="teams"
trigger="onSelectionChange">
<TeamsSelectable filterJoinable showTeamsAlert />
</Form.Item>
<Space align="center" className="w-full justify-end d-flex">
<Button
data-testid="create-button"
htmlType="submit"
loading={loading}
type="primary">
{t('label.create')}
</Button>
</Space>
</Form>
</Card>
</div>
);
};
export default SignUp;

View File

@ -11,6 +11,19 @@
* limitations under the License.
*/
export const mockFormData = {
name: 'Sample Name',
email: 'sample123@sample.com',
picture: 'Profile Picture',
userName: 'sample123',
};
export const mockChangedFormData = {
fullName: 'f_name m_name l_name',
userName: 'mockUserName',
email: 'test@gmail.com',
};
export const mockCreateUser = {
data: {
id: '911d4be4-6ebf-48a0-9016-43a2cf716428',

View File

@ -1,289 +0,0 @@
/*
* Copyright 2022 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { act, fireEvent, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { createUser } from 'rest/userAPI';
import SignUp from '.';
import AppState from '../../AppState';
import { getImages } from '../../utils/CommonUtils';
import { mockCreateUser } from './mocks/signup.mock';
let letExpectedUserName = { name: 'sample123', email: 'sample123@sample.com' };
const mockChangeHandler = jest.fn();
const mockSubmitHandler = jest.fn();
const mockShowErrorToast = jest.fn();
jest.mock('react-router-dom', () => ({
useHistory: jest.fn().mockReturnValue({
push: jest.fn(),
}),
}));
jest.mock('components/authentication/auth-provider/AuthProvider', () => ({
useAuthContext: jest.fn(() => ({
setIsSigningIn: jest.fn(),
})),
}));
jest.mock('components/TeamsSelectable/TeamsSelectable', () => {
return jest.fn().mockImplementation(() => <div>TeamSelectable</div>);
});
jest.mock('rest/userAPI', () => ({
createUser: jest
.fn()
.mockImplementation(() => Promise.resolve(mockCreateUser)),
}));
jest.mock('../../utils/ToastUtils', () => ({
showErrorToast: jest.fn().mockImplementation(() => mockShowErrorToast),
}));
jest.mock('../../AppState', () => ({
...jest.requireActual('../../AppState'),
newUser: {
name: 'Sample Name',
email: 'sample123@sample.com',
picture: 'Profile Picture',
},
updateUserDetails: jest.fn(),
updateUserPermissions: jest.fn(),
}));
jest.mock('../../utils/CommonUtils', () => ({
getImages: jest
.fn()
.mockResolvedValue(
'https://lh3.googleusercontent.com/a/ALm5wu0HwEPhAbyRha16cUHrEum-zxTDzj6KZiqYsT5Y=s96-c'
),
Transi18next: jest.fn().mockReturnValue('text'),
}));
jest.mock('utils/AuthProvider.util', () => ({
getNameFromUserData: jest.fn().mockImplementation(() => letExpectedUserName),
}));
describe('SignUp page', () => {
it('Component should render properly', async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({ data: {} })
);
const { getByTestId, queryByTestId } = render(<SignUp />);
const logo = getByTestId('om-logo');
const heading = getByTestId('om-heading');
const form = getByTestId('create-user-form');
const fullNameLabel = getByTestId('full-name-label');
const fullNameInput = getByTestId('full-name-input');
const usernameLabel = getByTestId('username-label');
const usernameInput = getByTestId('username-input');
const emailLabel = getByTestId('email-label');
const emailInput = getByTestId('email-input');
const selectTeamLabel = getByTestId('select-team-label');
const createButton = getByTestId('create-button');
const loadingContent = await queryByTestId('loading-content');
expect(logo).toBeInTheDocument();
expect(heading).toBeInTheDocument();
expect(form).toBeInTheDocument();
expect(fullNameLabel).toBeInTheDocument();
expect(fullNameInput).toBeInTheDocument();
expect(usernameLabel).toBeInTheDocument();
expect(usernameInput).toBeInTheDocument();
expect(emailLabel).toBeInTheDocument();
expect(emailInput).toBeInTheDocument();
expect(selectTeamLabel).toBeInTheDocument();
expect(createButton).toBeInTheDocument();
expect(loadingContent).toBeNull();
await act(async () => {
form.onsubmit = mockSubmitHandler;
fireEvent.submit(form);
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
});
});
it('Handlers in forms for change and submit should work properly', async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.resolve(undefined)
);
const { getByTestId } = render(<SignUp />);
const form = getByTestId('create-user-form');
const fullNameInput = getByTestId('full-name-input');
const usernameInput = getByTestId('username-input');
const emailInput = getByTestId('email-input');
expect(form).toBeInTheDocument();
expect(fullNameInput).toHaveValue('Sample Name');
expect(usernameInput).toHaveValue('sample123');
expect(emailInput).toHaveValue('sample123@sample.com');
fullNameInput.onchange = mockChangeHandler;
usernameInput.onchange = mockChangeHandler;
emailInput.onchange = mockChangeHandler;
await act(async () => {
fireEvent.change(fullNameInput, {
target: { name: 'displayName', value: 'Fname Mname Lname' },
});
fireEvent.change(usernameInput, {
target: { name: 'displayName', value: 'mockUserName' },
});
fireEvent.change(emailInput, {
target: { name: 'displayName', value: 'sample@sample.com' },
});
expect(mockChangeHandler).toHaveBeenCalledTimes(3);
form.onsubmit = mockSubmitHandler;
fireEvent.submit(form);
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
});
});
it('Error should be thrown if createUser API fails', async () => {
const { getByTestId } = render(<SignUp />);
const form = getByTestId('create-user-form');
const fullNameInput = getByTestId('full-name-input');
const usernameInput = getByTestId('username-input');
const emailInput = getByTestId('email-input');
expect(form).toBeInTheDocument();
expect(fullNameInput).toHaveValue('Sample Name');
expect(usernameInput).toHaveValue('sample123');
expect(emailInput).toHaveValue('sample123@sample.com');
fullNameInput.onchange = mockChangeHandler;
usernameInput.onchange = mockChangeHandler;
emailInput.onchange = mockChangeHandler;
await act(async () => {
fireEvent.change(fullNameInput, {
target: { name: 'displayName', value: 'Fname Mname Lname' },
});
fireEvent.change(usernameInput, {
target: { name: 'displayName', value: 'mockUserName' },
});
fireEvent.change(emailInput, {
target: { name: 'displayName', value: '' },
});
});
expect(mockChangeHandler).toHaveBeenCalledTimes(3);
form.onsubmit = mockSubmitHandler;
await act(async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.reject({
response: { data: { message: 'error' } },
})
);
fireEvent.submit(form);
});
expect(createUser as jest.Mock).toHaveBeenCalledTimes(1);
});
it('Handlers in form should work if data is empty', async () => {
(getImages as jest.Mock).mockImplementationOnce(() => Promise.reject(''));
letExpectedUserName = { name: '', email: '' };
AppState.newUser = {
name: '',
email: '',
picture: '',
};
const { getByTestId } = render(<SignUp />);
const form = getByTestId('create-user-form');
const fullNameInput = getByTestId('full-name-input');
const usernameInput = getByTestId('username-input');
const emailInput = getByTestId('email-input');
expect(form).toBeInTheDocument();
expect(fullNameInput).toHaveValue('');
expect(usernameInput).toHaveValue('');
expect(emailInput).toHaveValue('');
fullNameInput.onchange = mockChangeHandler;
usernameInput.onchange = mockChangeHandler;
emailInput.onchange = mockChangeHandler;
expect(mockChangeHandler).not.toHaveBeenCalled();
form.onsubmit = mockSubmitHandler;
await act(async () => {
fireEvent.submit(form);
});
expect(createUser as jest.Mock).toHaveBeenCalledTimes(0);
});
it('Create Button Should Work Properly and call the form handler', async () => {
(createUser as jest.Mock).mockImplementationOnce(() =>
Promise.resolve(undefined)
);
const { getByTestId } = render(<SignUp />);
const form = getByTestId('create-user-form');
const fullNameInput = getByTestId('full-name-input');
const usernameInput = getByTestId('username-input');
const emailInput = getByTestId('email-input');
expect(form).toBeInTheDocument();
fullNameInput.onchange = mockChangeHandler;
usernameInput.onchange = mockChangeHandler;
emailInput.onchange = mockChangeHandler;
await act(async () => {
fireEvent.change(fullNameInput, {
target: { name: 'displayName', value: 'Fname Mname Lname' },
});
fireEvent.change(usernameInput, {
target: { name: 'displayName', value: 'mockUserName' },
});
fireEvent.change(emailInput, {
target: { name: 'displayName', value: 'sample@sample.com' },
});
expect(mockChangeHandler).toHaveBeenCalledTimes(3);
form.onsubmit = mockSubmitHandler;
const createButton = getByTestId('create-button');
userEvent.click(createButton);
expect(mockSubmitHandler).toHaveBeenCalledTimes(1);
});
});
});

View File

@ -1,224 +0,0 @@
/*
* Copyright 2022 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Button } from 'antd';
import { AxiosError } from 'axios';
import { useAuthContext } from 'components/authentication/auth-provider/AuthProvider';
import { UserProfile } from 'components/authentication/auth-provider/AuthProvider.interface';
import TeamsSelectable from 'components/TeamsSelectable/TeamsSelectable';
import { CookieStorage } from 'cookie-storage';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory } from 'react-router-dom';
import { createUser } from 'rest/userAPI';
import { getNameFromUserData } from 'utils/AuthProvider.util';
import appState from '../../AppState';
import { ReactComponent as OMDLogo } from '../../assets/svg/logo-monogram.svg';
import { ELLIPSES, REDIRECT_PATHNAME, ROUTES } from '../../constants/constants';
import { CreateUser } from '../../generated/api/teams/createUser';
import { User } from '../../generated/entity/teams/user';
import { getImages, Transi18next } from '../../utils/CommonUtils';
import { showErrorToast } from '../../utils/ToastUtils';
const cookieStorage = new CookieStorage();
const SignUp = () => {
const { t } = useTranslation();
const history = useHistory();
const {
setIsSigningIn,
jwtPrincipalClaims = [],
authorizerConfig,
} = useAuthContext();
const [selectedTeams, setSelectedTeams] = useState<Array<string>>([]);
const [loading, setLoading] = useState<boolean>(false);
const [details, setDetails] = useState({
displayName: appState.newUser.name || '',
...getNameFromUserData(
appState.newUser as UserProfile,
jwtPrincipalClaims,
authorizerConfig?.principalDomain
),
});
const createNewUser = (details: User | CreateUser) => {
setLoading(true);
createUser(details as CreateUser)
.then((res) => {
if (res) {
appState.updateUserDetails(res);
cookieStorage.removeItem(REDIRECT_PATHNAME);
setIsSigningIn(false);
history.push(ROUTES.HOME);
} else {
setLoading(false);
}
})
.catch((err: AxiosError) => {
showErrorToast(
err,
t('server.create-entity-error', {
entity: t('label.user'),
})
);
})
.finally(() => {
setLoading(false);
});
};
const onChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
e.persist();
setDetails((prevState) => {
return {
...prevState,
[e.target.name]: e.target.value,
};
});
};
const onSubmitHandler = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (details.name && details.displayName) {
createNewUser({
...details,
teams: selectedTeams as Array<string>,
profile: {
images: getImages(appState.newUser.picture ?? ''),
},
});
}
};
return loading ? (
<p
className="text-center text-grey-body d-flex justify-center items-center"
data-testid="loading-content">
{t('label.creating-account')}
{ELLIPSES}
</p>
) : (
// TODO: replace this with form
<div className="d-flex justify-center">
<div className="d-flex flex-col items-center signup-box">
<div className="d-flex justify-center items-center">
<OMDLogo
data-testid="om-logo"
height={50}
name={t('label.open-metadata-logo')}
width={50}
/>
</div>
<div>
<h4 className="font-semibold" data-testid="om-heading">
<Transi18next
i18nKey="label.join-entity"
renderElement={<span className="text-primary" />}
values={{
entity: t('label.open-metadata'),
}}
/>
</h4>
</div>
<div className="w-full">
<form
action="."
data-testid="create-user-form"
method="POST"
onSubmit={onSubmitHandler}>
<div>
<label
className="d-block text-body text-grey-body required-field"
data-testid="full-name-label"
htmlFor="displayName">
{t('label.full-name')}
</label>
<input
required
autoComplete="off"
data-testid="full-name-input"
id="displayName"
name="displayName"
placeholder={t('label.your-entity', {
entity: t('label.full-name'),
})}
type="text"
value={details.displayName}
onChange={onChangeHandler}
/>
</div>
<div>
<label data-testid="username-label" htmlFor="name">
{t('label.username')}
</label>
<input
readOnly
required
autoComplete="off"
data-testid="username-input"
id="name"
name="name"
placeholder={t('label.username')}
type="text"
value={details.name}
onChange={onChangeHandler}
/>
</div>
<div>
<label data-testid="email-label" htmlFor="email">
{t('label.email')}
</label>
<input
readOnly
required
autoComplete="off"
data-testid="email-input"
id="email"
name="email"
placeholder={t('label.your-entity', {
entity: `${t('label.email')} ${t('label.address')}`,
})}
type="email"
value={details.email}
onChange={onChangeHandler}
/>
</div>
<div>
<label data-testid="select-team-label">
{t('label.select-field', {
field: t('label.team-plural-lowercase'),
})}
</label>
<TeamsSelectable
filterJoinable
showTeamsAlert
onSelectionChange={setSelectedTeams}
/>
</div>
<div className="d-flex justify-end">
<Button
data-testid="create-button"
htmlType="submit"
type="primary">
{t('label.create')}
</Button>
</div>
</form>
</div>
</div>
</div>
);
};
export default SignUp;