From 9b1e24c9ad67cc2d8fa82bf84d702ee88880161d Mon Sep 17 00:00:00 2001 From: meriadec Date: Mon, 7 May 2018 16:56:00 +0200 Subject: [PATCH] Skeleton for the new onboarding --- .../Onboarding/OnboardingBreadcrumb.js | 32 +++++ src/components/Onboarding/index.js | 118 ++++++------------ src/components/Onboarding/steps/Init.js | 18 +++ src/components/Onboarding/steps/UserChoice.js | 17 +++ src/reducers/index.js | 4 + src/reducers/onboarding.js | 84 +++++++++++++ 6 files changed, 193 insertions(+), 80 deletions(-) create mode 100644 src/components/Onboarding/OnboardingBreadcrumb.js create mode 100644 src/components/Onboarding/steps/Init.js create mode 100644 src/components/Onboarding/steps/UserChoice.js create mode 100644 src/reducers/onboarding.js diff --git a/src/components/Onboarding/OnboardingBreadcrumb.js b/src/components/Onboarding/OnboardingBreadcrumb.js new file mode 100644 index 00000000..8e789699 --- /dev/null +++ b/src/components/Onboarding/OnboardingBreadcrumb.js @@ -0,0 +1,32 @@ +// @flow + +import React from 'react' +import { connect } from 'react-redux' +import findIndex from 'lodash/findIndex' + +import type { OnboardingState } from 'reducers/onboarding' + +import Breadcrumb from 'components/Breadcrumb' + +const mapStateToProps = state => ({ + onboarding: state.onboarding, +}) + +type Props = { + onboarding: OnboardingState, +} + +function OnboardingBreadcrumb(props: Props) { + const { onboarding } = props + const { stepName } = onboarding + + const filteredSteps = onboarding.steps + .filter(step => !step.external) + .map(step => ({ ...step, label: step.label })) // TODO: translate + + const stepIndex = findIndex(filteredSteps, s => s.name === stepName) + + return +} + +export default connect(mapStateToProps)(OnboardingBreadcrumb) diff --git a/src/components/Onboarding/index.js b/src/components/Onboarding/index.js index 0a429687..8ed00c46 100644 --- a/src/components/Onboarding/index.js +++ b/src/components/Onboarding/index.js @@ -7,108 +7,83 @@ import { connect } from 'react-redux' import styled from 'styled-components' import type { T } from 'types/common' +import type { OnboardingState } from 'reducers/onboarding' import { saveSettings } from 'actions/settings' +import { nextStep, prevStep, jumpStep } from 'reducers/onboarding' import Box from 'components/base/Box' -import Button from 'components/base/Button' - -const STEPS = [ - { - title: ({ t }) => t('onboarding:step1.title'), - render: ({ t }: StepProps) => {t('onboarding:step1.greetings')}, - }, - { - title: ({ t }) => t('onboarding:step2.title'), - render: ({ t }: StepProps) => {t('onboarding:step2.greetings')}, - }, - { - title: ({ t }) => t('onboarding:step3.title'), - render: ({ t }: StepProps) => ( - - {t('onboarding:step3.greetings')} - - {'step 3 image'} - - {t('onboarding:step3.description')} - - ), - }, - { - title: ({ t }) => t('onboarding:step4.title'), - render: ({ t }: StepProps) => {t('onboarding:step4.greetings')}, - }, -] + +import OnboardingBreadcrumb from './OnboardingBreadcrumb' +import InitStep from './steps/Init' +import UserChoice from './steps/UserChoice' + +const STEPS = { + init: InitStep, + userChoice: UserChoice, +} const mapStateToProps = state => ({ hasCompletedOnboarding: state.settings.hasCompletedOnboarding, + onboarding: state.onboarding, }) const mapDispatchToProps = { saveSettings, + nextStep, + prevStep, + jumpStep, } type Props = { t: T, hasCompletedOnboarding: boolean, saveSettings: Function, + onboarding: OnboardingState, + prevStep: Function, + nextStep: Function, + jumpStep: Function, } -type StepProps = { +export type StepProps = { t: T, + prevStep: Function, + nextStep: Function, + jumpStep: Function, + finish: Function, } -type State = { - stepIndex: number, -} - -class Onboarding extends PureComponent { - state = { - stepIndex: 0, - } - - prev = () => this.setState({ stepIndex: Math.max(0, this.state.stepIndex - 1) }) - next = () => this.setState({ stepIndex: Math.min(STEPS.length - 1, this.state.stepIndex + 1) }) +class Onboarding extends PureComponent { finish = () => this.props.saveSettings({ hasCompletedOnboarding: true }) render() { - const { hasCompletedOnboarding, t } = this.props - const { stepIndex } = this.state + const { hasCompletedOnboarding, onboarding, prevStep, nextStep, jumpStep, t } = this.props if (hasCompletedOnboarding) { return null } - const step = STEPS[stepIndex] + const StepComponent = STEPS[onboarding.stepName] + const step = onboarding.steps[onboarding.stepIndex] - if (!step) { + if (!StepComponent || !step) { + console.warn(`You reached an impossible onboarding step.`) // eslint-disable-line return null } - const stepProps = { + const stepProps: StepProps = { t, + onboarding, + prevStep, + nextStep, + jumpStep, + finish: this.finish, } return ( - - - - {stepIndex === STEPS.length - 1 ? ( - - ) : ( - - )} - - {step.title(stepProps)} - {step.render(stepProps)} - + {step.options.showBreadcrumb && } + ) } @@ -116,9 +91,6 @@ class Onboarding extends PureComponent { const Container = styled(Box).attrs({ bg: 'white', - p: 6, - align: 'center', - justify: 'center', })` position: fixed; top: 0; @@ -128,18 +100,4 @@ const Container = styled(Box).attrs({ z-index: 100; ` -const Inner = styled(Box).attrs({ - bg: 'lightGraphite', - p: 4, -})` - border: 1px solid rgba(0, 0, 0, 0.1); - height: 400px; - width: 400px; - border-radius: 3px; -` - -const StepTitle = styled(Box).attrs({ - fontSize: 8, -})`` - export default compose(connect(mapStateToProps, mapDispatchToProps), translate())(Onboarding) diff --git a/src/components/Onboarding/steps/Init.js b/src/components/Onboarding/steps/Init.js new file mode 100644 index 00000000..76ae3cb3 --- /dev/null +++ b/src/components/Onboarding/steps/Init.js @@ -0,0 +1,18 @@ +// @flow + +import React from 'react' + +import Button from 'components/base/Button' +import Box from 'components/base/Box' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { nextStep } = props + return ( + + hey im step init + + + ) +} diff --git a/src/components/Onboarding/steps/UserChoice.js b/src/components/Onboarding/steps/UserChoice.js new file mode 100644 index 00000000..a4d407a8 --- /dev/null +++ b/src/components/Onboarding/steps/UserChoice.js @@ -0,0 +1,17 @@ +// @flow + +import React from 'react' + +import Button from 'components/base/Button' + +import type { StepProps } from '..' + +export default (props: StepProps) => { + const { jumpStep } = props + return ( +
+ hey im step user choice + +
+ ) +} diff --git a/src/reducers/index.js b/src/reducers/index.js index 5963f822..a73be396 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -13,6 +13,7 @@ import devices from './devices' import modals from './modals' import settings from './settings' import update from './update' +import onboarding from './onboarding' import type { AccountsState } from './accounts' import type { ApplicationState } from './application' @@ -20,6 +21,7 @@ import type { DevicesState } from './devices' import type { ModalsState } from './modals' import type { SettingsState } from './settings' import type { UpdateState } from './update' +import type { OnboardingState } from './onboarding' export type State = { accounts: AccountsState, @@ -30,6 +32,7 @@ export type State = { router: LocationShape, settings: SettingsState, update: UpdateState, + onboarding: OnboardingState, } export default combineReducers({ @@ -41,4 +44,5 @@ export default combineReducers({ router, settings, update, + onboarding, }) diff --git a/src/reducers/onboarding.js b/src/reducers/onboarding.js new file mode 100644 index 00000000..22a24fa3 --- /dev/null +++ b/src/reducers/onboarding.js @@ -0,0 +1,84 @@ +// @flow + +import { handleActions, createAction } from 'redux-actions' + +type Step = { + name: string, + external?: boolean, + label?: string, + options: { + showFooter: boolean, + showBackground: boolean, + showBreadcrumb: boolean, + }, +} + +export type OnboardingState = { + stepIndex: number, + stepName: string, // TODO: specify that the string comes from Steps type + steps: Step[], +} + +const state: OnboardingState = { + stepIndex: 0, + stepName: 'init', + steps: [ + { + name: 'init', + external: true, + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: false, + }, + }, + { + name: 'userChoice', + label: 'something:translated', + options: { + showFooter: false, + showBackground: true, + showBreadcrumb: true, + }, + }, + ], +} + +const handlers = { + ONBOARDING_NEXT_STEP: state => { + const step = state.steps.find(step => step.name === state.stepName) + if (!step) { + return state + } + const index = state.steps.indexOf(step) + if (index > state.steps.length - 2) { + return state + } + return { ...state, stepName: state.steps[index + 1].name, stepIndex: index + 1 } + }, + ONBOARDING_PREV_STEP: state => { + const step = state.steps.find(step => step.name === state.stepName) + if (!step) { + return state + } + const index = state.steps.indexOf(step) + if (index < 1) { + return state + } + return { ...state, stepName: state.steps[index - 1].name, stepIndex: index - 1 } + }, + ONBOARDING_JUMP_STEP: (state, { payload: stepName }) => { + const step = state.steps.find(step => step.name === stepName) + if (!step) { + return state + } + const index = state.steps.indexOf(step) + return { ...state, stepName: step.name, stepIndex: index } + }, +} + +export default handleActions(handlers, state) + +export const nextStep = createAction('ONBOARDING_NEXT_STEP') +export const prevStep = createAction('ONBOARDING_PREV_STEP') +export const jumpStep = createAction('ONBOARDING_JUMP_STEP')