feat(ui): login & sign-up page for IDPE (#17049)

pull/17153/head
Ariel Salem 2020-03-09 10:33:34 -07:00 committed by GitHub
parent a017f0c4a2
commit 4b3b1308f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1283 additions and 25 deletions

View File

@ -6,6 +6,7 @@
1. [17095](https://github.com/influxdata/influxdb/pull/17095): Extend pkger dashboards with table view support
1. [17114](https://github.com/influxdata/influxdb/pull/17114): Allow for retention to be provided to influx setup command as a duration
1. [17138](https://github.com/influxdata/influxdb/pull/17138): Extend pkger export all capabilities to support filtering by lable name and resource type
1. [17049](https://github.com/influxdata/influxdb/pull/17049): Added new login and sign-up screen that for cloud users that allows direct login from their region
### Bug Fixes
@ -19,6 +20,7 @@
1. [17113](https://github.com/influxdata/influxdb/pull/17113): Disabled group functionality for check query builder
1. [17120](https://github.com/influxdata/influxdb/pull/17120): Fixed cell configuration error that was popping up when users create a dashboard and accessed the disk usage cell for the first time
1. [17097](https://github.com/influxdata/influxdb/pull/17097): Listing all the default variables in the VariableTab of the script editor
1. [17049](https://github.com/influxdata/influxdb/pull/17049): Fixed bug that was preventing the interval status on the dashboard header from refreshing on selections
## v2.0.0-beta.5 [2020-02-27]

View File

@ -137,6 +137,7 @@
"@influxdata/influxdb-templates": "0.9.0",
"@influxdata/react-custom-scrollbars": "4.3.8",
"abortcontroller-polyfill": "^1.3.0",
"auth0-js": "^9.12.2",
"axios": "^0.19.0",
"babel-polyfill": "^6.26.0",
"bignumber.js": "^4.0.2",
@ -157,8 +158,8 @@
"moment": "^2.13.0",
"monaco-editor": "^0.19.2",
"monaco-editor-textmate": "^2.2.1",
"monaco-languageclient": "^0.11.0",
"monaco-editor-webpack-plugin": "^1.8.2",
"monaco-languageclient": "^0.11.0",
"monaco-textmate": "^3.0.1",
"normalizr": "^3.4.1",
"onigasm": "^2.2.4",
@ -181,6 +182,7 @@
"react-router": "^3.0.2",
"react-router-redux": "^4.0.8",
"react-scrollbars-custom": "^4.0.0-alpha.8",
"react-spring": "^8.0.27",
"react-virtualized": "^9.18.5",
"redux": "^4.0.0",
"redux-auth-wrapper": "^1.0.0",

View File

@ -1,9 +1,11 @@
// Libraries
import {PureComponent} from 'react'
import {FC, useEffect} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
import auth0js from 'auth0-js'
// APIs
import {postSignout} from 'src/client'
import {getAuth0Config} from 'src/authorizations/apis'
// Constants
import {CLOUD, CLOUD_URL, CLOUD_LOGOUT_PATH} from 'src/shared/constants'
@ -11,19 +13,20 @@ import {CLOUD, CLOUD_URL, CLOUD_LOGOUT_PATH} from 'src/shared/constants'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
type Props = WithRouterProps
// Utils
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
@ErrorHandling
export class Logout extends PureComponent<Props> {
public componentDidMount() {
this.handleSignOut()
}
public render() {
return null
}
private handleSignOut = async () => {
const Logout: FC<WithRouterProps> = ({router}) => {
const handleSignOut = async () => {
if (CLOUD && isFlagEnabled('regionBasedLoginPage')) {
const config = await getAuth0Config()
const auth0 = new auth0js.WebAuth({
domain: config.domain,
clientID: config.clientID,
})
auth0.logout({})
return
}
if (CLOUD) {
window.location.href = `${CLOUD_URL}${CLOUD_LOGOUT_PATH}`
return
@ -34,9 +37,14 @@ export class Logout extends PureComponent<Props> {
throw new Error(resp.data.message)
}
this.props.router.push(`/signin`)
router.push(`/signin`)
}
}
useEffect(() => {
handleSignOut()
}, [])
return null
}
export default withRouter<Props>(Logout)
export default ErrorHandling(withRouter<WithRouterProps>(Logout))

View File

@ -19,6 +19,9 @@ import {CLOUD, CLOUD_SIGNIN_PATHNAME} from 'src/shared/constants'
// Types
import {RemoteDataState} from 'src/types'
// Utils
import {isFlagEnabled} from 'src/shared/utils/featureFlag'
interface State {
loading: RemoteDataState
}
@ -81,6 +84,11 @@ export class Signin extends PureComponent<Props, State> {
clearInterval(this.intervalID)
if (CLOUD && isFlagEnabled('regionBasedLoginPage')) {
this.props.router.replace('/login')
return
}
// TODO: add returnTo to CLOUD signin
if (CLOUD) {
window.location.pathname = CLOUD_SIGNIN_PATHNAME

View File

@ -1,5 +1,5 @@
import AJAX from 'src/utils/ajax'
import {Authorization} from 'src/types'
import {Authorization, Auth0Config} from 'src/types'
export const createAuthorization = async (
authorization
@ -17,3 +17,14 @@ export const createAuthorization = async (
throw error
}
}
export const getAuth0Config = async (): Promise<Auth0Config> => {
try {
const response = await fetch('/api/v2private/oauth/clientConfig')
const data = await response.json()
return data
} catch (error) {
console.error(error)
throw error
}
}

View File

@ -0,0 +1,18 @@
// Libraries
import React, {FC} from 'react'
import classnames from 'classnames'
interface Props {
className?: string
}
export const GithubLogo: FC<Props> = ({className}) => (
<svg
className={classnames('github-logo', className)}
role="img"
viewBox="0 0 24 24"
>
<title>GitHub icon</title>
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>
)

View File

@ -0,0 +1,33 @@
// Libraries
import React, {FC} from 'react'
import classnames from 'classnames'
interface Props {
className?: string
}
export const GoogleLogo: FC<Props> = ({className}) => (
<svg
className={classnames('google-logo', className)}
x="0"
y="0"
viewBox="0 0 17.6 18"
>
<path
d="M15 15.8h-3v-2.3c1-.6 1.6-1.6 1.8-2.7H9V7.4h8.5c.1.6.2 1.2.2 1.8-.1 2.7-1 5.1-2.7 6.6z"
className="google-logo--blue"
/>
<path
d="M9 18c-3.5 0-6.6-2-8-5v-2.3h3c.7 2.1 2.7 3.7 5 3.7 1.2 0 2.2-.3 3-.9l2.9 2.3C13.5 17.2 11.4 18 9 18z"
className="google-logo--green"
/>
<path
d="M4 7.3c-.2.5-.3 1.1-.3 1.7 0 .6.1 1.2.3 1.7L1 13c-.6-1.2-1-2.6-1-4s.3-2.8 1-4h3v2.3z"
className="google-logo--yellow"
/>
<path
d="M12.4 4.9C11.5 4 10.3 3.6 9 3.6c-2.3 0-4.3 1.6-5 3.7L1 5c1.4-3 4.5-5 8-5 2.4 0 4.5.9 6 2.3l-2.6 2.6z"
className="google-logo--red"
/>
</svg>
)

View File

@ -0,0 +1,19 @@
import CSharpLogo from 'src/clientLibraries/graphics/CSharpLogo'
import {GithubLogo} from 'src/clientLibraries/graphics/GithubLogo'
import GoLogo from 'src/clientLibraries/graphics/GoLogo'
import {GoogleLogo} from 'src/clientLibraries/graphics/GoogleLogo'
import JavaLogo from 'src/clientLibraries/graphics/JavaLogo'
import JSLogo from 'src/clientLibraries/graphics/JSLogo'
import PythonLogo from 'src/clientLibraries/graphics/PythonLogo'
import RubyLogo from 'src/clientLibraries/graphics/RubyLogo'
export {
CSharpLogo,
GithubLogo,
GoLogo,
GoogleLogo,
JavaLogo,
JSLogo,
PythonLogo,
RubyLogo,
}

View File

@ -20,6 +20,7 @@ import GetOrganizations from 'src/shared/containers/GetOrganizations'
import Setup from 'src/Setup'
import Signin from 'src/Signin'
import SigninPage from 'src/onboarding/containers/SigninPage'
import {LoginPage} from 'src/onboarding/containers/LoginPage'
import Logout from 'src/Logout'
import TaskPage from 'src/tasks/containers/TaskPage'
import TasksPage from 'src/tasks/containers/TasksPage'
@ -191,6 +192,7 @@ class Root extends PureComponent {
component={OnboardingWizardPage}
/>
<Route component={UnauthenticatedApp}>
<Route path="/login" component={LoginPage} />
<Route path="/signin" component={SigninPage} />
<Route path="/logout" component={Logout} />
</Route>

View File

@ -0,0 +1,104 @@
// Libraries
import React, {FC, useState, ChangeEvent} from 'react'
import {
Button,
ButtonShape,
ButtonType,
Columns,
ComponentColor,
ComponentSize,
ComponentStatus,
Form,
Grid,
Input,
InputType,
VisibilityInput,
} from '@influxdata/clockface'
// Types
import {FormFieldValidation} from 'src/types'
interface Props {
buttonStatus: ComponentStatus
emailValidation: FormFieldValidation
email: string
passwordValidation: FormFieldValidation
password: string
handleInputChange: (event: ChangeEvent<HTMLInputElement>) => void
handleForgotPasswordClick: () => void
}
export const LoginForm: FC<Props> = ({
buttonStatus,
emailValidation,
email,
passwordValidation,
password,
handleInputChange,
handleForgotPasswordClick,
}) => {
const [isVisible, toggleVisibility] = useState(false)
return (
<>
<Grid>
<Grid.Row className="sign-up--form-padded-row">
<Grid.Column widthXS={Columns.Twelve}>
<Form.Element
label="Work Email Address"
required={true}
errorMessage={emailValidation.errorMessage}
>
<Input
name="email"
value={email}
type={InputType.Email}
size={ComponentSize.Large}
status={
emailValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onChange={handleInputChange}
/>
</Form.Element>
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column widthXS={Columns.Twelve}>
<Form.Element
label="Password"
required={true}
errorMessage={passwordValidation.errorMessage}
>
<VisibilityInput
name="password"
value={password}
size={ComponentSize.Large}
onChange={handleInputChange}
visible={isVisible}
status={
passwordValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onToggleClick={() => toggleVisibility(!isVisible)}
/>
</Form.Element>
</Grid.Column>
</Grid.Row>
</Grid>
<a onClick={handleForgotPasswordClick} className="login--forgot-password">
Forgot Password?
</a>
<Button
className="create-account--button"
text="Login"
color={ComponentColor.Primary}
size={ComponentSize.Large}
type={ButtonType.Submit}
status={buttonStatus}
shape={ButtonShape.StretchToFit}
/>
</>
)
}

View File

@ -0,0 +1,173 @@
// Libraries
import React, {FC, useState, ChangeEvent} from 'react'
import {
Button,
ButtonShape,
ButtonType,
Columns,
ComponentColor,
ComponentSize,
ComponentStatus,
Form,
Grid,
Input,
InputType,
VisibilityInput,
} from '@influxdata/clockface'
// Types
import {FormFieldValidation} from 'src/types'
interface Props {
buttonStatus: ComponentStatus
confirmPassword: string
confirmPasswordValidation: FormFieldValidation
email: string
emailValidation: FormFieldValidation
firstName: string
firstNameValidation: FormFieldValidation
lastName: string
lastNameValidation: FormFieldValidation
password: string
passwordValidation: FormFieldValidation
handleInputChange: (event: ChangeEvent<HTMLInputElement>) => void
}
export const SignUpForm: FC<Props> = ({
buttonStatus,
confirmPassword,
confirmPasswordValidation,
email,
emailValidation,
firstName,
firstNameValidation,
lastName,
lastNameValidation,
password,
passwordValidation,
handleInputChange,
}) => {
const [isVisible, toggleVisibility] = useState(false)
return (
<>
<Grid>
<Grid.Row className="sign-up--form-padded-row">
<Grid.Column widthXS={Columns.Six}>
<Form.Element
label="First Name"
required={true}
errorMessage={firstNameValidation.errorMessage}
>
<Input
name="firstName"
value={firstName}
autoFocus={true}
size={ComponentSize.Large}
status={
firstNameValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onChange={handleInputChange}
/>
</Form.Element>
</Grid.Column>
<Grid.Column widthXS={Columns.Six}>
<Form.Element
label="Last Name"
required={true}
errorMessage={lastNameValidation.errorMessage}
>
<Input
name="lastName"
value={lastName}
size={ComponentSize.Large}
status={
lastNameValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onChange={handleInputChange}
/>
</Form.Element>
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column widthXS={Columns.Twelve}>
<Form.Element
label="Work Email Address"
required={true}
errorMessage={emailValidation.errorMessage}
>
<Input
name="email"
value={email}
type={InputType.Email}
size={ComponentSize.Large}
status={
emailValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onChange={handleInputChange}
/>
</Form.Element>
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column widthXS={Columns.Twelve}>
<Form.Element
label="Password"
required={true}
errorMessage={passwordValidation.errorMessage}
>
<VisibilityInput
name="password"
value={password}
size={ComponentSize.Large}
onChange={handleInputChange}
visible={isVisible}
status={
passwordValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onToggleClick={() => toggleVisibility(!isVisible)}
/>
</Form.Element>
</Grid.Column>
<Grid.Column widthXS={Columns.Twelve}>
<Form.Element
label="Confirm Password"
required={true}
errorMessage={confirmPasswordValidation.errorMessage}
>
<VisibilityInput
name="confirmPassword"
value={confirmPassword}
size={ComponentSize.Large}
onChange={handleInputChange}
visible={isVisible}
status={
confirmPasswordValidation.hasError
? ComponentStatus.Error
: ComponentStatus.Default
}
onToggleClick={() => toggleVisibility(!isVisible)}
/>
</Form.Element>
</Grid.Column>
</Grid.Row>
</Grid>
<Button
className="create-account--button"
text="Create Account"
color={ComponentColor.Primary}
size={ComponentSize.Large}
type={ButtonType.Submit}
status={buttonStatus}
shape={ButtonShape.StretchToFit}
/>
</>
)
}

View File

@ -0,0 +1,166 @@
.clockface--app-wrapper.sign-up--page {
background-image: linear-gradient(
47deg,
rgba(19, 0, 45, 0) 65%,
rgba(191, 47, 229, 0.4) 100%
);
background-color: #13002d;
}
.create-account--button {
margin: 15px 0;
}
.github-logo {
width: $icon-size-md;
height: $icon-size-md;
fill: $g20-white;
}
.google-logo {
height: $icon-size-md;
width: $icon-size-md;
.google-logo--blue {
fill: #3e82f1;
}
.google-logo--green {
fill: #32a753;
}
.google-logo--red {
fill: #e74133;
}
.google-logo--yellow {
fill: #f9bb00;
}
}
.login--forgot-password {
color: $c-pool;
font-family: Roboto;
font-style: normal;
font-weight: 500;
font-size: 13px;
line-height: 15px;
cursor: pointer;
}
.sign-up--form {
min-width: 330px;
width: 100%;
height: 100%;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
.sign-up--form-padded-row {
padding-top: $ix-marg-d;
}
}
.sign-up--form-panel {
max-width: 500px;
width: 400px;
height: 100%;
justify-content: space-between;
}
.sign-up--login-container {
padding: $ix-marg-b;
text-align: center;
color: $g20-white;
}
.sign-up--login-text {
font-size: 16px;
margin-bottom: 0;
}
.sign-up--page-contents {
.cf-page-contents--padding {
height: 100vh;
display: inline-flex;
width: 100%;
}
}
.sign-up--panel {
height: 100%;
max-width: 1600px;
min-height: 670px + $ix-marg-d;
padding: $ix-marg-d;
margin: auto;
z-index: 0;
background: transparent !important;
z-index: 100;
}
@media screen and (min-width: $grid--breakpoint-md) {
.sign-up--form-panel {
max-width: 100%;
}
.sign-up--full-height {
min-height: 100%;
}
.sign-up--header-align {
text-align: center;
}
.sign-up--image {
display: inline-block;
position: fixed;
z-index: 0;
top: 20%;
right: 7%;
width: 160%;
height: 160%;
}
.sign-up--panel:not(.aws) {
height: 80vh;
padding: 64px;
}
.sign-up--panel {
max-height: 800px;
}
}
.sign-up--social-button-group {
position: relative;
top: $ix-marg-b;
text-align: center;
.signup-text {
position: relative;
right: $ix-marg-c;
}
.signup-icon {
width: $form-sm-height;
height: $form-sm-height;
background: transparent;
position: relative;
float: left;
top: $icon-margin;
}
}
.sign-up--social-header {
text-align: center;
font-size: 16px;
font-weight: 400;
margin: 0;
line-height: 0;
}
.sign-up--or {
position: relative;
top: 7px;
}

View File

@ -0,0 +1,52 @@
// Libraries
import React, {FC} from 'react'
import {
AlignItems,
AppWrapper,
Columns,
FlexBox,
FlexDirection,
Grid,
JustifyContent,
Page,
Panel,
} from '@influxdata/clockface'
// Components
import ErrorBoundary from 'src/shared/components/ErrorBoundary'
import LoginPageContents from 'src/onboarding/containers/LoginPageContents'
export const LoginPage: FC = () => (
<ErrorBoundary>
<AppWrapper className="sign-up--page">
<Page titleTag="Sign Up for InfluxDB Cloud">
<Page.Contents
scrollable={true}
fullWidth={true}
className="sign-up--page-contents"
>
<Panel className="sign-up--panel">
<FlexBox
direction={FlexDirection.Column}
stretchToFitHeight={true}
justifyContent={JustifyContent.Center}
alignItems={AlignItems.Center}
>
<Grid.Row className="sign-up--full-height">
<Grid.Column
widthXS={Columns.Twelve}
widthMD={Columns.Five}
offsetMD={Columns.Four}
widthLG={Columns.Four}
className="sign-up--full-height"
>
<LoginPageContents />
</Grid.Column>
</Grid.Row>
</FlexBox>
</Panel>
</Page.Contents>
</Page>
</AppWrapper>
</ErrorBoundary>
)

View File

@ -0,0 +1,459 @@
// Libraries
import React, {PureComponent, ChangeEvent, FormEvent} from 'react'
import {connect} from 'react-redux'
import {
AlignItems,
ComponentColor,
ComponentSize,
ComponentStatus,
FlexBox,
FlexDirection,
Grid,
JustifyContent,
Method,
Panel,
SelectGroup,
} from '@influxdata/clockface'
import auth0js, {WebAuth} from 'auth0-js'
// Components
import {LoginForm} from 'src/onboarding/components/LoginForm'
import {SignUpForm} from 'src/onboarding/components/SignUpForm'
import {SocialButton} from 'src/shared/components/SocialButton'
import {GoogleLogo, GithubLogo} from 'src/clientLibraries/graphics'
import {Transition, animated} from 'react-spring/renderprops'
// Types
import {Auth0Connection, FormFieldValidation} from 'src/types'
// APIs & Actions
import {notify} from 'src/shared/actions/notifications'
import {passwordResetSuccessfully} from 'src/shared/copy/notifications'
import {getAuth0Config} from 'src/authorizations/apis'
interface ErrorObject {
[key: string]: string | undefined
}
interface DispatchProps {
onNotify: typeof notify
}
enum ActiveTab {
SignUp = 'signup',
Login = 'login',
}
interface State {
activeTab: ActiveTab
buttonStatus: ComponentStatus
confirmPassword: string
confirmPasswordError: string
email: string
emailError: string
firstName: string
firstNameError: string
lastName: string
lastNameError: string
password: string
passwordError: string
}
class LoginPageContents extends PureComponent<DispatchProps> {
private auth0?: typeof WebAuth
state: State = {
activeTab: ActiveTab.Login,
buttonStatus: ComponentStatus.Default,
confirmPassword: '',
confirmPasswordError: '',
email: '',
emailError: '',
firstName: '',
firstNameError: '',
lastName: '',
lastNameError: '',
password: '',
passwordError: '',
}
public async componentDidMount() {
try {
const config = await getAuth0Config()
this.auth0 = auth0js.WebAuth({
domain: config.domain,
clientID: config.clientID,
redirectUri: config.redirectURL,
responseType: 'code',
})
} catch (error) {
console.error(error)
throw error
}
}
render() {
const {
activeTab,
buttonStatus,
confirmPassword,
confirmPasswordError,
email,
emailError,
firstName,
firstNameError,
lastName,
lastNameError,
password,
passwordError,
} = this.state
const loginTabActive = activeTab === ActiveTab.Login
return (
<form
action="/signup"
method={Method.Post}
onSubmit={this.handleSubmit}
className="sign-up--form"
>
<div className="sign-up--login-container">
<h2>Create your Free InfluxDB Cloud Account</h2>
<p className="sign-up--login-text">No credit card required</p>
</div>
<Panel className="sign-up--form-panel">
<Panel.Header size={ComponentSize.Large}>
<Grid>
<Grid.Row>
<p className="sign-up--social-header">Continue with</p>
</Grid.Row>
<Grid.Row className="sign-up--social-button-group">
<FlexBox
stretchToFitWidth={true}
direction={FlexDirection.Column}
justifyContent={JustifyContent.Center}
alignItems={AlignItems.Center}
margin={ComponentSize.Large}
>
<SocialButton
handleClick={() => {
this.handleSocialClick(Auth0Connection.Google)
}}
buttonText="Google"
>
<GoogleLogo className="signup-icon" />
</SocialButton>
<SocialButton
buttonText="Github"
handleClick={() => {
this.handleSocialClick(Auth0Connection.Github)
}}
>
<GithubLogo className="signup-icon" />
</SocialButton>
</FlexBox>
</Grid.Row>
</Grid>
</Panel.Header>
<div className="sign-up--or">
<p className="sign-up--social-header">OR</p>
</div>
<Panel.Body size={ComponentSize.Large}>
<div>
<FlexBox
stretchToFitWidth={true}
direction={FlexDirection.Row}
justifyContent={JustifyContent.Center}
>
<SelectGroup
size={ComponentSize.Large}
color={ComponentColor.Default}
>
<SelectGroup.Option
titleText="Login"
value={ActiveTab.Login}
id="login-option"
active={loginTabActive}
onClick={this.handleTabChange}
>
Login
</SelectGroup.Option>
<SelectGroup.Option
titleText="Sign Up"
value={ActiveTab.SignUp}
id="signup-option"
active={!loginTabActive}
onClick={this.handleTabChange}
>
Sign Up
</SelectGroup.Option>
</SelectGroup>
</FlexBox>
</div>
<Transition
native
reset
unique
items={loginTabActive}
from={{height: 0}}
enter={[
{
position: 'relative',
overflow: 'hidden',
height: 'auto',
},
]}
leave={{height: 0}}
>
{shouldShow =>
shouldShow &&
(props => (
<animated.div style={props}>
<LoginForm
buttonStatus={buttonStatus}
email={email}
emailValidation={this.formFieldTypeFactory(emailError)}
password={password}
passwordValidation={this.formFieldTypeFactory(
passwordError
)}
handleInputChange={this.handleInputChange}
handleForgotPasswordClick={this.handleForgotPasswordClick}
/>
</animated.div>
))
}
</Transition>
<Transition
native
reset
unique
items={loginTabActive === false}
from={{height: 0}}
enter={[
{
position: 'relative',
overflow: 'hidden',
height: 'auto',
},
]}
leave={{height: 0}}
>
{shouldShow =>
shouldShow &&
(props => (
<animated.div style={props}>
<SignUpForm
buttonStatus={buttonStatus}
confirmPassword={confirmPassword}
confirmPasswordValidation={this.formFieldTypeFactory(
confirmPasswordError
)}
email={email}
emailValidation={this.formFieldTypeFactory(emailError)}
firstName={firstName}
firstNameValidation={this.formFieldTypeFactory(
firstNameError
)}
lastName={lastName}
lastNameValidation={this.formFieldTypeFactory(
lastNameError
)}
password={password}
passwordValidation={this.formFieldTypeFactory(
passwordError
)}
handleInputChange={this.handleInputChange}
/>
</animated.div>
))
}
</Transition>
</Panel.Body>
</Panel>
</form>
)
}
private get validateFieldValues(): {
isValid: boolean
errors: {[fieldName: string]: string}
} {
const {
activeTab,
firstName,
lastName,
email,
password,
confirmPassword,
} = this.state
const passwordsMatch = confirmPassword === password
const firstNameError = firstName === '' ? 'First name is required' : ''
const lastNameError = lastName === '' ? 'Last name is required' : ''
const emailError = email === '' ? 'Email is required' : ''
const passwordError = password === '' ? 'Password is required' : ''
let confirmPasswordError = passwordsMatch
? ''
: "The input passwords don't match"
if (confirmPassword === '') {
confirmPasswordError = 'Confirm password is required'
}
const errors: ErrorObject = {
emailError,
passwordError,
}
if (activeTab === ActiveTab.SignUp) {
errors.firstNameError = firstNameError
errors.lastNameError = lastNameError
errors.confirmPasswordError = confirmPasswordError
}
const isValid = Object.values(errors).every(error => error === '')
return {isValid, errors}
}
private formFieldTypeFactory = (
errorMessage: string
): FormFieldValidation => ({
errorMessage,
hasError: errorMessage !== '',
})
private handleSubmit = (event: FormEvent) => {
const {isValid, errors} = this.validateFieldValues
const {
email,
password,
firstName: given_name,
lastName: family_name,
activeTab,
} = this.state
event.preventDefault()
if (!isValid) {
this.setState(errors)
return
}
this.setState({buttonStatus: ComponentStatus.Loading})
if (activeTab === ActiveTab.Login) {
this.auth0.login(
{
realm: Auth0Connection.Authentication,
email,
password,
},
error => {
if (error) {
this.setState({buttonStatus: ComponentStatus.Default})
return this.displayErrorMessage(errors, error)
}
}
)
return
}
this.auth0.signup(
{
connection: Auth0Connection.Authentication,
email,
password,
family_name,
given_name,
},
error => {
if (error) {
this.displayErrorMessage(errors, error)
this.setState({buttonStatus: ComponentStatus.Default})
return
}
// log the user into Auth0
this.auth0.login(
{
realm: Auth0Connection.Authentication,
email,
password,
},
error => {
if (error) {
this.setState({buttonStatus: ComponentStatus.Default})
this.displayErrorMessage(errors, error)
return
}
}
)
}
)
}
private displayErrorMessage = (errors, auth0Err) => {
// eslint-disable-next-line
if (/error in email/.test(auth0Err.code)) {
this.setState({
...errors,
emailError: 'Please enter a valid email address',
})
} else if (auth0Err.code === 'user_exists') {
const emailError = `An account with that email address already exists. Try logging in instead.`
this.setState({...errors, emailError})
} else {
const emailError = `We have been notified of an issue while creating your account. If this issue persists, please contact support@influxdata.com`
this.setState({...errors, emailError})
}
}
private handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
this.setState({[event.target.name]: event.target.value})
}
private handleTabChange = (value: ActiveTab) => {
this.setState({activeTab: value})
}
private handleSocialClick = (connection: Auth0Connection) => {
this.auth0.authorize({
connection,
})
}
private handleForgotPasswordClick = () => {
const {email} = this.state
const {onNotify} = this.props
if (!email) {
this.setState({emailError: 'Please enter a valid email address'})
return
}
this.auth0.changePassword(
{
email,
connection: Auth0Connection.Authentication,
},
(error, successMessage) => {
if (error) {
this.setState({emailError: error.message})
return
}
// notify user that change password email was sent successfully
// By default auth0 will send a success message even if the operation fails:
// https://community.auth0.com/t/auth0-changepassword-always-returns-ok-even-when-user-is-not-found/11081/8
onNotify(passwordResetSuccessfully(successMessage))
}
)
}
}
const mdtp: DispatchProps = {
onNotify: notify,
}
export default connect<{}, DispatchProps>(
null,
mdtp
)(LoginPageContents)

View File

@ -0,0 +1,26 @@
// Libraries
import React, {FC} from 'react'
import {ButtonBase, ButtonShape, ComponentSize} from '@influxdata/clockface'
interface Props {
buttonText: string
children: JSX.Element
handleClick?: () => void
}
export const SocialButton: FC<Props> = ({
buttonText,
children,
handleClick,
}) => {
return (
<ButtonBase
onClick={handleClick}
size={ComponentSize.Large}
shape={ButtonShape.StretchToFit}
>
{children}
<span className="signup-text">{buttonText}</span>
</ButtonBase>
)
}

View File

@ -722,6 +722,12 @@ export const authorizationCreateSuccess = (): Notification => ({
message: 'Token was created successfully',
})
export const passwordResetSuccessfully = (message: string): Notification => ({
...defaultSuccessNotification,
message: `${message}
If you haven't received an email, please ensure that the email you provided is correct.`,
})
export const authorizationCreateFailed = (): Notification => ({
...defaultErrorNotification,
message: 'Failed to create tokens',

View File

@ -7,6 +7,7 @@ export const OSS_FLAGS = {
telegrafEditor: false,
customCheckQuery: false,
matchingNotificationRules: false,
regionBasedLoginPage: false,
}
export const CLOUD_FLAGS = {
@ -17,6 +18,7 @@ export const CLOUD_FLAGS = {
telegrafEditor: false,
customCheckQuery: false,
matchingNotificationRules: false,
regionBasedLoginPage: false,
}
export const isFlagEnabled = (flagName: string, equals?: string | boolean) => {

View File

@ -60,7 +60,7 @@ $ix-link-warning-hover: $c-thunder;
$ix-link-info: $c-star;
$ix-link-info-hover: $c-comet;
$ix-link-danger: $c-curacao;
$ix-link-danger-hover: $c-dreamsicle;
$ix-link-danger-hover: $c-dreamsicle;
$ix-text-default: $g13-mist;
$ix-text-light: $g13-mist;
@ -77,9 +77,21 @@ $ix-text-tiny: 11px;
$ix-text-base: 12px;
$ix-text-base-1: (ceil($ix-text-base * $ix-text-scale));
$ix-text-base-2: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale));
$ix-text-base-3: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale));
$ix-text-base-4: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale * $ix-text-scale));
$ix-text-base-5: (ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale * $ix-text-scale * $ix-text-scale));
$ix-text-base-3: (
ceil($ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale)
);
$ix-text-base-4: (
ceil(
$ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale *
$ix-text-scale
)
);
$ix-text-base-5: (
ceil(
$ix-text-base * $ix-text-scale * $ix-text-scale * $ix-text-scale *
$ix-text-scale * $ix-text-scale
)
);
/* Form Element Sizing */
@ -107,4 +119,8 @@ $default-font: 'Roboto', Helvetica, sans-serif;
$code-font: 'RobotoMono', monospace;
/* Event Markers */
$event-marker-height: 20px;
$event-marker-height: 20px;
/* Icon Formatting */
$icon-margin: 6px;
$icon-size-md: 70px;

View File

@ -109,6 +109,7 @@
@import 'src/shared/components/dapperScrollbars/DapperScrollbars.scss';
@import 'src/templates/components/createFromTemplateOverlay/CreateFromTemplateOverlay.scss';
@import 'src/onboarding/components/SigninForm.scss';
@import 'src/onboarding/containers/LoginPage.scss';
@import 'src/shared/components/ThresholdsSettings.scss';
@import 'src/shared/components/ThresholdMarkers.scss';
@import 'src/shared/components/EventMarkers.scss';

View File

@ -1 +1,13 @@
export {Authorization, Permission} from 'src/client'
export enum Auth0Connection {
Google = 'google-oauth2',
Github = 'github',
Authentication = 'Username-Password-Authentication',
}
export type Auth0Config = {
clientID: string
domain: string
redirectURL: string
}

4
ui/src/types/form.ts Normal file
View File

@ -0,0 +1,4 @@
export interface FormFieldValidation {
hasError: boolean
errorMessage: string
}

View File

@ -12,6 +12,7 @@ export * from './dataExplorer'
export * from './dataLoaders'
export * from './filterEditor'
export * from './flux'
export * from './form'
export * from './histogram'
export * from './hosts'
export * from './influxAdmin'

View File

@ -915,6 +915,13 @@
dependencies:
regenerator-runtime "^0.12.0"
"@babel/runtime@^7.3.1":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.4.tgz#d79f5a2040f7caa24d53e563aad49cbc05581308"
integrity sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==
dependencies:
regenerator-runtime "^0.13.2"
"@babel/template@^7.1.0", "@babel/template@^7.2.2", "@babel/template@^7.4.0", "@babel/template@^7.4.4":
version "7.4.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
@ -2184,6 +2191,19 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
auth0-js@^9.12.2:
version "9.12.2"
resolved "https://registry.yarnpkg.com/auth0-js/-/auth0-js-9.12.2.tgz#8227259a94e8a47eecf8d7a630d99669049833a6"
integrity sha512-0VfPu5UcgkGKQc7Q8KPqgkqqhLgXGsDCro2tde7hHPYK9JEzOyq82v0szUTHWlwQE1VT8K2/qZAsGDf7hFjI7g==
dependencies:
base64-js "^1.3.0"
idtoken-verifier "^2.0.1"
js-cookie "^2.2.0"
qs "^6.7.0"
superagent "^3.8.3"
url-join "^4.0.1"
winchan "^0.2.2"
autoprefixer@^6.3.1:
version "6.7.7"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
@ -2320,6 +2340,11 @@ base64-js@^1.0.2:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==
base64-js@^1.3.0:
version "1.3.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
base@^0.11.1:
version "0.11.2"
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
@ -3256,7 +3281,7 @@ commondir@^1.0.1:
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
component-emitter@^1.2.1:
component-emitter@^1.2.0, component-emitter@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
@ -3370,6 +3395,11 @@ cookie@0.4.0:
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba"
integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==
cookiejar@^2.1.0:
version "2.1.2"
resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c"
integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==
copy-concurrently@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
@ -3543,6 +3573,11 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
crypto-js@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b"
integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q==
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@ -4438,6 +4473,11 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es6-promise@^4.2.8:
version "4.2.8"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@ -5172,6 +5212,15 @@ fork-ts-checker-webpack-plugin@^1.4.3:
tapable "^1.0.0"
worker-rpc "^0.1.0"
form-data@^2.3.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@ -5181,6 +5230,11 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
formidable@^1.2.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.1.tgz#70fb7ca0290ee6ff961090415f4b3df3d2082659"
integrity sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==
forwarded@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
@ -5941,6 +5995,18 @@ identity-obj-proxy@^3.0.0:
dependencies:
harmony-reflect "^1.4.6"
idtoken-verifier@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/idtoken-verifier/-/idtoken-verifier-2.0.2.tgz#7fd1c64c435abf07e92f137e7ac538a758fdc399"
integrity sha512-9UN83SKT9dtN3d7vNz3EMTqoaJi3D02Zg5XMqF6+bLrGL+Akbx4oj4SEWsgXtLF6cy46XrUcVzokFY+SWO+/MA==
dependencies:
base64-js "^1.3.0"
crypto-js "^3.2.1"
es6-promise "^4.2.8"
jsbn "^1.1.0"
unfetch "^4.1.0"
url-join "^4.0.1"
ieee754@^1.1.4:
version "1.1.12"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b"
@ -6957,6 +7023,11 @@ js-base64@^2.1.9:
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.9.tgz#748911fb04f48a60c4771b375cac45a80df11c03"
integrity sha512-xcinL3AuDJk7VSzsHgb9DvvIXayBbadtMZ4HFPx8rUszbW1MuNMlwYVC4zzCZ6e1sqZpnNS5ZFYOhXqA39T7LQ==
js-cookie@^2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8"
integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==
js-levenshtein@^1.1.3:
version "1.1.6"
resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
@ -6980,6 +7051,11 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
jsbn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
integrity sha1-sBMHyym2GKHtJux56RH4A8TaAEA=
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@ -7639,7 +7715,7 @@ merge-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
methods@~1.1.2:
methods@^1.1.1, methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
@ -7705,7 +7781,7 @@ mime@1.4.1:
resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==
mime@1.6.0:
mime@1.6.0, mime@^1.4.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
@ -9511,6 +9587,11 @@ qs@6.7.0:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
qs@^6.5.1, qs@^6.7.0:
version "6.9.1"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
qs@^6.5.2, qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
@ -9838,6 +9919,14 @@ react-scrollbars-custom@^4.0.0-alpha.8:
cnbuilder "^1.0.8"
react-draggable "^3.2.1"
react-spring@^8.0.27:
version "8.0.27"
resolved "https://registry.yarnpkg.com/react-spring/-/react-spring-8.0.27.tgz#97d4dee677f41e0b2adcb696f3839680a3aa356a"
integrity sha512-nDpWBe3ZVezukNRandTeLSPcwwTMjNVu1IDq9qA/AMiUqHuRN4BeSWvKr3eIxxg1vtiYiOLy4FqdfCP5IoP77g==
dependencies:
"@babel/runtime" "^7.3.1"
prop-types "^15.5.8"
react-test-renderer@^16.0.0-0:
version "16.5.2"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.5.2.tgz#92e9d2c6f763b9821b2e0b22f994ee675068b5ae"
@ -9906,6 +9995,19 @@ read-pkg@^3.0.0:
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^2.3.5:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
readable-stream@^3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.0.6.tgz#351302e4c68b5abd6a2ed55376a7f9a25be3057a"
@ -11147,6 +11249,22 @@ stylehacks@^4.0.0:
postcss "^7.0.0"
postcss-selector-parser "^3.0.0"
superagent@^3.8.3:
version "3.8.3"
resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.3.tgz#460ea0dbdb7d5b11bc4f78deba565f86a178e128"
integrity sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==
dependencies:
component-emitter "^1.2.0"
cookiejar "^2.1.0"
debug "^3.1.0"
extend "^3.0.0"
form-data "^2.3.1"
formidable "^1.2.0"
methods "^1.1.1"
mime "^1.4.1"
qs "^6.5.1"
readable-stream "^2.3.5"
supports-color@5.4.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
@ -11712,6 +11830,11 @@ underscore@~1.4.4:
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
integrity sha1-YaajIBBiKvoHljvzJSA88SI51gQ=
unfetch@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db"
integrity sha512-crP/n3eAPUJxZXM9T80/yv0YhkTEx2K1D3h7D1AJM6fzsWZrxdyRuLN0JH/dkZh1LNH8LxCnBzoPFCPbb2iGpg==
unherit@^1.0.4:
version "1.1.1"
resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c"
@ -11885,6 +12008,11 @@ urix@^0.1.0:
resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
url-join@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==
url-parse@^1.4.3:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
@ -12336,6 +12464,11 @@ wide-align@^1.1.0:
dependencies:
string-width "^1.0.2 || 2"
winchan@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/winchan/-/winchan-0.2.2.tgz#6766917b88e5e1cb75f455ffc7cc13f51e5c834e"
integrity sha512-pvN+IFAbRP74n/6mc6phNyCH8oVkzXsto4KCHPJ2AScniAnA1AmeLI03I2BzjePpaClGSI4GUMowzsD3qz5PRQ==
window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"