mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Migrations: Signup page (#21514)
* Start Angular migration * Add SignupCtrl * Change name signup * Add backend call * Put form in separate file * Add form model * Start using react-hook-forms * Add FormModel to state * Reduxify * Connect nav with Redux * Fix routing and navModel * Fetch state options on mount * Add default values and add button margin * Add errror messages * Fix title * Remove files and cleanup * Add Signup tests * Add boot config assingnAutoOrg and verifyEmailEnabled * Remove onmount call * Remove ctrl and move everything to SignupForm * Make routeParams optional for testing * Remove name if it is empty * Set username * Make function component * Fix subpath issues and add link button * Move redux to SignupPage
This commit is contained in:
18
public/app/features/profile/SignupForm.test.tsx
Normal file
18
public/app/features/profile/SignupForm.test.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { SignupForm } from './SignupForm';
|
||||
|
||||
describe('SignupForm', () => {
|
||||
describe('With different values for verifyEmail and autoAssignOrg', () => {
|
||||
it('should render input fields', () => {
|
||||
const wrapper = shallow(<SignupForm verifyEmailEnabled={true} autoAssignOrg={false} />);
|
||||
expect(wrapper.exists('Forms.Input[name="orgName"]'));
|
||||
expect(wrapper.exists('Forms.Input[name="code"]'));
|
||||
});
|
||||
it('should not render input fields', () => {
|
||||
const wrapper = shallow(<SignupForm verifyEmailEnabled={false} autoAssignOrg={true} />);
|
||||
expect(wrapper.exists('Forms.Input[name="orgName"]')).toBeFalsy();
|
||||
expect(wrapper.exists('Forms.Input[name="code"]')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
120
public/app/features/profile/SignupForm.tsx
Normal file
120
public/app/features/profile/SignupForm.tsx
Normal file
@@ -0,0 +1,120 @@
|
||||
import React, { FC } from 'react';
|
||||
import { Forms } from '@grafana/ui';
|
||||
import { css } from 'emotion';
|
||||
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { getBackendSrv } from '@grafana/runtime';
|
||||
|
||||
interface SignupFormModel {
|
||||
email: string;
|
||||
username?: string;
|
||||
password: string;
|
||||
orgName: string;
|
||||
code?: string;
|
||||
name?: string;
|
||||
}
|
||||
interface Props {
|
||||
email?: string;
|
||||
orgName?: string;
|
||||
username?: string;
|
||||
code?: string;
|
||||
name?: string;
|
||||
verifyEmailEnabled?: boolean;
|
||||
autoAssignOrg?: boolean;
|
||||
}
|
||||
|
||||
const buttonSpacing = css`
|
||||
margin-left: 15px;
|
||||
`;
|
||||
|
||||
export const SignupForm: FC<Props> = props => {
|
||||
const verifyEmailEnabled = props.verifyEmailEnabled;
|
||||
const autoAssignOrg = props.autoAssignOrg;
|
||||
|
||||
const onSubmit = async (formData: SignupFormModel) => {
|
||||
if (formData.name === '') {
|
||||
delete formData.name;
|
||||
}
|
||||
|
||||
const response = await getBackendSrv().post('/api/user/signup/step2', {
|
||||
email: formData.email,
|
||||
code: formData.code,
|
||||
username: formData.email,
|
||||
orgName: formData.orgName,
|
||||
password: formData.password,
|
||||
name: formData.name,
|
||||
});
|
||||
|
||||
if (response.code === 'redirect-to-select-org') {
|
||||
window.location.href = getConfig().appSubUrl + '/profile/select-org?signup=1';
|
||||
}
|
||||
window.location.href = getConfig().appSubUrl + '/';
|
||||
};
|
||||
|
||||
const defaultValues = {
|
||||
orgName: props.orgName,
|
||||
email: props.email,
|
||||
username: props.email,
|
||||
code: props.code,
|
||||
name: props.name,
|
||||
};
|
||||
|
||||
return (
|
||||
<Forms.Form defaultValues={defaultValues} onSubmit={onSubmit}>
|
||||
{({ register, errors }) => {
|
||||
return (
|
||||
<>
|
||||
{verifyEmailEnabled && (
|
||||
<Forms.Field label="Email verification code (sent to your email)">
|
||||
<Forms.Input name="code" size="md" ref={register} placeholder="Code" />
|
||||
</Forms.Field>
|
||||
)}
|
||||
{!autoAssignOrg && (
|
||||
<Forms.Field label="Org. name">
|
||||
<Forms.Input size="md" name="orgName" placeholder="Org. name" ref={register} />
|
||||
</Forms.Field>
|
||||
)}
|
||||
<Forms.Field label="Your name">
|
||||
<Forms.Input size="md" name="name" placeholder="(optional)" ref={register} />
|
||||
</Forms.Field>
|
||||
<Forms.Field label="Email" invalid={!!errors.email} error={!!errors.email && errors.email.message}>
|
||||
<Forms.Input
|
||||
size="md"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
ref={register({
|
||||
required: 'Email is required',
|
||||
pattern: {
|
||||
value: /^\S+@\S+$/,
|
||||
message: 'Email is invalid',
|
||||
},
|
||||
})}
|
||||
/>
|
||||
</Forms.Field>
|
||||
<Forms.Field
|
||||
label="Password"
|
||||
invalid={!!errors.password}
|
||||
error={!!errors.password && errors.password.message}
|
||||
>
|
||||
<Forms.Input
|
||||
size="md"
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
ref={register({ required: 'Password is required' })}
|
||||
/>
|
||||
</Forms.Field>
|
||||
|
||||
<Forms.Button type="submit">Submit</Forms.Button>
|
||||
<span className={buttonSpacing}>
|
||||
<Forms.LinkButton href={getConfig().appSubUrl + '/login'} variant="secondary">
|
||||
Back
|
||||
</Forms.LinkButton>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</Forms.Form>
|
||||
);
|
||||
};
|
||||
51
public/app/features/profile/SignupPage.tsx
Normal file
51
public/app/features/profile/SignupPage.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import React, { FC } from 'react';
|
||||
import { SignupForm } from './SignupForm';
|
||||
import Page from 'app/core/components/Page/Page';
|
||||
import { getConfig } from 'app/core/config';
|
||||
import { connect } from 'react-redux';
|
||||
import { hot } from 'react-hot-loader';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
const navModel = {
|
||||
main: {
|
||||
icon: 'gicon gicon-branding',
|
||||
text: 'Sign Up',
|
||||
subTitle: 'Register your Grafana account',
|
||||
breadcrumbs: [{ title: 'Login', url: 'login' }],
|
||||
},
|
||||
node: {
|
||||
text: '',
|
||||
},
|
||||
};
|
||||
|
||||
interface Props {
|
||||
email?: string;
|
||||
orgName?: string;
|
||||
username?: string;
|
||||
code?: string;
|
||||
name?: string;
|
||||
}
|
||||
export const SignupPage: FC<Props> = props => {
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page.Contents>
|
||||
<h3 className="p-b-1">You're almost there.</h3>
|
||||
<div className="p-b-1">
|
||||
We just need a couple of more bits of
|
||||
<br /> information to finish creating your account.
|
||||
</div>
|
||||
<SignupForm
|
||||
{...props}
|
||||
verifyEmailEnabled={getConfig().verifyEmailEnabled}
|
||||
autoAssignOrg={getConfig().autoAssignOrg}
|
||||
/>
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
...state.location.routeParams,
|
||||
});
|
||||
|
||||
export default hot(module)(connect(mapStateToProps)(SignupPage));
|
||||
Reference in New Issue
Block a user