Commit 5171e717 authored by David Burke's avatar David Burke

Reset password step 1 almost done

parent 30d40367
Pipeline #30980759 failed with stage
in 4 minutes and 23 seconds
import "@storybook/addon-knobs/register";
......@@ -19,6 +19,7 @@ import * as fromRegister from "./register/register.reducer";
import * as fromChangePassword from "./change-password/change-password.reducer";
import * as fromDeleteAccount from "./delete/delete.reducer";
import * as fromErrorReporting from "./error-reporting/error-reporting.reducer";
import * as fromForgotPassword from "./forgot-password/forgot-password.reducer";
export interface IAuthState {
email: string | null;
......@@ -93,6 +94,7 @@ export interface IAccountState {
changePassword: fromChangePassword.IChangePasswordState;
deleteAccount: fromDeleteAccount.IDeleteAccountState;
errorReporting: fromErrorReporting.IState;
forgotPassword: fromForgotPassword.IForgotPasswordState;
}
export const reducers: ActionReducerMap<IAccountState> = {
......@@ -102,7 +104,8 @@ export const reducers: ActionReducerMap<IAccountState> = {
confirmEmail: fromConfirmEmail.reducer,
changePassword: fromChangePassword.reducer,
deleteAccount: fromDeleteAccount.reducer,
errorReporting: fromErrorReporting.reducer
errorReporting: fromErrorReporting.reducer,
forgotPassword: fromForgotPassword.reducer
};
export const selectAccountState = createFeatureSelector<IAccountState>(
......@@ -281,3 +284,24 @@ export const getErrorReportingHasStarted = createSelector(
selectErrorReportingState,
fromErrorReporting.getHasStarted
);
export const selectForgotPasswordState = createSelector(
selectAccountState,
(state: IAccountState) => state.forgotPassword
);
export const getForgotPasswordForm = createSelector(
selectForgotPasswordState,
fromForgotPassword.getForm
);
export const getForgotPasswordHasStarted = createSelector(
selectForgotPasswordState,
fromForgotPassword.getHasStarted
);
export const getForgotPasswordHasFinished = createSelector(
selectForgotPasswordState,
fromForgotPassword.getHasFinished
);
export const getForgotPasswordErrorMessage = createSelector(
selectForgotPasswordState,
fromForgotPassword.getErrorMessage
);
import { Action } from "@ngrx/store";
export enum ForgotPasswordActionTypes {
SUBMIT_FORM = "[Forgot Password] Submit",
SUBMIT_FORM_SUCCESS = "[Forgot Password] Submit Success",
SUBMIT_FORM_FAILURE = "[Forgot Password] Submit Failure",
RESET_FORM = "[Forgot Password] Reset"
}
export class SubmitForm implements Action {
readonly type = ForgotPasswordActionTypes.SUBMIT_FORM;
}
export class SubmitFormSuccess implements Action {
readonly type = ForgotPasswordActionTypes.SUBMIT_FORM_SUCCESS;
}
export class SubmitFormFailure implements Action {
readonly type = ForgotPasswordActionTypes.SUBMIT_FORM_FAILURE;
}
export class ResetForm implements Action {
readonly type = ForgotPasswordActionTypes.RESET_FORM;
}
export type ForgotPasswordActionsUnion =
| SubmitForm
| SubmitFormSuccess
| SubmitFormFailure
| ResetForm;
<app-marketing-frame>
forgot-password works!
<div class="account-column__inner account-column__inner--right">
<div class="account-column__right-heading">
<h1 class="heading-medium">Reset&nbsp;Password</h1>
<div class="account-column__link">
<a class="text-link text-link--caret text-link--large" id="btn-signin" [routerLink]="['/login']">
Remembered? Log&nbsp;in
</a>
</div>
</div>
<form [ngrxFormState]="form" (submit)="onSubmit()" class="auth-form__form">
<div *ngIf="!hasFinished">
<p>Start password recovery by entering your email. You’ll need your backup code.</p>
<label [for]="form.controls.email.id">
Email
</label>
<input
type="email"
[ngrxFormControlState]="form.controls.email"
autofocus
class="form-field__input"
[class.form-field__input--invalid]="form.errors._email && form.isSubmitted"
[class.form-field__input--valid]="!form.errors._email"
/>
</div>
<div *ngIf="hasFinished">
<p>
Great! An email was sent to {{ form.value.email }}. Check your inbox to continue. Make sure you have your backup code ready.
</p>
<a class="text-link text-link--caret text-link--large" (click)="reset.emit()">
Click here to start over
</a>
</div>
<div *ngIf="isExtension">
<app-checkbox
title="My company has its own Passit server"
subtext="If you signed up for Passit anywhere other than app.passit.io (e.g. passit.mycompany.com, or your self-hosted server), you’ll need to specify where you want to log&nbsp;in."
[control]="form.controls.showUrl"
></app-checkbox>
<div *ngIf="form.value.showUrl" class="form-field form-field--large">
<label [for]="form.controls.url.id" class="form-field__label">Server URL</label>
<input
type="url"
[ngrxFormControlState]="form.controls.url"
class="form-field__input"
[class.form-field__input--invalid]="form.errors._url"
[class.form-field__input--one-action]="form.controls.url.isValidationPending"
>
<div class="form-field__actions" *ngIf="form.controls.url.isValidationPending">
<progress-indicator
[inProgress]="form.controls.url.isValidationPending"
[inputAction]="true"></progress-indicator>
</div>
</div>
<ul class="form-field__error-list">
<li *ngIf="form.errors._url?.$exists" class="form-field__error">
Cannot connect to {{ form.errors._url?.$exists }}
</li>
</ul>
</div>
<div *ngIf="!hasFinished" class="auth-form__actions">
<button
id="loginSubmit"
class="button button--large button--green"
[disabled]="hasStarted"
type="submit">
Next
</button>
<progress-indicator
[inProgress]="hasStarted"
inProgressText="Working"
></progress-indicator>
</div>
</form>
</div>
</app-marketing-frame>
import { Component, OnInit } from "@angular/core";
import {
Component,
ChangeDetectionStrategy,
Input,
Output,
EventEmitter
} from "@angular/core";
import { FormGroupState } from "ngrx-forms";
import { IForgotPasswordForm } from "./forgot-password.reducer";
@Component({
selector: "app-forgot-password",
templateUrl: "./forgot-password.component.html",
styleUrls: ["./forgot-password.component.scss"]
styleUrls: ["./forgot-password.component.scss", "../account.component.scss"],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ForgotPasswordComponent implements OnInit {
export class ForgotPasswordComponent {
@Input()
isExtension: boolean;
@Input()
form: FormGroupState<IForgotPasswordForm>;
@Input()
errorMessage: string;
@Input()
hasStarted: boolean;
@Input()
hasFinished: boolean;
@Output()
submitEmail = new EventEmitter();
@Output()
reset = new EventEmitter();
constructor() {}
ngOnInit() {}
onSubmit() {
if (this.form.isValid) {
this.submitEmail.emit();
}
}
}
import { Component, ChangeDetectionStrategy } from "@angular/core";
import * as fromAccount from "../account.reducer";
import { Store } from "@ngrx/store";
import { SubmitForm, ResetForm } from "./forgot-password.actions";
import { IS_EXTENSION } from "../../constants";
@Component({
template: `
<app-forgot-password
[form]="form$ | async"
[isExtension]="isExtension"
[hasStarted]="hasStarted$ | async"
[hasFinished]="hasFinished$ | async"
[errorMessage]="errorMessage$ | async"
(submitEmail)="submitEmail()"
(reset)="reset()"
></app-forgot-password>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ForgotPasswordContainer {
form$ = this.store.select(fromAccount.getForgotPasswordForm);
hasStarted$ = this.store.select(fromAccount.getForgotPasswordHasStarted);
hasFinished$ = this.store.select(fromAccount.getForgotPasswordHasFinished);
errorMessage$ = this.store.select(fromAccount.getForgotPasswordErrorMessage);
isExtension = IS_EXTENSION;
constructor(private store: Store<fromAccount.IAuthState>) {}
submitEmail() {
this.store.dispatch(new SubmitForm());
}
reset() {
this.store.dispatch(new ResetForm());
}
}
import { Injectable } from "@angular/core";
import { Effect, Actions } from "@ngrx/effects";
import {
SubmitForm,
ForgotPasswordActionTypes,
SubmitFormSuccess,
SubmitFormFailure
} from "./forgot-password.actions";
import { withLatestFrom, map, exhaustMap, catchError } from "rxjs/operators";
import { Store } from "@ngrx/store";
import { IState } from "../../app.reducers";
import { getForgotPasswordForm } from "../account.reducer";
import { UserService } from "../user";
import { of } from "rxjs";
@Injectable()
export class ForgotPasswordEffects {
@Effect()
submitForm$ = this.actions$
.ofType<SubmitForm>(ForgotPasswordActionTypes.SUBMIT_FORM)
.pipe(
withLatestFrom(this.store.select(getForgotPasswordForm)),
map(([action, form]) => form),
exhaustMap(form =>
this.userService.forgotPassword(form.value.email).pipe(
map(() => new SubmitFormSuccess()),
catchError(err => of(new SubmitFormFailure()))
)
)
);
constructor(
private actions$: Actions,
private store: Store<IState>,
private userService: UserService
) {}
}
import {
updateGroup,
validate,
createFormGroupState,
FormGroupState,
createFormStateReducerWithUpdate
} from "ngrx-forms";
import { required, pattern } from "ngrx-forms/validation";
import { IBaseFormState } from "../../utils/interfaces";
import {
ForgotPasswordActionsUnion,
ForgotPasswordActionTypes
} from "./forgot-password.actions";
import { hashPassword } from "simple-asymmetric-js/js/crypto";
const FORM_ID = "Forgot Password Form";
export interface IForgotPasswordForm {
email: string;
showUrl: boolean;
url: string;
}
const validateAndUpdateFormState = updateGroup<IForgotPasswordForm>({
email: validate(required, pattern(/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/))
});
export const initialFormState = validateAndUpdateFormState(
createFormGroupState<IForgotPasswordForm>(FORM_ID, {
email: "",
showUrl: false,
url: ""
})
);
export interface IForgotPasswordState extends IBaseFormState {
form: FormGroupState<IForgotPasswordForm>;
errorMessage: string | null;
}
const initialState: IForgotPasswordState = {
form: initialFormState,
hasStarted: false,
hasFinished: false,
errorMessage: null
};
export const formReducer = createFormStateReducerWithUpdate<
IForgotPasswordForm
>(validateAndUpdateFormState);
export function reducer(
state = initialState,
action: ForgotPasswordActionsUnion
): IForgotPasswordState {
const form = formReducer(state.form, action);
state = { ...state, form };
switch (action.type) {
case ForgotPasswordActionTypes.SUBMIT_FORM:
return {
...state,
hasStarted: true,
hasFinished: false
};
case ForgotPasswordActionTypes.SUBMIT_FORM_SUCCESS:
return {
...state,
hasFinished: true
};
case ForgotPasswordActionTypes.SUBMIT_FORM_FAILURE:
return {
...state,
errorMessage: "An error occured",
hasFinished: false,
hasStarted: false
};
case ForgotPasswordActionTypes.RESET_FORM:
return initialState;
}
return state;
}
export const getForm = (state: IForgotPasswordState) => state.form;
export const getErrorMessage = (state: IForgotPasswordState) =>
state.errorMessage;
export const getHasStarted = (state: IForgotPasswordState) => state.hasStarted;
export const getHasFinished = (state: IForgotPasswordState) =>
state.hasFinished;
......@@ -35,6 +35,8 @@ import { ErrorReportingEffects } from "./error-reporting/error-reporting.effects
import { BackupCodeComponent } from "./backup-code/backup-code.component";
import { ForgotPasswordComponent } from "./forgot-password/forgot-password.component";
import { MarketingFrameComponent } from "./marketing-frame/marketing-frame.component";
import { ForgotPasswordContainer } from "./forgot-password/forgot-password.container";
import { ForgotPasswordEffects } from "./forgot-password/forgot-password.effects";
export const COMPONENTS = [
AccountComponent,
......@@ -52,6 +54,7 @@ export const COMPONENTS = [
RegisterContainer,
BackupCodeComponent,
ForgotPasswordComponent,
ForgotPasswordContainer,
MarketingFrameComponent
];
......@@ -73,7 +76,8 @@ export const SERVICES = [UserService, ConfirmEmailGuard];
LoginFormEffects,
RegisterEffects,
ConfirmEmailEffects,
ErrorReportingEffects
ErrorReportingEffects,
ForgotPasswordEffects
])
],
declarations: COMPONENTS,
......
......@@ -199,6 +199,14 @@ export class UserService {
});
}
public forgotPassword(email: string) {
const url = this.sdk.formUrl("reset-password/");
const data = {
email
};
return this.http.post(url, data);
}
/** call set_keys user service method give it public and private key */
public rehydrate() {
return this.store
......
......@@ -20,6 +20,7 @@ import { NoContentContainer } from "./no-content";
import { PopupLoggedInGuard } from "./guards/popup-logged-in.guard";
import { ConfirmEmailGuard } from "./account/confirm-email/confirm-email.guard";
import { ErrorReportingContainer } from "./account/error-reporting/error-reporting.container";
import { ForgotPasswordContainer } from "./account/forgot-password/forgot-password.container";
/* tslint:disable:object-literal-sort-keys */
const appRoutes: Routes = [
......@@ -88,6 +89,15 @@ const appRoutes: Routes = [
title: "Confirm Email"
}
},
{
path: "reset-password",
component: ForgotPasswordContainer,
canActivate: [AlreadyLoggedInGuard],
data: {
title: "Reset Password",
showNavBar: false
}
},
{
path: "list",
component: SecretListContainer,
......
......@@ -13,4 +13,9 @@ export class NgPassitSDK extends PassitSDK {
super();
this.api = api;
}
/** Get full URL, for example passing /api/my-thing/ might return https://passit.example.com */
formUrl(url: string) {
return this.api.baseUrl + url;
}
}
import { InlineSVGModule } from "ng-inline-svg";
import { HttpClientModule } from "@angular/common/http";
import { storiesOf, moduleMetadata } from "@storybook/angular";
import { boolean, withKnobs } from "@storybook/addon-knobs/angular";
import { StoreModule } from "@ngrx/store";
import { NgrxFormsModule } from "ngrx-forms";
......@@ -10,8 +11,11 @@ import { MarketingFrameComponent } from "../app/account/marketing-frame/marketin
import { LoginComponent } from "../app/account/login/login.component";
import { SharedModule } from "../app/shared";
import { ProgressIndicatorModule } from "../app/progress-indicator";
import * as fromForgotPassword from "../app/account/forgot-password/forgot-password.reducer";
import * as fromLogin from "../app/account/login/login.reducer";
storiesOf("Account", module)
.addDecorator(withKnobs)
.addDecorator(
moduleMetadata({
imports: [
......@@ -32,11 +36,18 @@ storiesOf("Account", module)
}
}))
.add("Forgot Password", () => ({
component: ForgotPasswordComponent
component: ForgotPasswordComponent,
props: {
isExtension: boolean("isExtension", true),
form: fromForgotPassword.initialFormState
}
}))
.add("Marketing Frame", () => ({
component: MarketingFrameComponent
}))
.add("Login", () => ({
component: LoginComponent
component: LoginComponent,
props: {
form: fromLogin.initialState.form
}
}));
......@@ -400,6 +400,23 @@
semver "^5.3.0"
semver-intersect "^1.1.2"
"@storybook/addon-knobs@^3.4.11":
version "3.4.11"
resolved "https://registry.yarnpkg.com/@storybook/addon-knobs/-/addon-knobs-3.4.11.tgz#13ca0c73f19c0a3fda1a1d1ff07ef00fa3a914b3"
dependencies:
"@storybook/components" "3.4.11"
babel-runtime "^6.26.0"
deep-equal "^1.0.1"
global "^4.3.2"
insert-css "^2.0.0"
lodash.debounce "^4.0.8"
moment "^2.21.0"
prop-types "^15.6.1"
react-color "^2.14.0"
react-datetime "^2.14.0"
react-textarea-autosize "^5.2.1"
util-deprecate "^1.0.2"
"@storybook/addons@3.4.11":
version "3.4.11"
resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-3.4.11.tgz#f3e27c46d80ad1f171888c4aad0a19a8a032d072"
......@@ -615,6 +632,12 @@
dependencies:
"@types/node" "*"
"@types/prop-types@*":
version "15.5.5"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.5.tgz#17038dd322c2325f5da650a94d5f9974943625e3"
dependencies:
"@types/react" "*"
"@types/q@^0.0.32":
version "0.0.32"
resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5"
......@@ -625,10 +648,23 @@
dependencies:
"@types/node" "*"
"@types/react@*":
version "16.4.14"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.14.tgz#47c604c8e46ed674bbdf4aabf82b34b9041c6a04"
dependencies:
"@types/prop-types" "*"
csstype "^2.2.0"
"@types/selenium-webdriver@^3.0.0":
version "3.0.10"
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.10.tgz#e98cc6f05b4b436277671c784ee2f9d05a634f9b"
"@types/storybook__addon-knobs@^3.4.1":
version "3.4.1"
resolved "https://registry.yarnpkg.com/@types/storybook__addon-knobs/-/storybook__addon-knobs-3.4.1.tgz#36a9ff38c50b00b198bfdc04f6c0aaf747e0ed86"
dependencies:
"@types/react" "*"
"@webassemblyjs/ast@1.4.3":
version "1.4.3"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.4.3.tgz#3b3f6fced944d8660273347533e6d4d315b5934a"
......@@ -3223,7 +3259,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-react-class@^15.6.2:
create-react-class@^15.5.2, create-react-class@^15.6.2:
version "15.6.3"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036"
dependencies:
......@@ -5920,6 +5956,10 @@ inquirer@^5.2.0:
strip-ansi "^4.0.0"
through "^2.3.6"
insert-css@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/insert-css/-/insert-css-2.0.0.tgz#eb5d1097b7542f4c79ea3060d3aee07d053880f4"
inside@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/inside/-/inside-1.0.0.tgz#db45e993573cdb3db70b9832e8285bad46424770"
......@@ -7078,7 +7118,7 @@ lodash@^3.8.0:
version "3.10.1"
resolved "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.2.1:
lodash@^4.0.1, lodash@^4.2.1:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
......@@ -7186,6 +7226,10 @@ marked@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.4.0.tgz#9ad2c2a7a1791f10a852e0112f77b571dce10c66"
material-colors@^1.2.1:
version "1.2.6"
resolved "https://registry.yarnpkg.com/material-colors/-/material-colors-1.2.6.tgz#6d1958871126992ceecc72f4bcc4d8f010865f46"
math-expression-evaluator@^1.2.14:
version "1.2.17"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac"
......@@ -7415,7 +7459,7 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
dependencies:
minimist "0.0.8"
moment@^2.10.6:
moment@^2.10.6, moment@^2.21.0:
version "2.22.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
......@@ -7885,6 +7929,10 @@ object-assign@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
object-assign@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1, object-assign@latest:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
......@@ -8838,7 +8886,7 @@ promise.prototype.finally@^3.1.0:
dependencies:
asap "~2.0.3"
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
......@@ -9092,6 +9140,25 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
react-color@^2.14.0:
version "2.14.1"
resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.14.1.tgz#db8ad4f45d81e74896fc2e1c99508927c6d084e0"
dependencies:
lodash "^4.0.1"
material-colors "^1.2.1"
prop-types "^15.5.10"
reactcss "^1.2.0"
tinycolor2 "^1.4.1"
react-datetime@^2.14.0:
version "2.15.0"
resolved "https://registry.yarnpkg.com/react-datetime/-/react-datetime-2.15.0.tgz#a8f7da6c58b6b45dbeea32d4e8485db17614e12c"
dependencies:
create-react-class "^15.5.2"
object-assign "^3.0.0"
prop-types "^15.5.7"
react-onclickoutside "^6.5.0"
react-dev-utils@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.2.tgz#7bb68d2c4f6ffe7ed1184c5b0124fcad692774d2"
......@@ -9166,6 +9233,10 @@ react-modal@^3.3.2:
react-lifecycles-compat "^3.0.0"
warning "^3.0.0"
react-onclickoutside@^6.5.0:
version "6.7.1"
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.7.1.tgz#6a5b5b8b4eae6b776259712c89c8a2b36b17be93"
react-split-pane@^0.1.77:
version "0.1.84"
resolved "https://registry.yarnpkg.com/react-split-pane/-/react-split-pane-0.1.84.tgz#b9c1499cbc40b09cf29953ee6f5ff1039d31906e"
......@@ -9181,6 +9252,12 @@ react-style-proptype@^3.0.0:
dependencies:
prop-types "^15.5.4"
react-textarea-autosize@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-5.2.1.tgz#2b78f9067180f41b08ac59f78f1581abadd61e54"
dependencies:
prop-types "^15.6.0"
react-transition-group@^2.0.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.4.0.tgz#1d9391fabfd82e016f26fabd1eec329dbd922b5a"
......@@ -9210,6 +9287,12 @@ react@^16.0.0:
prop-types "^15.6.2"
schedule "^0.5.0"
reactcss@^1.2.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
dependencies:
lodash "^4.0.1"
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
......@@ -11090,6 +11173,10 @@ timers-browserify@^2.0.4:
dependencies:
setimmediate "^1.0.4"
tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
tmp@0.0.30:
version "0.0.30"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment