diff options
43 files changed, 2725 insertions, 283 deletions
diff --git a/packages/anastasis-core/src/reducer-types.ts b/packages/anastasis-core/src/reducer-types.ts index f7ba9e0f1..1a443bf9b 100644 --- a/packages/anastasis-core/src/reducer-types.ts +++ b/packages/anastasis-core/src/reducer-types.ts @@ -23,7 +23,7 @@ export interface Policy { authentication_method: number; provider: string; }[]; -} +} export interface PolicyProvider { provider_url: string; @@ -51,7 +51,7 @@ export interface ReducerStateBackup { identity_attributes?: { [n: string]: string }; authentication_providers?: { [url: string]: AuthenticationProviderStatus }; authentication_methods?: AuthMethod[]; - required_attributes?: any; + required_attributes?: UserAttributeSpec[]; selected_continent?: string; selected_country?: string; secret_name?: string; @@ -133,7 +133,7 @@ export interface ReducerStateRecovery { selected_country?: string; currencies?: string[]; - required_attributes?: any; + required_attributes?: UserAttributeSpec[]; /** * Recovery information, used by the UI. diff --git a/packages/anastasis-webui/.gitignore b/packages/anastasis-webui/.gitignore index 5baa9defe..32d0a5057 100644 --- a/packages/anastasis-webui/.gitignore +++ b/packages/anastasis-webui/.gitignore @@ -2,3 +2,4 @@ node_modules /build /*.log /size-plugin.json +/storybook-static/ diff --git a/packages/anastasis-webui/.storybook/.babelrc b/packages/anastasis-webui/.storybook/.babelrc new file mode 100644 index 000000000..610b6f339 --- /dev/null +++ b/packages/anastasis-webui/.storybook/.babelrc @@ -0,0 +1,25 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + + /** + * + * @author Sebastian Javier Marchano (sebasjm) + */ +{ + "presets": [ + "preact-cli/babel" + ] +}
\ No newline at end of file diff --git a/packages/anastasis-webui/.storybook/main.js b/packages/anastasis-webui/.storybook/main.js new file mode 100644 index 000000000..f8e4bbcc7 --- /dev/null +++ b/packages/anastasis-webui/.storybook/main.js @@ -0,0 +1,57 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + + +module.exports = { + "stories": [ + "../src/**/*.stories.mdx", + "../src/**/*.stories.@(js|jsx|ts|tsx)" + ], + "addons": [ + "@storybook/preset-scss", + "@storybook/addon-a11y", + "@storybook/addon-essentials" //docs, control, actions, viewpot, toolbar, background + ], + // sb does not yet support new jsx transform by default + // https://github.com/storybookjs/storybook/issues/12881 + // https://github.com/storybookjs/storybook/issues/12952 + babel: async (options) => ({ + ...options, + presets: [ + ...options.presets, + [ + '@babel/preset-react', { + runtime: 'automatic', + }, + 'preset-react-jsx-transform' + ], + ], + }), + webpackFinal: (config) => { + // should be removed after storybook 6.3 + // https://github.com/storybookjs/storybook/issues/12853#issuecomment-821576113 + config.resolve.alias = { + react: "preact/compat", + "react-dom": "preact/compat", + }; + return config; + }, +}
\ No newline at end of file diff --git a/packages/anastasis-webui/.storybook/preview.js b/packages/anastasis-webui/.storybook/preview.js new file mode 100644 index 000000000..7cb9405ba --- /dev/null +++ b/packages/anastasis-webui/.storybook/preview.js @@ -0,0 +1,49 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import "../src/scss/main.scss" +import { TranslationProvider } from '../src/context/translation' +import { h } from 'preact'; + + +export const parameters = { + controls: { expanded: true }, +} + +export const globalTypes = { + locale: { + name: 'Locale', + description: 'Internationalization locale', + defaultValue: 'en', + toolbar: { + icon: 'globe', + items: [ + { value: 'en', right: 'πΊπΈ', title: 'English' }, + { value: 'es', right: 'πͺπΈ', title: 'Spanish' }, + ], + }, + }, +}; + +export const decorators = [ + (Story, { globals }) => { + document.body.parentElement.classList = "has-aside-left has-aside-mobile-transition has-navbar-fixed-top has-aside-expanded" + return <Story /> + }, + (Story, { globals }) => <TranslationProvider initial='en' forceLang={globals.locale}> + <Story /> + </TranslationProvider>, +]; diff --git a/packages/anastasis-webui/package.json b/packages/anastasis-webui/package.json index 8f7711313..57cfdd8d4 100644 --- a/packages/anastasis-webui/package.json +++ b/packages/anastasis-webui/package.json @@ -8,7 +8,9 @@ "serve": "sirv build --port 8080 --cors --single", "dev": "preact watch", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", - "test": "jest ./tests" + "test": "jest ./tests", + "build-storybook": "build-storybook", + "storybook": "start-storybook -p 6006" }, "eslintConfig": { "parser": "@typescript-eslint/parser", @@ -30,6 +32,12 @@ }, "devDependencies": { "@creativebulma/bulma-tooltip": "^1.2.0", + "@storybook/addon-a11y": "^6.2.9", + "@storybook/addon-actions": "^6.2.9", + "@storybook/addon-essentials": "^6.2.9", + "@storybook/addon-links": "^6.2.9", + "@storybook/preact": "^6.2.9", + "@storybook/preset-scss": "^1.0.3", "@types/enzyme": "^3.10.5", "@types/jest": "^26.0.8", "@typescript-eslint/eslint-plugin": "^2.25.0", diff --git a/packages/anastasis-webui/src/components/menu/SideBar.tsx b/packages/anastasis-webui/src/components/menu/SideBar.tsx index 628adb571..df582a5d0 100644 --- a/packages/anastasis-webui/src/components/menu/SideBar.tsx +++ b/packages/anastasis-webui/src/components/menu/SideBar.tsx @@ -20,7 +20,9 @@ */ -import { h, VNode } from 'preact'; +import { Fragment, h, VNode } from 'preact'; +import { BackupStates, RecoveryStates } from '../../../../anastasis-core/lib'; +import { useAnastasisContext } from '../../context/anastasis'; import { Translate } from '../../i18n'; import { LangSelector } from './LangSelector'; @@ -31,8 +33,9 @@ interface Props { export function Sidebar({ mobile }: Props): VNode { // const config = useConfigContext(); const config = { version: 'none' } - const process = { env : { __VERSION__: '0.0.0'}} - + const process = { env: { __VERSION__: '0.0.0' } } + const reducer = useAnastasisContext()! + return ( <aside class="aside is-placed-left is-expanded"> {mobile && <div class="footer" onClick={(e) => { return e.stopImmediatePropagation() }}> @@ -47,52 +50,117 @@ export function Sidebar({ mobile }: Props): VNode { </div> </div> <div class="menu is-menu-main"> - <p class="menu-label"> - <Translate>Back up a secret</Translate> - </p> + {!reducer.currentReducerState && + <p class="menu-label"> + <Translate>Backup or Recorver</Translate> + </p> + } <ul class="menu-list"> - <li> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-square-edit-outline" /></span> - <span class="menu-item-label"><Translate>Location & Currency</Translate></span> - </div> - </li> - <li class="is-active"> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-cash-register" /></span> - <span class="menu-item-label"><Translate>Personal information</Translate></span> - </div> - </li> - <li> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-shopping" /></span> - <span class="menu-item-label"><Translate>Authorization methods</Translate></span> - </div> - </li> - <li> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-bank" /></span> - <span class="menu-item-label"><Translate>Recovery policies</Translate></span> - </div> - </li> - <li> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-bank" /></span> - <span class="menu-item-label"><Translate>Enter secrets</Translate></span> - </div> - </li> - <li> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-bank" /></span> - <span class="menu-item-label"><Translate>Payment (optional)</Translate></span> - </div> - </li> - <li> - <div class="has-icon"> - <span class="icon"><i class="mdi mdi-cash" /></span> - <span class="menu-item-label">Backup completed</span> - </div> - </li> + {!reducer.currentReducerState && + <li> + <div class="ml-4"> + <span class="menu-item-label"><Translate>Start one options</Translate></span> + </div> + </li> + } + {reducer.currentReducerState && reducer.currentReducerState.backup_state ? <Fragment> + <li class={reducer.currentReducerState.backup_state === BackupStates.ContinentSelecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>Continent selection</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.CountrySelecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>Country selection</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.UserAttributesCollecting ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>User attributes</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.AuthenticationsEditing ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>Auth methods</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.PoliciesReviewing ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>PoliciesReviewing</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.SecretEditing ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>SecretEditing</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.PoliciesPaying ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>PoliciesPaying</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.BackupFinished ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>BackupFinished</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.backup_state === BackupStates.TruthsPaying ? 'is-active' : ''}> + <div class="ml-4"> + + <span class="menu-item-label"><Translate>TruthsPaying</Translate></span> + </div> + </li> + </Fragment> : (reducer.currentReducerState && reducer.currentReducerState?.recovery_state && <Fragment> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.ContinentSelecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>TruthsPaying</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.CountrySelecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>CountrySelecting</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.UserAttributesCollecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>UserAttributesCollecting</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.SecretSelecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>SecretSelecting</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.ChallengeSelecting ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>ChallengeSelecting</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.ChallengeSolving ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>ChallengeSolving</Translate></span> + </div> + </li> + <li class={reducer.currentReducerState.recovery_state === RecoveryStates.RecoveryFinished ? 'is-active' : ''}> + <div class="ml-4"> + <span class="menu-item-label"><Translate>RecoveryFinished</Translate></span> + </div> + </li> + </Fragment>)} + {reducer.currentReducerState && + <li> + <div class="buttons ml-4"> + <button class="button is-danger is-right" onClick={() => reducer.reset()}>Reset session</button> + </div> + </li> + } + </ul> </div> </aside> diff --git a/packages/anastasis-webui/src/context/anastasis.ts b/packages/anastasis-webui/src/context/anastasis.ts new file mode 100644 index 000000000..e7f93ed43 --- /dev/null +++ b/packages/anastasis-webui/src/context/anastasis.ts @@ -0,0 +1,41 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createContext, h, VNode } from 'preact'; +import { useContext } from 'preact/hooks'; +import { AnastasisReducerApi } from '../hooks/use-anastasis-reducer'; + +type Type = AnastasisReducerApi | undefined; + +const initial = undefined + +const Context = createContext<Type>(initial) + +interface Props { + value: AnastasisReducerApi; + children: any; +} + +export const AnastasisProvider = ({ value, children }: Props): VNode => { + return h(Context.Provider, { value, children }); +} + +export const useAnastasisContext = (): Type => useContext(Context);
\ No newline at end of file diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx new file mode 100644 index 000000000..d28a6df43 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.stories.tsx @@ -0,0 +1,63 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { AttributeEntryScreen as TestedComponent } from './AttributeEntryScreen'; + + +export default { + title: 'Pages/AttributeEntryScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const WithSomeAttributes = createExample(TestedComponent, { + ...reducerStatesExample.attributeEditing, + required_attributes: [{ + name: 'first', + label: 'first', + type: 'type', + uuid: 'asdasdsa1', + widget: 'wid', + }, { + name: 'pepe', + label: 'second', + type: 'type', + uuid: 'asdasdsa2', + widget: 'wid', + }, { + name: 'pepe2', + label: 'third', + type: 'type', + uuid: 'asdasdsa3', + widget: 'calendar', + }] +} as ReducerState); + +export const Empty = createExample(TestedComponent, { + ...reducerStatesExample.attributeEditing, + required_attributes: undefined +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx index 6f87a3358..2f804f940 100644 --- a/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AttributeEntryScreen.tsx @@ -1,15 +1,24 @@ /* eslint-disable @typescript-eslint/camelcase */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { ReducerStateRecovery, ReducerStateBackup } from "../../../../anastasis-core/lib"; +import { ReducerStateRecovery, ReducerStateBackup, UserAttributeSpec } from "anastasis-core/lib"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer"; import { AnastasisClientFrame, withProcessLabel, LabeledInput } from "./index"; -export function AttributeEntryScreen(props: AttributeEntryProps): VNode { - const { reducer, reducerState: backupState } = props; - const [attrs, setAttrs] = useState<Record<string, string>>( - props.reducerState.identity_attributes ?? {} - ); +export function AttributeEntryScreen(): VNode { + const reducer = useAnastasisContext() + const state = reducer?.currentReducerState + const currentIdentityAttributes = state && "identity_attributes" in state ? (state.identity_attributes || {}) : {} + const [attrs, setAttrs] = useState<Record<string, string>>(currentIdentityAttributes); + + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || !("required_attributes" in reducer.currentReducerState)) { + return <div>invalid state</div> + } + return ( <AnastasisClientFrame title={withProcessLabel(reducer, "Select Country")} @@ -17,7 +26,7 @@ export function AttributeEntryScreen(props: AttributeEntryProps): VNode { identity_attributes: attrs, })} > - {backupState.required_attributes.map((x: any, i: number) => { + {reducer.currentReducerState.required_attributes?.map((x, i: number) => { return ( <AttributeEntryField key={i} @@ -40,7 +49,7 @@ export interface AttributeEntryFieldProps { isFirst: boolean; value: string; setValue: (newValue: string) => void; - spec: any; + spec: UserAttributeSpec; } export function AttributeEntryField(props: AttributeEntryFieldProps): VNode { diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx new file mode 100644 index 000000000..44d3795b2 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.stories.tsx @@ -0,0 +1,35 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../utils'; +import { AuthenticationEditorScreen as TestedComponent } from './AuthenticationEditorScreen'; + + +export default { + title: 'Pages/AuthenticationEditorScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Example = createExample(TestedComponent, reducerStatesExample.authEditing); diff --git a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx index fc28942aa..e9ffccbac 100644 --- a/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/AuthenticationEditorScreen.tsx @@ -1,7 +1,8 @@ /* eslint-disable @typescript-eslint/camelcase */ +import { AuthMethod, ReducerStateBackup } from "anastasis-core"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { AuthMethod, ReducerStateBackup } from "anastasis-core"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer"; import { AuthMethodEmailSetup } from "./AuthMethodEmailSetup"; import { AuthMethodPostSetup } from "./AuthMethodPostSetup"; @@ -9,12 +10,18 @@ import { AuthMethodQuestionSetup } from "./AuthMethodQuestionSetup"; import { AuthMethodSmsSetup } from "./AuthMethodSmsSetup"; import { AnastasisClientFrame } from "./index"; -export function AuthenticationEditorScreen(props: AuthenticationEditorProps): VNode { +export function AuthenticationEditorScreen(): VNode { const [selectedMethod, setSelectedMethod] = useState<string | undefined>( undefined ); - const { reducer, backupState } = props; - const providers = backupState.authentication_providers!; + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { + return <div>invalid state</div> + } + const providers = reducer.currentReducerState.authentication_providers!; const authAvailableSet = new Set<string>(); for (const provKey of Object.keys(providers)) { const p = providers[provKey]; @@ -52,14 +59,14 @@ export function AuthenticationEditorScreen(props: AuthenticationEditorProps): VN disabled={!authAvailableSet.has(props.method)} onClick={() => { setSelectedMethod(props.method); - reducer.dismissError(); + if (reducer) reducer.dismissError(); }} > {props.label} </button> ); } - const configuredAuthMethods: AuthMethod[] = backupState.authentication_methods ?? []; + const configuredAuthMethods: AuthMethod[] = reducer.currentReducerState.authentication_methods ?? []; const haveMethodsConfigured = configuredAuthMethods.length; return ( <AnastasisClientFrame title="Backup: Configure Authentication Methods"> diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx new file mode 100644 index 000000000..65a2b7e13 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.stories.tsx @@ -0,0 +1,60 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { BackupFinishedScreen as TestedComponent } from './BackupFinishedScreen'; + + +export default { + title: 'Pages/BackupFinishedScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Simple = createExample(TestedComponent, reducerStatesExample.backupFinished); + +export const WithName = createExample(TestedComponent, {...reducerStatesExample.backupFinished, + secret_name: 'super_secret', +} as ReducerState); + +export const WithDetails = createExample(TestedComponent, { + ...reducerStatesExample.backupFinished, + secret_name: 'super_secret', + success_details: { + 'http://anastasis.net': { + policy_expiration: { + t_ms: 'never' + }, + policy_version: 0 + }, + 'http://taler.net': { + policy_expiration: { + t_ms: new Date().getTime() + 60*60*24*1000 + }, + policy_version: 1 + }, + } +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx index 6c2770947..218f1d1fd 100644 --- a/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/BackupFinishedScreen.tsx @@ -1,23 +1,33 @@ import { h, VNode } from "preact"; -import { BackupReducerProps, AnastasisClientFrame } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; -export function BackupFinishedScreen(props: BackupReducerProps): VNode { +export function BackupFinishedScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { + return <div>invalid state</div> + } + const details = reducer.currentReducerState.success_details return (<AnastasisClientFrame hideNext title="Backup finished"> <p> - Your backup of secret "{props.backupState.secret_name ?? "??"}" was + Your backup of secret "{reducer.currentReducerState.secret_name ?? "??"}" was successful. </p> <p>The backup is stored by the following providers:</p> - <ul> - {Object.keys(props.backupState.success_details!).map((x, i) => { - const sd = props.backupState.success_details![x]; + + {details && <ul> + {Object.keys(details).map((x, i) => { + const sd = details[x]; return ( <li key={i}> {x} (Policy version {sd.policy_version}) </li> ); })} - </ul> - <button onClick={() => props.reducer.reset()}>Back to start</button> + </ul>} + <button onClick={() => reducer.reset()}>Back to start</button> </AnastasisClientFrame>); } diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx new file mode 100644 index 000000000..4f186c031 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.stories.tsx @@ -0,0 +1,83 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { ChallengeOverviewScreen as TestedComponent } from './ChallengeOverviewScreen'; + + +export default { + title: 'Pages/ChallengeOverviewScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const OneChallenge = createExample(TestedComponent, {...reducerStatesExample.challengeSelecting, + recovery_information: { + policies: [[{uuid:'1'}]], + challenges: [{ + cost: 'USD:1', + instructions: 'just go for it', + type: 'question', + uuid: '1', + }] + }, +} as ReducerState); + +export const MoreChallenges = createExample(TestedComponent, {...reducerStatesExample.challengeSelecting, + recovery_information: { + policies: [[{uuid:'1'}, {uuid:'2'}],[{uuid:'3'}]], + challenges: [{ + cost: 'USD:1', + instructions: 'just go for it', + type: 'question', + uuid: '1', + },{ + cost: 'USD:1', + instructions: 'just go for it', + type: 'question', + uuid: '2', + },{ + cost: 'USD:1', + instructions: 'just go for it', + type: 'question', + uuid: '3', + }] + }, +} as ReducerState); + +export const OneBadConfiguredPolicy = createExample(TestedComponent, {...reducerStatesExample.challengeSelecting, + recovery_information: { + policies: [[{uuid:'2'}]], + challenges: [{ + cost: 'USD:1', + instructions: 'just go for it', + type: 'sasd', + uuid: '1', + }] + }, +} as ReducerState); + +export const NoPolicies = createExample(TestedComponent, reducerStatesExample.challengeSelecting); diff --git a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx index 1f108ce6d..c9b52e91b 100644 --- a/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ChallengeOverviewScreen.tsx @@ -1,10 +1,21 @@ import { h, VNode } from "preact"; -import { RecoveryReducerProps, AnastasisClientFrame } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; + +export function ChallengeOverviewScreen(): VNode { + const reducer = useAnastasisContext() + + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { + return <div>invalid state</div> + } + + const policies = reducer.currentReducerState.recovery_information?.policies ?? []; + const chArr = reducer.currentReducerState.recovery_information?.challenges ?? []; + const challengeFeedback = reducer.currentReducerState?.challenge_feedback; -export function ChallengeOverviewScreen(props: RecoveryReducerProps): VNode { - const { recoveryState, reducer } = props; - const policies = recoveryState.recovery_information!.policies; - const chArr = recoveryState.recovery_information!.challenges; const challenges: { [uuid: string]: { type: string; @@ -22,15 +33,21 @@ export function ChallengeOverviewScreen(props: RecoveryReducerProps): VNode { return ( <AnastasisClientFrame title="Recovery: Solve challenges"> <h2>Policies</h2> - {policies.map((x, i) => { + {!policies.length && <p> + No policies found + </p>} + {policies.map((row, i) => { return ( <div key={i}> <h3>Policy #{i + 1}</h3> - {x.map((x, j) => { - const ch = challenges[x.uuid]; - const feedback = recoveryState.challenge_feedback?.[x.uuid]; + {row.map(column => { + const ch = challenges[column.uuid]; + if (!ch) return <div> + There is no challenge for this policy + </div> + const feedback = challengeFeedback?.[column.uuid]; return ( - <div key={j} + <div key={column.uuid} style={{ borderLeft: "2px solid gray", paddingLeft: "0.5em", @@ -46,7 +63,7 @@ export function ChallengeOverviewScreen(props: RecoveryReducerProps): VNode { {feedback?.state !== "solved" ? ( <button onClick={() => reducer.transition("select_challenge", { - uuid: x.uuid, + uuid: column.uuid, })} > Solve diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx new file mode 100644 index 000000000..aad37cd7f --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.stories.tsx @@ -0,0 +1,36 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../utils'; +import { ContinentSelectionScreen as TestedComponent } from './ContinentSelectionScreen'; + + +export default { + title: 'Pages/ContinentSelectionScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Backup = createExample(TestedComponent, reducerStatesExample.backupSelectCountry); +export const Recovery = createExample(TestedComponent, reducerStatesExample.recoverySelectCountry); diff --git a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx index 2fed23d4e..ad529a4a7 100644 --- a/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ContinentSelectionScreen.tsx @@ -1,15 +1,16 @@ import { h, VNode } from "preact"; -import { CommonReducerProps, AnastasisClientFrame, withProcessLabel } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame, withProcessLabel } from "./index"; -export function ContinentSelectionScreen(props: CommonReducerProps): VNode { - const { reducer, reducerState } = props; +export function ContinentSelectionScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer || !reducer.currentReducerState || !("continents" in reducer.currentReducerState)) { + return <div /> + } const sel = (x: string): void => reducer.transition("select_continent", { continent: x }); return ( - <AnastasisClientFrame - hideNext - title={withProcessLabel(reducer, "Select Continent")} - > - {reducerState.continents.map((x: any) => ( + <AnastasisClientFrame hideNext title={withProcessLabel(reducer, "Select Continent")}> + {reducer.currentReducerState.continents.map((x: any) => ( <button onClick={() => sel(x.name)} key={x.name}> {x.name} </button> diff --git a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx new file mode 100644 index 000000000..adf36980f --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.stories.tsx @@ -0,0 +1,36 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../utils'; +import { CountrySelectionScreen as TestedComponent } from './CountrySelectionScreen'; + + +export default { + title: 'Pages/CountrySelectionScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Backup = createExample(TestedComponent, reducerStatesExample.backupSelectCountry); +export const Recovery = createExample(TestedComponent, reducerStatesExample.recoverySelectCountry); diff --git a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx index dbe4b7616..555622c1d 100644 --- a/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/CountrySelectionScreen.tsx @@ -1,19 +1,23 @@ /* eslint-disable @typescript-eslint/camelcase */ import { h, VNode } from "preact"; -import { CommonReducerProps, AnastasisClientFrame, withProcessLabel } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame, withProcessLabel } from "./index"; -export function CountrySelectionScreen(props: CommonReducerProps): VNode { - const { reducer, reducerState } = props; +export function CountrySelectionScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || !("countries" in reducer.currentReducerState)) { + return <div>invalid state</div> + } const sel = (x: any): void => reducer.transition("select_country", { country_code: x.code, currencies: [x.currency], }); return ( - <AnastasisClientFrame - hideNext - title={withProcessLabel(reducer, "Select Country")} - > - {reducerState.countries.map((x: any) => ( + <AnastasisClientFrame hideNext title={withProcessLabel(reducer, "Select Country")} > + {reducer.currentReducerState.countries.map((x: any) => ( <button onClick={() => sel(x)} key={x.name}> {x.name} ({x.currency}) </button> diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx new file mode 100644 index 000000000..1a9462b88 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.stories.tsx @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { PoliciesPayingScreen as TestedComponent } from './PoliciesPayingScreen'; + + +export default { + title: 'Pages/PoliciesPayingScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Example = createExample(TestedComponent, reducerStatesExample.policyPay); +export const WithSomePaymentRequest = createExample(TestedComponent, { + ...reducerStatesExample.policyPay, + policy_payment_requests: [{ + payto: 'payto://x-taler-bank/bank.taler/account-a', + provider: 'provider1' + }, { + payto: 'payto://x-taler-bank/bank.taler/account-b', + provider: 'provider2' + }] +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx index be74729eb..8a39cf0e4 100644 --- a/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/PoliciesPayingScreen.tsx @@ -1,9 +1,17 @@ import { h, VNode } from "preact"; -import { BackupReducerProps, AnastasisClientFrame } from "./index"; - -export function PoliciesPayingScreen(props: BackupReducerProps): VNode { - const payments = props.backupState.policy_payment_requests ?? []; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; +export function PoliciesPayingScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { + return <div>invalid state</div> + } + const payments = reducer.currentReducerState.policy_payment_requests ?? []; + return ( <AnastasisClientFrame hideNext title="Backup: Recovery Document Payments"> <p> @@ -19,7 +27,7 @@ export function PoliciesPayingScreen(props: BackupReducerProps): VNode { ); })} </ul> - <button onClick={() => props.reducer.transition("pay", {})}> + <button onClick={() => reducer.transition("pay", {})}> Check payment status now </button> </AnastasisClientFrame> diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx new file mode 100644 index 000000000..0c1842420 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.stories.tsx @@ -0,0 +1,42 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { RecoveryFinishedScreen as TestedComponent } from './RecoveryFinishedScreen'; + + +export default { + title: 'Pages/RecoveryFinishedScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const NormalEnding = createExample(TestedComponent, { + ...reducerStatesExample.recoveryFinished, + core_secret: { mime: 'text/plain', value: 'hello' } +} as ReducerState); + +export const BadEnding = createExample(TestedComponent, reducerStatesExample.recoveryFinished); diff --git a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx index 7ccc511ff..8c8a2c7c8 100644 --- a/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/RecoveryFinishedScreen.tsx @@ -3,13 +3,31 @@ import { decodeCrock } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; -import { RecoveryReducerProps, AnastasisClientFrame } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; -export function RecoveryFinishedScreen(props: RecoveryReducerProps): VNode { +export function RecoveryFinishedScreen(): VNode { + const reducer = useAnastasisContext() + + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { + return <div>invalid state</div> + } + const encodedSecret = reducer.currentReducerState.core_secret?.value + if (!encodedSecret) { + return <AnastasisClientFrame title="Recovery Problem" hideNext> + <p> + Secret not found + </p> + </AnastasisClientFrame> + } + const secret = bytesToString(decodeCrock(encodedSecret)) return ( <AnastasisClientFrame title="Recovery Finished" hideNext> <p> - Secret: {bytesToString(decodeCrock(props.recoveryState.core_secret?.value!))} + Secret: {secret} </p> </AnastasisClientFrame> ); diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx new file mode 100644 index 000000000..b52699e7b --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.stories.tsx @@ -0,0 +1,81 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { ReviewPoliciesScreen as TestedComponent } from './ReviewPoliciesScreen'; + + +export default { + title: 'Pages/ReviewPoliciesScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const HasPoliciesButMethodListIsEmpty = createExample(TestedComponent, { + ...reducerStatesExample.policyReview, + policies: [{ + methods: [{ + authentication_method: 0, + provider: 'asd' + },{ + authentication_method: 1, + provider: 'asd' + }] + },{ + methods: [{ + authentication_method: 1, + provider: 'asd' + }] + }], + authentication_methods: [] +} as ReducerState); + +export const SomePoliciesWithMethods = createExample(TestedComponent, { + ...reducerStatesExample.policyReview, + policies: [{ + methods: [{ + authentication_method: 0, + provider: 'asd' + },{ + authentication_method: 1, + provider: 'asd' + }] + },{ + methods: [{ + authentication_method: 1, + provider: 'asd' + }] + }], + authentication_methods: [{ + challenge: 'asd', + instructions: 'ins', + type: 'type', + },{ + challenge: 'asd2', + instructions: 'ins2', + type: 'type2', + }] +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx index 3e20538d2..b360ccaf0 100644 --- a/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/ReviewPoliciesScreen.tsx @@ -1,35 +1,49 @@ /* eslint-disable @typescript-eslint/camelcase */ import { h, VNode } from "preact"; -import { BackupReducerProps, AnastasisClientFrame } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; + +export function ReviewPoliciesScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { + return <div>invalid state</div> + } + const authMethods = reducer.currentReducerState.authentication_methods ?? []; + const policies = reducer.currentReducerState.policies ?? []; -export function ReviewPoliciesScreen(props: BackupReducerProps): VNode { - const { reducer, backupState } = props; - const authMethods = backupState.authentication_methods!; return ( <AnastasisClientFrame title="Backup: Review Recovery Policies"> - {backupState.policies?.map((p, i) => { - const policyName = p.methods - .map((x, i) => authMethods[x.authentication_method].type) - .join(" + "); + {policies.map((p, policy_index) => { + const methods = p.methods + .map(x => authMethods[x.authentication_method] && ({ ...authMethods[x.authentication_method], provider: x.provider })) + .filter(x => !!x) + + const policyName = methods.map(x => x.type).join(" + "); + return ( - <div key={i} class="policy"> + <div key={policy_index} class="policy"> <h3> - Policy #{i + 1}: {policyName} + Policy #{policy_index + 1}: {policyName} </h3> Required Authentications: + {!methods.length && <p> + No auth method found + </p>} <ul> - {p.methods.map((x, i) => { - const m = authMethods[x.authentication_method]; + {methods.map((m, i) => { return ( <li key={i}> - {m.type} ({m.instructions}) at provider {x.provider} + {m.type} ({m.instructions}) at provider {m.provider} </li> ); })} </ul> <div> <button - onClick={() => reducer.transition("delete_policy", { policy_index: i })} + onClick={() => reducer.transition("delete_policy", { policy_index })} > Delete Policy </button> diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx new file mode 100644 index 000000000..18560356a --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.stories.tsx @@ -0,0 +1,44 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { SecretEditorScreen as TestedComponent } from './SecretEditorScreen'; + + +export default { + title: 'Pages/SecretEditorScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const WithSecretNamePreselected = createExample(TestedComponent, { + ...reducerStatesExample.secretEdition, + secret_name: 'someSecretName', +} as ReducerState); + +export const WithoutName = createExample(TestedComponent, { + ...reducerStatesExample.secretEdition, +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx index 086d4921d..a5235d66c 100644 --- a/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretEditorScreen.tsx @@ -2,18 +2,29 @@ import { encodeCrock, stringToBytes } from "@gnu-taler/taler-util"; import { h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { useAnastasisContext } from "../../context/anastasis"; import { - BackupReducerProps, AnastasisClientFrame, - LabeledInput, + LabeledInput } from "./index"; -export function SecretEditorScreen(props: BackupReducerProps): VNode { - const { reducer } = props; - const [secretName, setSecretName] = useState( - props.backupState.secret_name ?? "", - ); +export function SecretEditorScreen(): VNode { + const reducer = useAnastasisContext() const [secretValue, setSecretValue] = useState(""); + + const currentSecretName = reducer?.currentReducerState + && ("secret_name" in reducer.currentReducerState) + && reducer.currentReducerState.secret_name; + + const [secretName, setSecretName] = useState(currentSecretName || ""); + + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { + return <div>invalid state</div> + } + const secretNext = (): void => { reducer.runTransaction(async (tx) => { await tx.transition("enter_secret_name", { diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx new file mode 100644 index 000000000..e9c597023 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.stories.tsx @@ -0,0 +1,50 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { SecretSelectionScreen as TestedComponent } from './SecretSelectionScreen'; + + +export default { + title: 'Pages/SecretSelectionScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Example = createExample(TestedComponent, { + ...reducerStatesExample.secretSelection, + recovery_document: { + provider_url: 'http://anastasis.url/', + secret_name: 'secretName', + version: 1, + }, +} as ReducerState); + + +export const NoRecoveryDocumentFound = createExample(TestedComponent, { + ...reducerStatesExample.secretSelection, + recovery_document: undefined, +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx index 7cb7fdf20..903f57868 100644 --- a/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SecretSelectionScreen.tsx @@ -1,17 +1,29 @@ /* eslint-disable @typescript-eslint/camelcase */ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; -import { RecoveryReducerProps, AnastasisClientFrame } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; -export function SecretSelectionScreen(props: RecoveryReducerProps): VNode { - const { reducer, recoveryState } = props; +export function SecretSelectionScreen(): VNode { const [selectingVersion, setSelectingVersion] = useState<boolean>(false); - const [otherVersion, setOtherVersion] = useState<number>( - recoveryState.recovery_document?.version ?? 0 - ); - const recoveryDocument = recoveryState.recovery_document!; const [otherProvider, setOtherProvider] = useState<string>(""); + const reducer = useAnastasisContext() + + const currentVersion = reducer?.currentReducerState + && ("recovery_document" in reducer.currentReducerState) + && reducer.currentReducerState.recovery_document?.version; + + const [otherVersion, setOtherVersion] = useState<number>(currentVersion || 0); + + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { + return <div>invalid state</div> + } + function selectVersion(p: string, n: number): void { + if (!reducer) return; reducer.runTransaction(async (tx) => { await tx.transition("change_version", { version: n, @@ -20,12 +32,21 @@ export function SecretSelectionScreen(props: RecoveryReducerProps): VNode { setSelectingVersion(false); }); } + + const recoveryDocument = reducer.currentReducerState.recovery_document + if (!recoveryDocument) { + return ( + <AnastasisClientFrame hideNav title="Recovery: Problem"> + <p>No recovery document found</p> + </AnastasisClientFrame> + ) + } if (selectingVersion) { return ( <AnastasisClientFrame hideNav title="Recovery: Select secret"> <p>Select a different version of the secret</p> <select onChange={(e) => setOtherProvider((e.target as any).value)}> - {Object.keys(recoveryState.authentication_providers ?? {}).map( + {Object.keys(reducer.currentReducerState.authentication_providers ?? {}).map( (x, i) => ( <option key={i} selected={x === recoveryDocument.provider_url} value={x}> {x} diff --git a/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx index 6296dc022..2c27895c2 100644 --- a/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveEmailEntry.tsx @@ -1,14 +1,17 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, LabeledInput } from "./index"; import { SolveEntryProps } from "./SolveScreen"; -export function SolveEmailEntry(props: SolveEntryProps): VNode { +export function SolveEmailEntry({ challenge, feedback }: SolveEntryProps): VNode { const [answer, setAnswer] = useState(""); - const { reducer, challenge, feedback } = props; - const next = (): void => reducer.transition("solve_challenge", { - answer, - }); + const reducer = useAnastasisContext() + const next = (): void => { + if (reducer) reducer.transition("solve_challenge", { + answer, + }) + }; return ( <AnastasisClientFrame title="Recovery: Solve challenge" diff --git a/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx b/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx index b11ceed27..1a824acb8 100644 --- a/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx +++ b/packages/anastasis-webui/src/pages/home/SolvePostEntry.tsx @@ -1,14 +1,15 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, LabeledInput } from "./index"; import { SolveEntryProps } from "./SolveScreen"; -export function SolvePostEntry(props: SolveEntryProps): VNode { +export function SolvePostEntry({ challenge, feedback }: SolveEntryProps): VNode { const [answer, setAnswer] = useState(""); - const { reducer, challenge, feedback } = props; - const next = (): void => reducer.transition("solve_challenge", { - answer, - }); + const reducer = useAnastasisContext() + const next = (): void => { + if (reducer) reducer.transition("solve_challenge", { answer }) + }; return ( <AnastasisClientFrame title="Recovery: Solve challenge" diff --git a/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx index 6393958b3..72dadbe89 100644 --- a/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveQuestionEntry.tsx @@ -1,14 +1,15 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, LabeledInput } from "./index"; import { SolveEntryProps } from "./SolveScreen"; -export function SolveQuestionEntry(props: SolveEntryProps): VNode { +export function SolveQuestionEntry({ challenge, feedback }: SolveEntryProps): VNode { const [answer, setAnswer] = useState(""); - const { reducer, challenge, feedback } = props; - const next = (): void => reducer.transition("solve_challenge", { - answer, - }); + const reducer = useAnastasisContext() + const next = (): void => { + if (reducer) reducer.transition("solve_challenge", { answer }) + }; return ( <AnastasisClientFrame title="Recovery: Solve challenge" diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx new file mode 100644 index 000000000..69af9be42 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.stories.tsx @@ -0,0 +1,121 @@ +/* eslint-disable @typescript-eslint/camelcase */ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { SolveScreen as TestedComponent } from './SolveScreen'; + + +export default { + title: 'Pages/SolveScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const NoInformation = createExample(TestedComponent, reducerStatesExample.challengeSolving); + +export const NotSupportedChallenge = createExample(TestedComponent, { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [{ + cost: 'USD:1', + instructions: 'follow htis instructions', + type: 'chall-type', + uuid: 'ASDASDSAD!1' + }], + policies: [], + }, + selected_challenge_uuid: 'ASDASDSAD!1' +} as ReducerState); + +export const MismatchedChallengeId = createExample(TestedComponent, { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [{ + cost: 'USD:1', + instructions: 'follow htis instructions', + type: 'chall-type', + uuid: 'ASDASDSAD!1' + }], + policies: [], + }, + selected_challenge_uuid: 'no-no-no' +} as ReducerState); + +export const SmsChallenge = createExample(TestedComponent, { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [{ + cost: 'USD:1', + instructions: 'follow htis instructions', + type: 'sms', + uuid: 'ASDASDSAD!1' + }], + policies: [], + }, + selected_challenge_uuid: 'ASDASDSAD!1' +} as ReducerState); + +export const QuestionChallenge = createExample(TestedComponent, { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [{ + cost: 'USD:1', + instructions: 'follow htis instructions', + type: 'question', + uuid: 'ASDASDSAD!1' + }], + policies: [], + }, + selected_challenge_uuid: 'ASDASDSAD!1' +} as ReducerState); + +export const EmailChallenge = createExample(TestedComponent, { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [{ + cost: 'USD:1', + instructions: 'follow htis instructions', + type: 'email', + uuid: 'ASDASDSAD!1' + }], + policies: [], + }, + selected_challenge_uuid: 'ASDASDSAD!1' +} as ReducerState); + +export const PostChallenge = createExample(TestedComponent, { + ...reducerStatesExample.challengeSolving, + recovery_information: { + challenges: [{ + cost: 'USD:1', + instructions: 'follow htis instructions', + type: 'post', + uuid: 'ASDASDSAD!1' + }], + policies: [], + }, + selected_challenge_uuid: 'ASDASDSAD!1' +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx index 357a7c2d3..05ae50b48 100644 --- a/packages/anastasis-webui/src/pages/home/SolveScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveScreen.tsx @@ -1,17 +1,31 @@ import { h, VNode } from "preact"; -import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer"; +import { ChallengeFeedback, ChallengeInfo } from "../../../../anastasis-core/lib"; +import { useAnastasisContext } from "../../context/anastasis"; import { SolveEmailEntry } from "./SolveEmailEntry"; import { SolvePostEntry } from "./SolvePostEntry"; import { SolveQuestionEntry } from "./SolveQuestionEntry"; import { SolveSmsEntry } from "./SolveSmsEntry"; import { SolveUnsupportedEntry } from "./SolveUnsupportedEntry"; -import { RecoveryReducerProps } from "./index"; -import { ChallengeInfo, ChallengeFeedback } from "../../../../anastasis-core/lib"; -export function SolveScreen(props: RecoveryReducerProps): VNode { - const chArr = props.recoveryState.recovery_information!.challenges; - const challengeFeedback = props.recoveryState.challenge_feedback ?? {}; - const selectedUuid = props.recoveryState.selected_challenge_uuid!; +export function SolveScreen(): VNode { + const reducer = useAnastasisContext() + + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.recovery_state === undefined) { + return <div>invalid state</div> + } + + if (!reducer.currentReducerState.recovery_information) { + return <div>no recovery information found</div> + } + if (!reducer.currentReducerState.selected_challenge_uuid) { + return <div>no selected uuid</div> + } + const chArr = reducer.currentReducerState.recovery_information.challenges; + const challengeFeedback = reducer.currentReducerState.challenge_feedback ?? {}; + const selectedUuid = reducer.currentReducerState.selected_challenge_uuid; const challenges: { [uuid: string]: ChallengeInfo; } = {}; @@ -25,17 +39,15 @@ export function SolveScreen(props: RecoveryReducerProps): VNode { email: SolveEmailEntry, post: SolvePostEntry, }; - const SolveDialog = dialogMap[selectedChallenge.type] ?? SolveUnsupportedEntry; + const SolveDialog = dialogMap[selectedChallenge?.type] ?? SolveUnsupportedEntry; return ( <SolveDialog challenge={selectedChallenge} - reducer={props.reducer} feedback={challengeFeedback[selectedUuid]} /> ); } export interface SolveEntryProps { - reducer: AnastasisReducerApi; challenge: ChallengeInfo; feedback?: ChallengeFeedback; } diff --git a/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx b/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx index d0cd41332..163e0d1f3 100644 --- a/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx +++ b/packages/anastasis-webui/src/pages/home/SolveSmsEntry.tsx @@ -1,14 +1,17 @@ import { h, VNode } from "preact"; import { useState } from "preact/hooks"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame, LabeledInput } from "./index"; import { SolveEntryProps } from "./SolveScreen"; -export function SolveSmsEntry(props: SolveEntryProps): VNode { +export function SolveSmsEntry({ challenge, feedback }: SolveEntryProps): VNode { const [answer, setAnswer] = useState(""); - const { reducer, challenge, feedback } = props; - const next = (): void => reducer.transition("solve_challenge", { - answer, - }); + const reducer = useAnastasisContext() + const next = (): void => { + if (reducer) reducer.transition("solve_challenge", { + answer, + }) + }; return ( <AnastasisClientFrame title="Recovery: Solve challenge" diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx new file mode 100644 index 000000000..ad84cd8f2 --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/StartScreen.stories.tsx @@ -0,0 +1,35 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { createExample, reducerStatesExample } from '../../utils'; +import { StartScreen as TestedComponent } from './StartScreen'; + + +export default { + title: 'Pages/StartScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const InitialState = createExample(TestedComponent, reducerStatesExample.initial);
\ No newline at end of file diff --git a/packages/anastasis-webui/src/pages/home/StartScreen.tsx b/packages/anastasis-webui/src/pages/home/StartScreen.tsx index 38124887c..6625ec5b8 100644 --- a/packages/anastasis-webui/src/pages/home/StartScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/StartScreen.tsx @@ -1,14 +1,34 @@ + import { h, VNode } from "preact"; -import { AnastasisReducerApi } from "../../hooks/use-anastasis-reducer"; +import { useAnastasisContext } from "../../context/anastasis"; import { AnastasisClientFrame } from "./index"; -export function StartScreen(props: { reducer: AnastasisReducerApi; }): VNode { +export function StartScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } return ( <AnastasisClientFrame hideNav title="Home"> - <button autoFocus onClick={() => props.reducer.startBackup()}> - Backup - </button> - <button onClick={() => props.reducer.startRecover()}>Recover</button> + <div> + <section class="section is-main-section"> + <div class="columns"> + <div class="column" /> + <div class="column is-four-fifths"> + + <div class="buttons is-right"> + <button class="button is-success" autoFocus onClick={() => reducer.startBackup()}> + Backup + </button> + + <button class="button is-info" onClick={() => reducer.startRecover()}>Recover</button> + </div> + + </div> + <div class="column" /> + </div> + </section> + </div> </AnastasisClientFrame> ); } diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx new file mode 100644 index 000000000..e2f3d521e --- /dev/null +++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.stories.tsx @@ -0,0 +1,40 @@ +/* + This file is part of GNU Taler + (C) 2021 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ReducerState } from 'anastasis-core'; +import { createExample, reducerStatesExample } from '../../utils'; +import { TruthsPayingScreen as TestedComponent } from './TruthsPayingScreen'; + + +export default { + title: 'Pages/TruthsPayingScreen', + component: TestedComponent, + argTypes: { + onUpdate: { action: 'onUpdate' }, + onBack: { action: 'onBack' }, + }, +}; + +export const Example = createExample(TestedComponent, reducerStatesExample.truthsPaying); +export const WithPaytoList = createExample(TestedComponent, { + ...reducerStatesExample.truthsPaying, + payments: ['payto://x-taler-bank/bank/account'] +} as ReducerState); diff --git a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx index 5b8a835b8..319f590a0 100644 --- a/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx +++ b/packages/anastasis-webui/src/pages/home/TruthsPayingScreen.tsx @@ -1,8 +1,16 @@ import { h, VNode } from "preact"; -import { BackupReducerProps, AnastasisClientFrame } from "./index"; +import { useAnastasisContext } from "../../context/anastasis"; +import { AnastasisClientFrame } from "./index"; -export function TruthsPayingScreen(props: BackupReducerProps): VNode { - const payments = props.backupState.payments ?? []; +export function TruthsPayingScreen(): VNode { + const reducer = useAnastasisContext() + if (!reducer) { + return <div>no reducer in context</div> + } + if (!reducer.currentReducerState || reducer.currentReducerState.backup_state === undefined) { + return <div>invalid state</div> + } + const payments = reducer.currentReducerState.payments ?? []; return ( <AnastasisClientFrame hideNext @@ -17,7 +25,7 @@ export function TruthsPayingScreen(props: BackupReducerProps): VNode { return <li key={i}>{x}</li>; })} </ul> - <button onClick={() => props.reducer.transition("pay", {})}> + <button onClick={() => reducer.transition("pay", {})}> Check payment status now </button> </AnastasisClientFrame> diff --git a/packages/anastasis-webui/src/pages/home/index.tsx b/packages/anastasis-webui/src/pages/home/index.tsx index 5001d1ee4..4cec47ec8 100644 --- a/packages/anastasis-webui/src/pages/home/index.tsx +++ b/packages/anastasis-webui/src/pages/home/index.tsx @@ -1,28 +1,25 @@ import { - Component, - ComponentChildren, - createContext, - Fragment, + BackupStates, + RecoveryStates, + ReducerStateBackup, + ReducerStateRecovery +} from "anastasis-core"; +import { + ComponentChildren, Fragment, FunctionalComponent, h, - VNode, + VNode } from "preact"; import { - useContext, useErrorBoundary, useLayoutEffect, - useRef, + useRef } from "preact/hooks"; import { Menu } from "../../components/menu"; -import { - BackupStates, - RecoveryStates, - ReducerStateBackup, - ReducerStateRecovery, -} from "anastasis-core"; +import { AnastasisProvider, useAnastasisContext } from "../../context/anastasis"; import { AnastasisReducerApi, - useAnastasisReducer, + useAnastasisReducer } from "../../hooks/use-anastasis-reducer"; import { AttributeEntryScreen } from "./AttributeEntryScreen"; import { AuthenticationEditorScreen } from "./AuthenticationEditorScreen"; @@ -38,19 +35,11 @@ import { SecretSelectionScreen } from "./SecretSelectionScreen"; import { SolveScreen } from "./SolveScreen"; import { StartScreen } from "./StartScreen"; import { TruthsPayingScreen } from "./TruthsPayingScreen"; -import "./../home/style"; - -const WithReducer = createContext<AnastasisReducerApi | undefined>(undefined); function isBackup(reducer: AnastasisReducerApi): boolean { return !!reducer.currentReducerState?.backup_state; } -export interface CommonReducerProps { - reducer: AnastasisReducerApi; - reducerState: ReducerStateBackup | ReducerStateRecovery; -} - export function withProcessLabel( reducer: AnastasisReducerApi, text: string, @@ -61,16 +50,6 @@ export function withProcessLabel( return `Recovery: ${text}`; } -export interface BackupReducerProps { - reducer: AnastasisReducerApi; - backupState: ReducerStateBackup; -} - -export interface RecoveryReducerProps { - reducer: AnastasisReducerApi; - recoveryState: ReducerStateRecovery; -} - interface AnastasisClientFrameProps { onNext?(): void; title: string; @@ -88,7 +67,7 @@ interface AnastasisClientFrameProps { function ErrorBoundary(props: { reducer: AnastasisReducerApi; children: ComponentChildren; -}) { +}): VNode { const [error, resetError] = useErrorBoundary((error) => console.log("got error", error), ); @@ -113,7 +92,7 @@ function ErrorBoundary(props: { } export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode { - const reducer = useContext(WithReducer); + const reducer = useAnastasisContext(); if (!reducer) { return <p>Fatal: Reducer must be in context.</p>; } @@ -135,9 +114,8 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode { <Menu title="Anastasis" /> <div> <div class="home" onKeyPress={(e) => handleKeyPress(e)}> - <button onClick={() => reducer.reset()}>Reset session</button> <h1>{props.title}</h1> - <ErrorBanner reducer={reducer} /> + <ErrorBanner /> {props.children} {!props.hideNav ? ( <div> @@ -154,96 +132,94 @@ export function AnastasisClientFrame(props: AnastasisClientFrameProps): VNode { const AnastasisClient: FunctionalComponent = () => { const reducer = useAnastasisReducer(); return ( - <WithReducer.Provider value={reducer}> + <AnastasisProvider value={reducer}> <ErrorBoundary reducer={reducer}> <AnastasisClientImpl /> </ErrorBoundary> - </WithReducer.Provider> + </AnastasisProvider> ); }; const AnastasisClientImpl: FunctionalComponent = () => { - const reducer = useContext(WithReducer)!; - const reducerState = reducer.currentReducerState; - if (!reducerState) { - return <StartScreen reducer={reducer} />; + const reducer = useAnastasisContext() + if (!reducer) { + return <p>Fatal: Reducer must be in context.</p>; + } + const state = reducer.currentReducerState; + if (!state) { + return <StartScreen />; } console.log("state", reducer.currentReducerState); if ( - reducerState.backup_state === BackupStates.ContinentSelecting || - reducerState.recovery_state === RecoveryStates.ContinentSelecting + state.backup_state === BackupStates.ContinentSelecting || + state.recovery_state === RecoveryStates.ContinentSelecting ) { return ( - <ContinentSelectionScreen reducer={reducer} reducerState={reducerState} /> + <ContinentSelectionScreen /> ); } if ( - reducerState.backup_state === BackupStates.CountrySelecting || - reducerState.recovery_state === RecoveryStates.CountrySelecting + state.backup_state === BackupStates.CountrySelecting || + state.recovery_state === RecoveryStates.CountrySelecting ) { return ( - <CountrySelectionScreen reducer={reducer} reducerState={reducerState} /> + <CountrySelectionScreen /> ); } if ( - reducerState.backup_state === BackupStates.UserAttributesCollecting || - reducerState.recovery_state === RecoveryStates.UserAttributesCollecting + state.backup_state === BackupStates.UserAttributesCollecting || + state.recovery_state === RecoveryStates.UserAttributesCollecting ) { return ( - <AttributeEntryScreen reducer={reducer} reducerState={reducerState} /> + <AttributeEntryScreen /> ); } - if (reducerState.backup_state === BackupStates.AuthenticationsEditing) { + if (state.backup_state === BackupStates.AuthenticationsEditing) { return ( - <AuthenticationEditorScreen - backupState={reducerState} - reducer={reducer} - /> + <AuthenticationEditorScreen /> ); } - if (reducerState.backup_state === BackupStates.PoliciesReviewing) { + if (state.backup_state === BackupStates.PoliciesReviewing) { return ( - <ReviewPoliciesScreen reducer={reducer} backupState={reducerState} /> + <ReviewPoliciesScreen /> ); } - if (reducerState.backup_state === BackupStates.SecretEditing) { - return <SecretEditorScreen reducer={reducer} backupState={reducerState} />; + if (state.backup_state === BackupStates.SecretEditing) { + return <SecretEditorScreen />; } - if (reducerState.backup_state === BackupStates.BackupFinished) { - const backupState: ReducerStateBackup = reducerState; - return <BackupFinishedScreen reducer={reducer} backupState={backupState} />; + if (state.backup_state === BackupStates.BackupFinished) { + return <BackupFinishedScreen />; } - if (reducerState.backup_state === BackupStates.TruthsPaying) { - return <TruthsPayingScreen reducer={reducer} backupState={reducerState} />; + if (state.backup_state === BackupStates.TruthsPaying) { + return <TruthsPayingScreen />; } - if (reducerState.backup_state === BackupStates.PoliciesPaying) { - const backupState: ReducerStateBackup = reducerState; - return <PoliciesPayingScreen reducer={reducer} backupState={backupState} />; + if (state.backup_state === BackupStates.PoliciesPaying) { + return <PoliciesPayingScreen />; } - if (reducerState.recovery_state === RecoveryStates.SecretSelecting) { + if (state.recovery_state === RecoveryStates.SecretSelecting) { return ( - <SecretSelectionScreen reducer={reducer} recoveryState={reducerState} /> + <SecretSelectionScreen /> ); } - if (reducerState.recovery_state === RecoveryStates.ChallengeSelecting) { + if (state.recovery_state === RecoveryStates.ChallengeSelecting) { return ( - <ChallengeOverviewScreen reducer={reducer} recoveryState={reducerState} /> + <ChallengeOverviewScreen /> ); } - if (reducerState.recovery_state === RecoveryStates.ChallengeSolving) { - return <SolveScreen reducer={reducer} recoveryState={reducerState} />; + if (state.recovery_state === RecoveryStates.ChallengeSolving) { + return <SolveScreen />; } - if (reducerState.recovery_state === RecoveryStates.RecoveryFinished) { + if (state.recovery_state === RecoveryStates.RecoveryFinished) { return ( - <RecoveryFinishedScreen reducer={reducer} recoveryState={reducerState} /> + <RecoveryFinishedScreen /> ); } @@ -251,7 +227,9 @@ const AnastasisClientImpl: FunctionalComponent = () => { return ( <AnastasisClientFrame hideNav title="Bug"> <p>Bug: Unknown state.</p> - <button onClick={() => reducer.reset()}>Reset</button> + <div class="buttons is-right"> + <button class="button" onClick={() => reducer.reset()}>Reset</button> + </div> </AnastasisClientFrame> ); }; @@ -282,26 +260,20 @@ export function LabeledInput(props: LabeledInputProps): VNode { ); } -interface ErrorBannerProps { - reducer: AnastasisReducerApi; -} - /** - * Show a dismissable error banner if there is a current error. + * Show a dismissible error banner if there is a current error. */ -function ErrorBanner(props: ErrorBannerProps): VNode | null { - const currentError = props.reducer.currentError; - if (currentError) { - return ( - <div id="error"> - <p>Error: {JSON.stringify(currentError)}</p> - <button onClick={() => props.reducer.dismissError()}> - Dismiss Error - </button> - </div> - ); - } - return null; +function ErrorBanner(): VNode | null { + const reducer = useAnastasisContext(); + if (!reducer || !reducer.currentError) return null; + return ( + <div id="error"> + <p>Error: {JSON.stringify(reducer.currentError)}</p> + <button onClick={() => reducer.dismissError()}> + Dismiss Error + </button> + </div> + ); } export default AnastasisClient; diff --git a/packages/anastasis-webui/src/utils/index.tsx b/packages/anastasis-webui/src/utils/index.tsx new file mode 100644 index 000000000..d1d861469 --- /dev/null +++ b/packages/anastasis-webui/src/utils/index.tsx @@ -0,0 +1,161 @@ +/* eslint-disable @typescript-eslint/camelcase */ +import { BackupStates, RecoveryStates, ReducerState } from 'anastasis-core'; +import { FunctionalComponent, h, VNode } from 'preact'; +import { AnastasisProvider } from '../context/anastasis'; + +export function createExample<Props>(Component: FunctionalComponent<Props>, currentReducerState?: ReducerState, props?: Partial<Props>): { (args: Props): VNode } { + const r = (args: Props): VNode => { + return <AnastasisProvider value={{ + currentReducerState, + currentError: undefined, + back: () => { null }, + dismissError: () => { null }, + reset: () => { null }, + runTransaction: () => { null }, + startBackup: () => { null }, + startRecover: () => { null }, + transition: () => { null }, + }}> + <Component {...args} /> + </AnastasisProvider> + } + r.args = props + return r +} + +const base = { + continents: [ + { + name: "Europe" + }, + { + name: "India" + }, + { + name: "Asia" + }, + { + name: "North America" + }, + { + name: "Testcontinent" + } + ], + countries: [ + { + code: "xx", + name: "Testland", + continent: "Testcontinent", + continent_i18n: { + de_DE: "Testkontinent" + }, + name_i18n: { + de_DE: "Testlandt", + de_CH: "Testlandi", + fr_FR: "Testpais", + en_UK: "Testland" + }, + currency: "TESTKUDOS", + call_code: "+00" + }, + { + code: "xy", + name: "Demoland", + continent: "Testcontinent", + continent_i18n: { + de_DE: "Testkontinent" + }, + name_i18n: { + de_DE: "Demolandt", + de_CH: "Demolandi", + fr_FR: "Demopais", + en_UK: "Demoland" + }, + currency: "KUDOS", + call_code: "+01" + } + ], + authentication_providers: { + "http://localhost:8086/": { + http_status: 200, + annual_fee: "COL:0", + business_name: "ana", + currency: "COL", + liability_limit: "COL:10", + methods: [ + { + type: "question", + usage_fee: "COL:0" + } + ], + salt: "WBMDD76BR1E90YQ5AHBMKPH7GW", + storage_limit_in_megabytes: 16, + truth_upload_fee: "COL:0" + }, + "http://localhost:8087/": { + code: 8414, + hint: "request to provider failed" + }, + "http://localhost:8088/": { + code: 8414, + hint: "request to provider failed" + }, + "http://localhost:8089/": { + code: 8414, + hint: "request to provider failed" + } + }, + // expiration: { + // d_ms: 1792525051855 // check t_ms + // }, +} as Partial<ReducerState> + +export const reducerStatesExample = { + initial: undefined, + recoverySelectCountry: {...base, + recovery_state: RecoveryStates.CountrySelecting + } as ReducerState, + backupSelectCountry: {...base, + backup_state: BackupStates.CountrySelecting + } as ReducerState, + recoverySelectContinent: {...base, + recovery_state: RecoveryStates.ContinentSelecting, + } as ReducerState, + backupSelectContinent: {...base, + backup_state: BackupStates.ContinentSelecting, + } as ReducerState, + secretSelection: {...base, + recovery_state: RecoveryStates.SecretSelecting, + } as ReducerState, + recoveryFinished: {...base, + recovery_state: RecoveryStates.RecoveryFinished, + } as ReducerState, + challengeSelecting: {...base, + recovery_state: RecoveryStates.ChallengeSelecting, + } as ReducerState, + challengeSolving: {...base, + recovery_state: RecoveryStates.ChallengeSolving, + } as ReducerState, + secretEdition: {...base, + backup_state: BackupStates.SecretEditing, + } as ReducerState, + policyReview: {...base, + backup_state: BackupStates.PoliciesReviewing, + } as ReducerState, + policyPay: {...base, + backup_state: BackupStates.PoliciesPaying, + } as ReducerState, + backupFinished: {...base, + backup_state: BackupStates.BackupFinished, + } as ReducerState, + authEditing: {...base, + backup_state: BackupStates.AuthenticationsEditing + } as ReducerState, + attributeEditing: {...base, + backup_state: BackupStates.UserAttributesCollecting + } as ReducerState, + truthsPaying: {...base, + backup_state: BackupStates.TruthsPaying + } as ReducerState, + +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5f77e575..cd6d7ae58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,12 @@ importers: specifiers: '@creativebulma/bulma-tooltip': ^1.2.0 '@gnu-taler/taler-util': workspace:^0.8.3 + '@storybook/addon-a11y': ^6.2.9 + '@storybook/addon-actions': ^6.2.9 + '@storybook/addon-essentials': ^6.2.9 + '@storybook/addon-links': ^6.2.9 + '@storybook/preact': ^6.2.9 + '@storybook/preset-scss': ^1.0.3 '@types/enzyme': ^3.10.5 '@types/jest': ^26.0.8 '@typescript-eslint/eslint-plugin': ^2.25.0 @@ -67,6 +73,12 @@ importers: preact-router: 3.2.1_preact@10.5.14 devDependencies: '@creativebulma/bulma-tooltip': 1.2.0 + '@storybook/addon-a11y': 6.3.7 + '@storybook/addon-actions': 6.3.7 + '@storybook/addon-essentials': 6.3.7_typescript@3.9.10 + '@storybook/addon-links': 6.3.12 + '@storybook/preact': 6.3.7_preact@10.5.14+typescript@3.9.10 + '@storybook/preset-scss': 1.0.3_sass-loader@10.2.0 '@types/enzyme': 3.10.9 '@types/jest': 26.0.24 '@typescript-eslint/eslint-plugin': 2.34.0_2b015b1c4b7c4a3ed9a197dc233b1a35 @@ -1724,6 +1736,15 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-syntax-jsx/7.14.5: + resolution: {integrity: sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-plugin-utils': 7.14.5 + dev: true + /@babel/plugin-syntax-jsx/7.14.5_@babel+core@7.13.16: resolution: {integrity: sha512-ohuFIsOMXJnbOMRfX7/w7LocdR6R7whhuRD4ax8IipLcLPlZGJKkBxgHp++U4N/vKyU16/YDQr2f5seajD3jIw==} engines: {node: '>=6.9.0'} @@ -2812,6 +2833,19 @@ packages: '@babel/helper-plugin-utils': 7.14.5 dev: true + /@babel/plugin-transform-react-jsx/7.14.9: + resolution: {integrity: sha512-30PeETvS+AeD1f58i1OVyoDlVYQhap/K20ZrMjLmmzmC2AYR/G43D4sdJAaDAqCD3MYpSWbmrz3kES158QSLjw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + dependencies: + '@babel/helper-annotate-as-pure': 7.14.5 + '@babel/helper-module-imports': 7.14.5 + '@babel/helper-plugin-utils': 7.14.5 + '@babel/plugin-syntax-jsx': 7.14.5 + '@babel/types': 7.15.0 + dev: true + /@babel/plugin-transform-react-jsx/7.14.9_@babel+core@7.13.16: resolution: {integrity: sha512-30PeETvS+AeD1f58i1OVyoDlVYQhap/K20ZrMjLmmzmC2AYR/G43D4sdJAaDAqCD3MYpSWbmrz3kES158QSLjw==} engines: {node: '>=6.9.0'} @@ -4800,6 +4834,101 @@ packages: - '@types/react' dev: true + /@storybook/addon-docs/6.3.7_typescript@3.9.10: + resolution: {integrity: sha512-cyuyoLuB5ELhbrXgnZneDCHqNq1wSdWZ4dzdHy1E5WwLPEhLlD6INfEsm8gnDIb4IncYuzMhK3XYBDd7d3ijOg==} + peerDependencies: + '@storybook/angular': 6.3.7 + '@storybook/vue': 6.3.7 + '@storybook/vue3': 6.3.7 + '@storybook/web-components': 6.3.7 + lit: ^2.0.0-rc.1 + lit-html: ^1.4.1 || ^2.0.0-rc.3 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + svelte: ^3.31.2 + sveltedoc-parser: ^4.1.0 + vue: ^2.6.10 || ^3.0.0 + webpack: '*' + peerDependenciesMeta: + '@storybook/angular': + optional: true + '@storybook/vue': + optional: true + '@storybook/vue3': + optional: true + '@storybook/web-components': + optional: true + lit: + optional: true + lit-html: + optional: true + react: + optional: true + react-dom: + optional: true + svelte: + optional: true + sveltedoc-parser: + optional: true + vue: + optional: true + webpack: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/generator': 7.15.0 + '@babel/parser': 7.15.3 + '@babel/plugin-transform-react-jsx': 7.14.9_@babel+core@7.15.0 + '@babel/preset-env': 7.15.0_@babel+core@7.15.0 + '@jest/transform': 26.6.2 + '@mdx-js/loader': 1.6.22 + '@mdx-js/mdx': 1.6.22 + '@mdx-js/react': 1.6.22 + '@storybook/addons': 6.3.7 + '@storybook/api': 6.3.7 + '@storybook/builder-webpack4': 6.3.7_typescript@3.9.10 + '@storybook/client-api': 6.3.7 + '@storybook/client-logger': 6.3.7 + '@storybook/components': 6.3.7 + '@storybook/core': 6.3.7_36f75bb62e0c484c1a06658ad2872463 + '@storybook/core-events': 6.3.7 + '@storybook/csf': 0.0.1 + '@storybook/csf-tools': 6.3.7_@babel+core@7.15.0 + '@storybook/node-logger': 6.3.7 + '@storybook/postinstall': 6.3.7 + '@storybook/source-loader': 6.3.7 + '@storybook/theming': 6.3.7 + acorn: 7.4.1 + acorn-jsx: 5.3.2_acorn@7.4.1 + acorn-walk: 7.2.0 + core-js: 3.16.2 + doctrine: 3.0.0 + escodegen: 2.0.0 + fast-deep-equal: 3.1.3 + global: 4.4.0 + html-tags: 3.1.0 + js-string-escape: 1.0.1 + loader-utils: 2.0.0 + lodash: 4.17.21 + p-limit: 3.1.0 + prettier: 2.2.1 + prop-types: 15.7.2 + react-element-to-jsx-string: 14.3.2 + regenerator-runtime: 0.13.9 + remark-external-links: 8.0.0 + remark-slug: 6.1.0 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@storybook/manager-webpack5' + - '@types/react' + - supports-color + - typescript + - webpack-cli + - webpack-command + dev: true + /@storybook/addon-docs/6.3.7_typescript@4.3.5: resolution: {integrity: sha512-cyuyoLuB5ELhbrXgnZneDCHqNq1wSdWZ4dzdHy1E5WwLPEhLlD6INfEsm8gnDIb4IncYuzMhK3XYBDd7d3ijOg==} peerDependencies: @@ -4955,6 +5084,89 @@ packages: - webpack-command dev: true + /@storybook/addon-essentials/6.3.7_typescript@3.9.10: + resolution: {integrity: sha512-ZWAW3qMFrrpfSekmCZibp/ivnohFLJdJweiIA0CLnuCNuuK9kQdpFahWdvyBy5NlCj3UJwB7epTZYZyHqYW7UQ==} + peerDependencies: + '@babel/core': ^7.9.6 + '@storybook/vue': 6.3.7 + '@storybook/web-components': 6.3.7 + babel-loader: ^8.0.0 + lit-html: ^1.4.1 || ^2.0.0-rc.3 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + webpack: '*' + peerDependenciesMeta: + '@storybook/vue': + optional: true + '@storybook/web-components': + optional: true + lit-html: + optional: true + react: + optional: true + react-dom: + optional: true + webpack: + optional: true + dependencies: + '@storybook/addon-actions': 6.3.7 + '@storybook/addon-backgrounds': 6.3.7 + '@storybook/addon-controls': 6.3.7 + '@storybook/addon-docs': 6.3.7_typescript@3.9.10 + '@storybook/addon-measure': 2.0.0_a4b77c99d63b159b69a1438c89904ed9 + '@storybook/addon-toolbars': 6.3.7 + '@storybook/addon-viewport': 6.3.7 + '@storybook/addons': 6.3.7 + '@storybook/api': 6.3.7 + '@storybook/node-logger': 6.3.7 + core-js: 3.16.2 + regenerator-runtime: 0.13.9 + storybook-addon-outline: 1.4.1 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@storybook/angular' + - '@storybook/builder-webpack5' + - '@storybook/components' + - '@storybook/core-events' + - '@storybook/manager-webpack5' + - '@storybook/theming' + - '@storybook/vue3' + - '@types/react' + - lit + - supports-color + - svelte + - sveltedoc-parser + - typescript + - vue + - webpack-cli + - webpack-command + dev: true + + /@storybook/addon-links/6.3.12: + resolution: {integrity: sha512-NfOGEm0+QxIrAXCa05LOXmxLtI+RlcDqHXZ1jNNj8mjeRoG1nX3qhkB8PWWIBbPuz+bktLV9ox8UZj0W6+ZPOQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + dependencies: + '@storybook/addons': 6.3.12 + '@storybook/client-logger': 6.3.12 + '@storybook/core-events': 6.3.12 + '@storybook/csf': 0.0.1 + '@storybook/router': 6.3.12 + '@types/qs': 6.9.7 + core-js: 3.16.2 + global: 4.4.0 + prop-types: 15.7.2 + qs: 6.10.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + dev: true + /@storybook/addon-measure/2.0.0_a4b77c99d63b159b69a1438c89904ed9: resolution: {integrity: sha512-ZhdT++cX+L9LwjhGYggvYUUVQH/MGn2rwbrAwCMzA/f2QTFvkjxzX8nDgMxIhaLCDC+gHIxfJG2wrWN0jkBr3g==} peerDependencies: @@ -5023,6 +5235,23 @@ packages: - '@types/react' dev: true + /@storybook/addons/6.3.12: + resolution: {integrity: sha512-UgoMyr7Qr0FS3ezt8u6hMEcHgyynQS9ucr5mAwZky3wpXRPFyUTmMto9r4BBUdqyUvTUj/LRKIcmLBfj+/l0Fg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@storybook/api': 6.3.12 + '@storybook/channels': 6.3.12 + '@storybook/client-logger': 6.3.12 + '@storybook/core-events': 6.3.12 + '@storybook/router': 6.3.12 + '@storybook/theming': 6.3.12 + core-js: 3.16.2 + global: 4.4.0 + regenerator-runtime: 0.13.9 + dev: true + /@storybook/addons/6.3.7: resolution: {integrity: sha512-9stVjTcc52bqqh7YQex/LpSjJ4e2Czm4/ZYDjIiNy0p4OZEx+yLhL5mZzMWh2NQd6vv+pHASBSxf2IeaR5511A==} peerDependencies: @@ -5059,6 +5288,34 @@ packages: regenerator-runtime: 0.13.9 dev: true + /@storybook/api/6.3.12: + resolution: {integrity: sha512-LScRXUeCWEW/OP+jiooNMQICVdusv7azTmULxtm72fhkXFRiQs2CdRNTiqNg46JLLC9z95f1W+pGK66X6HiiQA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@reach/router': 1.3.4 + '@storybook/channels': 6.3.12 + '@storybook/client-logger': 6.3.12 + '@storybook/core-events': 6.3.12 + '@storybook/csf': 0.0.1 + '@storybook/router': 6.3.12 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.3.12 + '@types/reach__router': 1.3.9 + core-js: 3.16.2 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.10.1 + regenerator-runtime: 0.13.9 + store2: 2.12.0 + telejson: 5.3.3 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + /@storybook/api/6.3.7: resolution: {integrity: sha512-57al8mxmE9agXZGo8syRQ8UhvGnDC9zkuwkBPXchESYYVkm3Mc54RTvdAOYDiy85VS4JxiGOywHayCaRwgUddQ==} peerDependencies: @@ -5117,6 +5374,96 @@ packages: util-deprecate: 1.0.2 dev: true + /@storybook/builder-webpack4/6.3.7_6b8328ae33be7bccbaedcbeca6bc1253: + resolution: {integrity: sha512-M5envblMzAUrNqP1+ouKiL8iSIW/90+kBRU2QeWlZoZl1ib+fiFoKk06cgbaC70Bx1lU8nOnI/VBvB5pLhXLaw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/plugin-proposal-class-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-decorators': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-export-default-from': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-object-rest-spread': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-proposal-optional-chaining': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-private-methods': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.15.0 + '@babel/plugin-transform-arrow-functions': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-block-scoping': 7.15.3_@babel+core@7.15.0 + '@babel/plugin-transform-classes': 7.14.9_@babel+core@7.15.0 + '@babel/plugin-transform-destructuring': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-transform-for-of': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-parameters': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-shorthand-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-spread': 7.14.6_@babel+core@7.15.0 + '@babel/plugin-transform-template-literals': 7.14.5_@babel+core@7.15.0 + '@babel/preset-env': 7.15.0_@babel+core@7.15.0 + '@babel/preset-react': 7.14.5_@babel+core@7.15.0 + '@babel/preset-typescript': 7.15.0_@babel+core@7.15.0 + '@storybook/addons': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/api': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/channel-postmessage': 6.3.7 + '@storybook/channels': 6.3.7 + '@storybook/client-api': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/client-logger': 6.3.7 + '@storybook/components': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/core-common': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/core-events': 6.3.7 + '@storybook/node-logger': 6.3.7 + '@storybook/router': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/ui': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@types/node': 14.17.10 + '@types/webpack': 4.41.30 + autoprefixer: 9.8.6 + babel-loader: 8.2.2_be352a5a80662835a7707f972edfcfde + babel-plugin-macros: 2.8.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.15.0 + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.16.2 + css-loader: 3.6.0_webpack@4.46.0 + dotenv-webpack: 1.8.0_webpack@4.46.0 + file-loader: 6.2.0_webpack@4.46.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6 + fs-extra: 9.1.0 + glob: 7.1.7 + glob-promise: 3.4.0_glob@7.1.7 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + pnp-webpack-plugin: 1.6.4_typescript@3.9.10 + postcss: 7.0.36 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0_postcss@7.0.36+webpack@4.46.0 + raw-loader: 4.0.2_webpack@4.46.0 + react: 16.14.0 + react-dev-utils: 11.0.4 + react-dom: 16.14.0_react@16.14.0 + stable: 0.1.8 + style-loader: 1.3.0_webpack@4.46.0 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + url-loader: 4.1.1_file-loader@6.2.0+webpack@4.46.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-filter-warnings-plugin: 1.2.1_webpack@4.46.0 + webpack-hot-middleware: 2.25.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/builder-webpack4/6.3.7_8073bd74a106ff14517e8eecceb690e6: resolution: {integrity: sha512-M5envblMzAUrNqP1+ouKiL8iSIW/90+kBRU2QeWlZoZl1ib+fiFoKk06cgbaC70Bx1lU8nOnI/VBvB5pLhXLaw==} peerDependencies: @@ -5207,6 +5554,94 @@ packages: - webpack-command dev: true + /@storybook/builder-webpack4/6.3.7_typescript@3.9.10: + resolution: {integrity: sha512-M5envblMzAUrNqP1+ouKiL8iSIW/90+kBRU2QeWlZoZl1ib+fiFoKk06cgbaC70Bx1lU8nOnI/VBvB5pLhXLaw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/plugin-proposal-class-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-decorators': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-export-default-from': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-object-rest-spread': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-proposal-optional-chaining': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-private-methods': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.15.0 + '@babel/plugin-transform-arrow-functions': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-block-scoping': 7.15.3_@babel+core@7.15.0 + '@babel/plugin-transform-classes': 7.14.9_@babel+core@7.15.0 + '@babel/plugin-transform-destructuring': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-transform-for-of': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-parameters': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-shorthand-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-spread': 7.14.6_@babel+core@7.15.0 + '@babel/plugin-transform-template-literals': 7.14.5_@babel+core@7.15.0 + '@babel/preset-env': 7.15.0_@babel+core@7.15.0 + '@babel/preset-react': 7.14.5_@babel+core@7.15.0 + '@babel/preset-typescript': 7.15.0_@babel+core@7.15.0 + '@storybook/addons': 6.3.7 + '@storybook/api': 6.3.7 + '@storybook/channel-postmessage': 6.3.7 + '@storybook/channels': 6.3.7 + '@storybook/client-api': 6.3.7 + '@storybook/client-logger': 6.3.7 + '@storybook/components': 6.3.7 + '@storybook/core-common': 6.3.7_typescript@3.9.10 + '@storybook/core-events': 6.3.7 + '@storybook/node-logger': 6.3.7 + '@storybook/router': 6.3.7 + '@storybook/semver': 7.3.2 + '@storybook/theming': 6.3.7 + '@storybook/ui': 6.3.7 + '@types/node': 14.17.10 + '@types/webpack': 4.41.30 + autoprefixer: 9.8.6 + babel-loader: 8.2.2_be352a5a80662835a7707f972edfcfde + babel-plugin-macros: 2.8.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.15.0 + case-sensitive-paths-webpack-plugin: 2.4.0 + core-js: 3.16.2 + css-loader: 3.6.0_webpack@4.46.0 + dotenv-webpack: 1.8.0_webpack@4.46.0 + file-loader: 6.2.0_webpack@4.46.0 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 4.1.6 + fs-extra: 9.1.0 + glob: 7.1.7 + glob-promise: 3.4.0_glob@7.1.7 + global: 4.4.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + pnp-webpack-plugin: 1.6.4_typescript@3.9.10 + postcss: 7.0.36 + postcss-flexbugs-fixes: 4.2.1 + postcss-loader: 4.3.0_postcss@7.0.36+webpack@4.46.0 + raw-loader: 4.0.2_webpack@4.46.0 + react-dev-utils: 11.0.4 + stable: 0.1.8 + style-loader: 1.3.0_webpack@4.46.0 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + url-loader: 4.1.1_file-loader@6.2.0+webpack@4.46.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-filter-warnings-plugin: 1.2.1_webpack@4.46.0 + webpack-hot-middleware: 2.25.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/builder-webpack4/6.3.7_typescript@4.3.5: resolution: {integrity: sha512-M5envblMzAUrNqP1+ouKiL8iSIW/90+kBRU2QeWlZoZl1ib+fiFoKk06cgbaC70Bx1lU8nOnI/VBvB5pLhXLaw==} peerDependencies: @@ -5307,6 +5742,14 @@ packages: telejson: 5.3.3 dev: true + /@storybook/channels/6.3.12: + resolution: {integrity: sha512-l4sA+g1PdUV8YCbgs47fIKREdEQAKNdQIZw0b7BfTvY9t0x5yfBywgQhYON/lIeiNGz2OlIuD+VUtqYfCtNSyw==} + dependencies: + core-js: 3.16.2 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + dev: true + /@storybook/channels/6.3.7: resolution: {integrity: sha512-aErXO+SRO8MPp2wOkT2n9d0fby+8yM35tq1tI633B4eQsM74EykbXPv7EamrYPqp1AI4BdiloyEpr0hmr2zlvg==} dependencies: @@ -5369,6 +5812,13 @@ packages: util-deprecate: 1.0.2 dev: true + /@storybook/client-logger/6.3.12: + resolution: {integrity: sha512-zNDsamZvHnuqLznDdP9dUeGgQ9TyFh4ray3t1VGO7ZqWVZ2xtVCCXjDvMnOXI2ifMpX5UsrOvshIPeE9fMBmiQ==} + dependencies: + core-js: 3.16.2 + global: 4.4.0 + dev: true + /@storybook/client-logger/6.3.7: resolution: {integrity: sha512-BQRErHE3nIEuUJN/3S3dO1LzxAknOgrFeZLd4UVcH/fvjtS1F4EkhcbH+jNyUWvcWGv66PZYN0oFPEn/g3Savg==} dependencies: @@ -5446,6 +5896,77 @@ packages: - '@types/react' dev: true + /@storybook/core-client/6.3.7_3c33386fd9b1d5f07f48f07869b17b73: + resolution: {integrity: sha512-M/4A65yV+Y4lsCQXX4BtQO/i3BcMPrU5FkDG8qJd3dkcx2fUlFvGWqQPkcTZE+MPVvMEGl/AsEZSADzah9+dAg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/channel-postmessage': 6.3.7 + '@storybook/client-api': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/client-logger': 6.3.7 + '@storybook/core-events': 6.3.7 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.3.7_react-dom@16.14.0+react@16.14.0 + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.16.2 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.10.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 3.9.10 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/core-client/6.3.7_6b8328ae33be7bccbaedcbeca6bc1253: + resolution: {integrity: sha512-M/4A65yV+Y4lsCQXX4BtQO/i3BcMPrU5FkDG8qJd3dkcx2fUlFvGWqQPkcTZE+MPVvMEGl/AsEZSADzah9+dAg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/channel-postmessage': 6.3.7 + '@storybook/client-api': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/client-logger': 6.3.7 + '@storybook/core-events': 6.3.7 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.3.7_react-dom@16.14.0+react@16.14.0 + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.16.2 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.10.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 3.9.10 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + /@storybook/core-client/6.3.7_70d39b49be06a92d1ddadaf2d0a6de02: resolution: {integrity: sha512-M/4A65yV+Y4lsCQXX4BtQO/i3BcMPrU5FkDG8qJd3dkcx2fUlFvGWqQPkcTZE+MPVvMEGl/AsEZSADzah9+dAg==} peerDependencies: @@ -5517,6 +6038,73 @@ packages: - '@types/react' dev: true + /@storybook/core-client/6.3.7_typescript@3.9.10: + resolution: {integrity: sha512-M/4A65yV+Y4lsCQXX4BtQO/i3BcMPrU5FkDG8qJd3dkcx2fUlFvGWqQPkcTZE+MPVvMEGl/AsEZSADzah9+dAg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.3.7 + '@storybook/channel-postmessage': 6.3.7 + '@storybook/client-api': 6.3.7 + '@storybook/client-logger': 6.3.7 + '@storybook/core-events': 6.3.7 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.3.7 + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.16.2 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.10.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 3.9.10 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + transitivePeerDependencies: + - '@types/react' + dev: true + + /@storybook/core-client/6.3.7_typescript@3.9.10+webpack@4.46.0: + resolution: {integrity: sha512-M/4A65yV+Y4lsCQXX4BtQO/i3BcMPrU5FkDG8qJd3dkcx2fUlFvGWqQPkcTZE+MPVvMEGl/AsEZSADzah9+dAg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + webpack: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@storybook/addons': 6.3.7 + '@storybook/channel-postmessage': 6.3.7 + '@storybook/client-api': 6.3.7 + '@storybook/client-logger': 6.3.7 + '@storybook/core-events': 6.3.7 + '@storybook/csf': 0.0.1 + '@storybook/ui': 6.3.7 + airbnb-js-shims: 2.2.1 + ansi-to-html: 0.6.15 + core-js: 3.16.2 + global: 4.4.0 + lodash: 4.17.21 + qs: 6.10.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + typescript: 3.9.10 + unfetch: 4.2.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - '@types/react' + dev: true + /@storybook/core-client/6.3.7_typescript@4.3.5: resolution: {integrity: sha512-M/4A65yV+Y4lsCQXX4BtQO/i3BcMPrU5FkDG8qJd3dkcx2fUlFvGWqQPkcTZE+MPVvMEGl/AsEZSADzah9+dAg==} peerDependencies: @@ -5584,6 +6172,73 @@ packages: - '@types/react' dev: true + /@storybook/core-common/6.3.7_6b8328ae33be7bccbaedcbeca6bc1253: + resolution: {integrity: sha512-exLoqRPPsAefwyjbsQBLNFrlPCcv69Q/pclqmIm7FqAPR7f3CKP1rqsHY0PnemizTL/+cLX5S7mY90gI6wpNug==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/plugin-proposal-class-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-decorators': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-export-default-from': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-object-rest-spread': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-proposal-optional-chaining': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-private-methods': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.15.0 + '@babel/plugin-transform-arrow-functions': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-block-scoping': 7.15.3_@babel+core@7.15.0 + '@babel/plugin-transform-classes': 7.14.9_@babel+core@7.15.0 + '@babel/plugin-transform-destructuring': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-transform-for-of': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-parameters': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-shorthand-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-spread': 7.14.6_@babel+core@7.15.0 + '@babel/preset-env': 7.15.0_@babel+core@7.15.0 + '@babel/preset-react': 7.14.5_@babel+core@7.15.0 + '@babel/preset-typescript': 7.15.0_@babel+core@7.15.0 + '@babel/register': 7.15.3_@babel+core@7.15.0 + '@storybook/node-logger': 6.3.7 + '@storybook/semver': 7.3.2 + '@types/glob-base': 0.3.0 + '@types/micromatch': 4.0.2 + '@types/node': 14.17.10 + '@types/pretty-hrtime': 1.0.1 + babel-loader: 8.2.2_be352a5a80662835a7707f972edfcfde + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.15.0 + chalk: 4.1.2 + core-js: 3.16.2 + express: 4.17.1 + file-system-cache: 1.0.5 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.3.2 + glob: 7.1.7 + glob-base: 0.3.0 + interpret: 2.2.0 + json5: 2.2.0 + lazy-universal-dotenv: 3.0.1 + micromatch: 4.0.4 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/core-common/6.3.7_8073bd74a106ff14517e8eecceb690e6: resolution: {integrity: sha512-exLoqRPPsAefwyjbsQBLNFrlPCcv69Q/pclqmIm7FqAPR7f3CKP1rqsHY0PnemizTL/+cLX5S7mY90gI6wpNug==} peerDependencies: @@ -5651,6 +6306,71 @@ packages: - webpack-command dev: true + /@storybook/core-common/6.3.7_typescript@3.9.10: + resolution: {integrity: sha512-exLoqRPPsAefwyjbsQBLNFrlPCcv69Q/pclqmIm7FqAPR7f3CKP1rqsHY0PnemizTL/+cLX5S7mY90gI6wpNug==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/plugin-proposal-class-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-decorators': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-export-default-from': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-nullish-coalescing-operator': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-object-rest-spread': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-proposal-optional-chaining': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-proposal-private-methods': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-syntax-dynamic-import': 7.8.3_@babel+core@7.15.0 + '@babel/plugin-transform-arrow-functions': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-block-scoping': 7.15.3_@babel+core@7.15.0 + '@babel/plugin-transform-classes': 7.14.9_@babel+core@7.15.0 + '@babel/plugin-transform-destructuring': 7.14.7_@babel+core@7.15.0 + '@babel/plugin-transform-for-of': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-parameters': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-shorthand-properties': 7.14.5_@babel+core@7.15.0 + '@babel/plugin-transform-spread': 7.14.6_@babel+core@7.15.0 + '@babel/preset-env': 7.15.0_@babel+core@7.15.0 + '@babel/preset-react': 7.14.5_@babel+core@7.15.0 + '@babel/preset-typescript': 7.15.0_@babel+core@7.15.0 + '@babel/register': 7.15.3_@babel+core@7.15.0 + '@storybook/node-logger': 6.3.7 + '@storybook/semver': 7.3.2 + '@types/glob-base': 0.3.0 + '@types/micromatch': 4.0.2 + '@types/node': 14.17.10 + '@types/pretty-hrtime': 1.0.1 + babel-loader: 8.2.2_be352a5a80662835a7707f972edfcfde + babel-plugin-macros: 3.1.0 + babel-plugin-polyfill-corejs3: 0.1.7_@babel+core@7.15.0 + chalk: 4.1.2 + core-js: 3.16.2 + express: 4.17.1 + file-system-cache: 1.0.5 + find-up: 5.0.0 + fork-ts-checker-webpack-plugin: 6.3.2 + glob: 7.1.7 + glob-base: 0.3.0 + interpret: 2.2.0 + json5: 2.2.0 + lazy-universal-dotenv: 3.0.1 + micromatch: 4.0.4 + pkg-dir: 5.0.0 + pretty-hrtime: 1.0.3 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/core-common/6.3.7_typescript@4.3.5: resolution: {integrity: sha512-exLoqRPPsAefwyjbsQBLNFrlPCcv69Q/pclqmIm7FqAPR7f3CKP1rqsHY0PnemizTL/+cLX5S7mY90gI6wpNug==} peerDependencies: @@ -5716,12 +6436,136 @@ packages: - webpack-command dev: true + /@storybook/core-events/6.3.12: + resolution: {integrity: sha512-SXfD7xUUMazaeFkB92qOTUV8Y/RghE4SkEYe5slAdjeocSaH7Nz2WV0rqNEgChg0AQc+JUI66no8L9g0+lw4Gw==} + dependencies: + core-js: 3.16.2 + dev: true + /@storybook/core-events/6.3.7: resolution: {integrity: sha512-l5Hlhe+C/dqxjobemZ6DWBhTOhQoFF3F1Y4kjFGE7pGZl/mas4M72I5I/FUcYCmbk2fbLfZX8hzKkUqS1hdyLA==} dependencies: core-js: 3.16.2 dev: true + /@storybook/core-server/6.3.7_36f75bb62e0c484c1a06658ad2872463: + resolution: {integrity: sha512-m5OPD/rmZA7KFewkXzXD46/i1ngUoFO4LWOiAY/wR6RQGjYXGMhSa5UYFF6MNwSbiGS5YieHkR5crB1HP47AhQ==} + peerDependencies: + '@storybook/builder-webpack5': 6.3.7 + '@storybook/manager-webpack5': 6.3.7 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + '@storybook/manager-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/builder-webpack4': 6.3.7_typescript@3.9.10 + '@storybook/core-client': 6.3.7_typescript@3.9.10+webpack@4.46.0 + '@storybook/core-common': 6.3.7_typescript@3.9.10 + '@storybook/csf-tools': 6.3.7_@babel+core@7.15.0 + '@storybook/manager-webpack4': 6.3.7_typescript@3.9.10 + '@storybook/node-logger': 6.3.7 + '@storybook/semver': 7.3.2 + '@types/node': 14.17.10 + '@types/node-fetch': 2.5.12 + '@types/pretty-hrtime': 1.0.1 + '@types/webpack': 4.41.30 + better-opn: 2.1.1 + boxen: 4.2.0 + chalk: 4.1.2 + cli-table3: 0.6.0 + commander: 6.2.1 + compression: 1.7.4 + core-js: 3.16.2 + cpy: 8.1.2 + detect-port: 1.3.0 + express: 4.17.1 + file-system-cache: 1.0.5 + fs-extra: 9.1.0 + globby: 11.0.4 + ip: 1.1.5 + node-fetch: 2.6.1 + pretty-hrtime: 1.0.3 + prompts: 2.4.1 + regenerator-runtime: 0.13.9 + serve-favicon: 2.5.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - supports-color + - webpack-cli + - webpack-command + dev: true + + /@storybook/core-server/6.3.7_6b8328ae33be7bccbaedcbeca6bc1253: + resolution: {integrity: sha512-m5OPD/rmZA7KFewkXzXD46/i1ngUoFO4LWOiAY/wR6RQGjYXGMhSa5UYFF6MNwSbiGS5YieHkR5crB1HP47AhQ==} + peerDependencies: + '@storybook/builder-webpack5': 6.3.7 + '@storybook/manager-webpack5': 6.3.7 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + '@storybook/manager-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/builder-webpack4': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/core-client': 6.3.7_3c33386fd9b1d5f07f48f07869b17b73 + '@storybook/core-common': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/csf-tools': 6.3.7 + '@storybook/manager-webpack4': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/node-logger': 6.3.7 + '@storybook/semver': 7.3.2 + '@types/node': 14.17.10 + '@types/node-fetch': 2.5.12 + '@types/pretty-hrtime': 1.0.1 + '@types/webpack': 4.41.30 + better-opn: 2.1.1 + boxen: 4.2.0 + chalk: 4.1.2 + cli-table3: 0.6.0 + commander: 6.2.1 + compression: 1.7.4 + core-js: 3.16.2 + cpy: 8.1.2 + detect-port: 1.3.0 + express: 4.17.1 + file-system-cache: 1.0.5 + fs-extra: 9.1.0 + globby: 11.0.4 + ip: 1.1.5 + node-fetch: 2.6.1 + pretty-hrtime: 1.0.3 + prompts: 2.4.1 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + regenerator-runtime: 0.13.9 + serve-favicon: 2.5.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + util-deprecate: 1.0.2 + webpack: 4.46.0 + transitivePeerDependencies: + - '@babel/core' + - '@types/react' + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/core-server/6.3.7_f0b419a7e119055c71dcaf6063a7ba7a: resolution: {integrity: sha512-m5OPD/rmZA7KFewkXzXD46/i1ngUoFO4LWOiAY/wR6RQGjYXGMhSa5UYFF6MNwSbiGS5YieHkR5crB1HP47AhQ==} peerDependencies: @@ -5840,6 +6684,60 @@ packages: - webpack-command dev: true + /@storybook/core/6.3.7_36f75bb62e0c484c1a06658ad2872463: + resolution: {integrity: sha512-YTVLPXqgyBg7TALNxQ+cd+GtCm/NFjxr/qQ1mss1T9GCMR0IjE0d0trgOVHHLAO8jCVlK8DeuqZCCgZFTXulRw==} + peerDependencies: + '@storybook/builder-webpack5': 6.3.7 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/core-client': 6.3.7_typescript@3.9.10 + '@storybook/core-server': 6.3.7_36f75bb62e0c484c1a06658ad2872463 + typescript: 3.9.10 + transitivePeerDependencies: + - '@babel/core' + - '@storybook/manager-webpack5' + - '@types/react' + - supports-color + - webpack + - webpack-cli + - webpack-command + dev: true + + /@storybook/core/6.3.7_6b8328ae33be7bccbaedcbeca6bc1253: + resolution: {integrity: sha512-YTVLPXqgyBg7TALNxQ+cd+GtCm/NFjxr/qQ1mss1T9GCMR0IjE0d0trgOVHHLAO8jCVlK8DeuqZCCgZFTXulRw==} + peerDependencies: + '@storybook/builder-webpack5': 6.3.7 + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + '@storybook/builder-webpack5': + optional: true + typescript: + optional: true + dependencies: + '@storybook/core-client': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/core-server': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + typescript: 3.9.10 + transitivePeerDependencies: + - '@babel/core' + - '@storybook/manager-webpack5' + - '@types/react' + - supports-color + - webpack + - webpack-cli + - webpack-command + dev: true + /@storybook/core/6.3.7_f0b419a7e119055c71dcaf6063a7ba7a: resolution: {integrity: sha512-YTVLPXqgyBg7TALNxQ+cd+GtCm/NFjxr/qQ1mss1T9GCMR0IjE0d0trgOVHHLAO8jCVlK8DeuqZCCgZFTXulRw==} peerDependencies: @@ -5894,6 +6792,28 @@ packages: - webpack-command dev: true + /@storybook/csf-tools/6.3.7: + resolution: {integrity: sha512-A7yGsrYwh+vwVpmG8dHpEimX021RbZd9VzoREcILH56u8atssdh/rseljyWlRes3Sr4QgtLvDB7ggoJ+XDZH7w==} + dependencies: + '@babel/generator': 7.15.0 + '@babel/parser': 7.15.3 + '@babel/plugin-transform-react-jsx': 7.14.9 + '@babel/preset-env': 7.15.0 + '@babel/traverse': 7.15.0 + '@babel/types': 7.15.0 + '@mdx-js/mdx': 1.6.22 + '@storybook/csf': 0.0.1 + core-js: 3.16.2 + fs-extra: 9.1.0 + js-string-escape: 1.0.1 + lodash: 4.17.21 + prettier: 2.2.1 + regenerator-runtime: 0.13.9 + transitivePeerDependencies: + - '@babel/core' + - supports-color + dev: true + /@storybook/csf-tools/6.3.7_@babel+core@7.13.16: resolution: {integrity: sha512-A7yGsrYwh+vwVpmG8dHpEimX021RbZd9VzoREcILH56u8atssdh/rseljyWlRes3Sr4QgtLvDB7ggoJ+XDZH7w==} dependencies: @@ -5944,6 +6864,63 @@ packages: lodash: 4.17.21 dev: true + /@storybook/manager-webpack4/6.3.7_6b8328ae33be7bccbaedcbeca6bc1253: + resolution: {integrity: sha512-cwUdO3oklEtx6y+ZOl2zHvflICK85emiXBQGgRcCsnwWQRBZOMh+tCgOSZj4jmISVpT52RtT9woG4jKe15KBig==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/plugin-transform-template-literals': 7.14.5_@babel+core@7.15.0 + '@babel/preset-react': 7.14.5_@babel+core@7.15.0 + '@storybook/addons': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/core-client': 6.3.7_3c33386fd9b1d5f07f48f07869b17b73 + '@storybook/core-common': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/node-logger': 6.3.7 + '@storybook/theming': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/ui': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@types/node': 14.17.10 + '@types/webpack': 4.41.30 + babel-loader: 8.2.2_be352a5a80662835a7707f972edfcfde + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + core-js: 3.16.2 + css-loader: 3.6.0_webpack@4.46.0 + dotenv-webpack: 1.8.0_webpack@4.46.0 + express: 4.17.1 + file-loader: 6.2.0_webpack@4.46.0 + file-system-cache: 1.0.5 + find-up: 5.0.0 + fs-extra: 9.1.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + node-fetch: 2.6.1 + pnp-webpack-plugin: 1.6.4_typescript@3.9.10 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + style-loader: 1.3.0_webpack@4.46.0 + telejson: 5.3.3 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + url-loader: 4.1.1_file-loader@6.2.0+webpack@4.46.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/manager-webpack4/6.3.7_8073bd74a106ff14517e8eecceb690e6: resolution: {integrity: sha512-cwUdO3oklEtx6y+ZOl2zHvflICK85emiXBQGgRcCsnwWQRBZOMh+tCgOSZj4jmISVpT52RtT9woG4jKe15KBig==} peerDependencies: @@ -6001,6 +6978,61 @@ packages: - webpack-command dev: true + /@storybook/manager-webpack4/6.3.7_typescript@3.9.10: + resolution: {integrity: sha512-cwUdO3oklEtx6y+ZOl2zHvflICK85emiXBQGgRcCsnwWQRBZOMh+tCgOSZj4jmISVpT52RtT9woG4jKe15KBig==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@babel/core': 7.15.0 + '@babel/plugin-transform-template-literals': 7.14.5_@babel+core@7.15.0 + '@babel/preset-react': 7.14.5_@babel+core@7.15.0 + '@storybook/addons': 6.3.7 + '@storybook/core-client': 6.3.7_typescript@3.9.10+webpack@4.46.0 + '@storybook/core-common': 6.3.7_typescript@3.9.10 + '@storybook/node-logger': 6.3.7 + '@storybook/theming': 6.3.7 + '@storybook/ui': 6.3.7 + '@types/node': 14.17.10 + '@types/webpack': 4.41.30 + babel-loader: 8.2.2_be352a5a80662835a7707f972edfcfde + case-sensitive-paths-webpack-plugin: 2.4.0 + chalk: 4.1.2 + core-js: 3.16.2 + css-loader: 3.6.0_webpack@4.46.0 + dotenv-webpack: 1.8.0_webpack@4.46.0 + express: 4.17.1 + file-loader: 6.2.0_webpack@4.46.0 + file-system-cache: 1.0.5 + find-up: 5.0.0 + fs-extra: 9.1.0 + html-webpack-plugin: 4.5.2_webpack@4.46.0 + node-fetch: 2.6.1 + pnp-webpack-plugin: 1.6.4_typescript@3.9.10 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + resolve-from: 5.0.0 + style-loader: 1.3.0_webpack@4.46.0 + telejson: 5.3.3 + terser-webpack-plugin: 4.2.3_webpack@4.46.0 + ts-dedent: 2.2.0 + typescript: 3.9.10 + url-loader: 4.1.1_file-loader@6.2.0+webpack@4.46.0 + util-deprecate: 1.0.2 + webpack: 4.46.0 + webpack-dev-middleware: 3.7.3_webpack@4.46.0 + webpack-virtual-modules: 0.2.2 + transitivePeerDependencies: + - '@types/react' + - supports-color + - webpack-cli + - webpack-command + dev: true + /@storybook/manager-webpack4/6.3.7_typescript@4.3.5: resolution: {integrity: sha512-cwUdO3oklEtx6y+ZOl2zHvflICK85emiXBQGgRcCsnwWQRBZOMh+tCgOSZj4jmISVpT52RtT9woG4jKe15KBig==} peerDependencies: @@ -6105,6 +7137,66 @@ packages: - webpack-command dev: true + /@storybook/preact/6.3.7_preact@10.5.14+typescript@3.9.10: + resolution: {integrity: sha512-mP6+e1toCd59ALUNsiJWQN0CuOVV7faaMcAs21T+GJeU5igEWbRpe/ixKdMdu7RqHA9jAHOeMZfjSNhBkvnwAw==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + '@babel/core': '*' + preact: ^8.0.0||^10.0.0 + webpack: '*' + dependencies: + '@babel/plugin-transform-react-jsx': 7.14.9 + '@storybook/addons': 6.3.7_react-dom@16.14.0+react@16.14.0 + '@storybook/core': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@storybook/core-common': 6.3.7_6b8328ae33be7bccbaedcbeca6bc1253 + '@types/webpack-env': 1.16.2 + core-js: 3.16.2 + global: 4.4.0 + preact: 10.5.14 + react: 16.14.0 + react-dom: 16.14.0_react@16.14.0 + read-pkg-up: 7.0.1 + regenerator-runtime: 0.13.9 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - '@storybook/builder-webpack5' + - '@storybook/manager-webpack5' + - '@types/react' + - supports-color + - typescript + - webpack-cli + - webpack-command + dev: true + + /@storybook/preset-scss/1.0.3_sass-loader@10.2.0: + resolution: {integrity: sha512-o9Iz6wxPeNENrQa2mKlsDKynBfqU2uWaRP80HeWp4TkGgf7/x3DVF2O7yi9N0x/PI1qzzTTpxlQ90D62XmpiTw==} + peerDependencies: + css-loader: '*' + sass-loader: '*' + style-loader: '*' + dependencies: + sass-loader: 10.2.0_sass@1.43.2 + dev: true + + /@storybook/router/6.3.12: + resolution: {integrity: sha512-G/pNGCnrJRetCwyEZulHPT+YOcqEj/vkPVDTUfii2qgqukup6K0cjwgd7IukAURnAnnzTi1gmgFuEKUi8GE/KA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@reach/router': 1.3.4 + '@storybook/client-logger': 6.3.12 + '@types/reach__router': 1.3.9 + core-js: 3.16.2 + fast-deep-equal: 3.1.3 + global: 4.4.0 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.10.1 + ts-dedent: 2.2.0 + dev: true + /@storybook/router/6.3.7: resolution: {integrity: sha512-6tthN8op7H0NoYoE1SkQFJKC42pC4tGaoPn7kEql8XGeUJnxPtVFjrnywlTrRnKuxZKIvbilQBAwDml97XH23Q==} peerDependencies: @@ -6170,6 +7262,26 @@ packages: regenerator-runtime: 0.13.9 dev: true + /@storybook/theming/6.3.12: + resolution: {integrity: sha512-wOJdTEa/VFyFB2UyoqyYGaZdym6EN7RALuQOAMT6zHA282FBmKw8nL5DETHEbctpnHdcrMC/391teK4nNSrdOA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 + react-dom: ^16.8.0 || ^17.0.0 + dependencies: + '@emotion/core': 10.1.1 + '@emotion/is-prop-valid': 0.8.8 + '@emotion/styled': 10.0.27_@emotion+core@10.1.1 + '@storybook/client-logger': 6.3.12 + core-js: 3.16.2 + deep-object-diff: 1.1.0 + emotion-theming: 10.0.27_@emotion+core@10.1.1 + global: 4.4.0 + memoizerific: 1.11.3 + polished: 4.1.3 + resolve-from: 5.0.0 + ts-dedent: 2.2.0 + dev: true + /@storybook/theming/6.3.7: resolution: {integrity: sha512-GXBdw18JJd5jLLcRonAZWvGGdwEXByxF1IFNDSOYCcpHWsMgTk4XoLjceu9MpXET04pVX51LbVPLCvMdggoohg==} peerDependencies: @@ -7709,7 +8821,7 @@ packages: /axios/0.21.1: resolution: {integrity: sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==} dependencies: - follow-redirects: 1.14.2_debug@4.3.2 + follow-redirects: 1.14.2 transitivePeerDependencies: - debug @@ -11506,7 +12618,7 @@ packages: readable-stream: 2.3.7 dev: true - /follow-redirects/1.14.2_debug@4.3.2: + /follow-redirects/1.14.2: resolution: {integrity: sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA==} engines: {node: '>=4.0'} peerDependencies: @@ -11514,8 +12626,6 @@ packages: peerDependenciesMeta: debug: optional: true - dependencies: - debug: 4.3.2_supports-color@6.1.0 /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -12550,7 +13660,7 @@ packages: engines: {node: '>=8.0.0'} dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.14.2_debug@4.3.2 + follow-redirects: 1.14.2 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -16025,6 +17135,15 @@ packages: resolution: {integrity: sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==} dev: true + /pnp-webpack-plugin/1.6.4_typescript@3.9.10: + resolution: {integrity: sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==} + engines: {node: '>=6'} + dependencies: + ts-pnp: 1.2.0_typescript@3.9.10 + transitivePeerDependencies: + - typescript + dev: true + /pnp-webpack-plugin/1.6.4_typescript@4.3.5: resolution: {integrity: sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==} engines: {node: '>=6'} @@ -16038,7 +17157,7 @@ packages: resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==} engines: {node: '>=6'} dependencies: - ts-pnp: 1.2.0_typescript@4.4.3 + ts-pnp: 1.2.0_typescript@4.3.5 transitivePeerDependencies: - typescript dev: true @@ -19791,7 +20910,7 @@ packages: resolution: {integrity: sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==} dev: true - /ts-pnp/1.2.0_typescript@4.3.5: + /ts-pnp/1.2.0_typescript@3.9.10: resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} engines: {node: '>=6'} peerDependencies: @@ -19800,10 +20919,10 @@ packages: typescript: optional: true dependencies: - typescript: 4.3.5 + typescript: 3.9.10 dev: true - /ts-pnp/1.2.0_typescript@4.4.3: + /ts-pnp/1.2.0_typescript@4.3.5: resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} engines: {node: '>=6'} peerDependencies: @@ -19812,7 +20931,7 @@ packages: typescript: optional: true dependencies: - typescript: 4.4.3 + typescript: 4.3.5 dev: true /tsconfig-paths/3.9.0: |