Merge pull request #1764 from influxdata/feat/onboarding-routing

Handling onboarding with react router
pull/10616/head
Brandon Farmer 2018-12-06 13:54:48 -08:00 committed by GitHub
commit 3b8578c7d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 215 additions and 111 deletions

View File

@ -1,6 +1,7 @@
// Libraries
import React, {ReactElement, PureComponent} from 'react'
import {connect} from 'react-redux'
import {InjectedRouter} from 'react-router'
// APIs
import {getSetupStatus} from 'src/onboarding/apis'
@ -10,8 +11,9 @@ import {notify as notifyAction} from 'src/shared/actions/notifications'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import OnboardingWizard from 'src/onboarding/containers/OnboardingWizard'
import Notifications from 'src/shared/components/notifications/Notifications'
// Utils
import {isOnboardingURL} from 'src/onboarding/utils'
// Types
import {Notification, NotificationFunc, RemoteDataState} from 'src/types'
@ -24,6 +26,7 @@ interface State {
interface Props {
links: Links
router: InjectedRouter
children: ReactElement<any>
notify: (message: Notification | NotificationFunc) => void
}
@ -40,35 +43,35 @@ export class Setup extends PureComponent<Props, State> {
}
public async componentDidMount() {
const {links} = this.props
const {links, router} = this.props
if (isOnboardingURL()) {
this.setState({
loading: RemoteDataState.Done,
})
return
}
const isSetupAllowed = await getSetupStatus(links.setup)
this.setState({
loading: RemoteDataState.Done,
isSetupComplete: !isSetupAllowed,
})
if (!isSetupAllowed) {
return
}
router.push('/onboarding/0')
}
public render() {
const {isSetupComplete} = this.state
if (this.isLoading) {
return <div className="page-spinner" />
}
if (!isSetupComplete) {
return (
<div className="chronograf-root">
<Notifications inPresentationMode={true} />
<OnboardingWizard onCompleteSetup={this.handleCompleteSetup} />
</div>
)
} else {
return this.props.children && React.cloneElement(this.props.children)
}
}
public handleCompleteSetup = () => {
this.setState({isSetupComplete: true})
}
private get isLoading(): boolean {
const {loading} = this.state
return (

View File

@ -33,6 +33,8 @@ import GetLinks from 'src/shared/containers/GetLinks'
import GetMe from 'src/shared/containers/GetMe'
import SourcesPage from 'src/sources/components/SourcesPage'
import OnboardingWizardPage from 'src/onboarding/containers/OnboardingWizardPage'
// Actions
import {disablePresentationMode} from 'src/shared/actions/app'
@ -77,6 +79,14 @@ class Root extends PureComponent {
<Router history={history}>
<Route component={GetLinks}>
<Route component={Setup}>
<Route
path="/onboarding/:stepID"
component={OnboardingWizardPage}
/>
<Route
path="/onboarding/:stepID/:substepID"
component={OnboardingWizardPage}
/>
<Route component={Signin}>
<Route component={GetMe}>
<Route component={GetOrganizations}>
@ -116,8 +126,8 @@ class Root extends PureComponent {
</Route>
</Route>
</Route>
<Route path="*" component={NotFound} />
</Route>
<Route path="*" component={NotFound} />
</Router>
</Provider>
)

View File

@ -4,12 +4,7 @@ import {StepStatus} from 'src/clockface/constants/wizard'
// Types
import {SetupParams} from 'src/onboarding/apis'
export type Action =
| SetSetupParams
| IncrementCurrentStepIndex
| DecrementCurrentStepIndex
| SetCurrentStepIndex
| SetStepStatus
export type Action = SetSetupParams | SetStepStatus
interface SetSetupParams {
type: 'SET_SETUP_PARAMS'
@ -21,32 +16,6 @@ export const setSetupParams = (setupParams: SetupParams): SetSetupParams => ({
payload: {setupParams},
})
interface SetCurrentStepIndex {
type: 'SET_CURRENT_STEP_INDEX'
payload: {index: number}
}
export const setCurrentStepIndex = (index: number): SetCurrentStepIndex => ({
type: 'SET_CURRENT_STEP_INDEX',
payload: {index},
})
interface IncrementCurrentStepIndex {
type: 'INCREMENT_CURRENT_STEP_INDEX'
}
export const incrementCurrentStepIndex = (): IncrementCurrentStepIndex => ({
type: 'INCREMENT_CURRENT_STEP_INDEX',
})
interface DecrementCurrentStepIndex {
type: 'DECREMENT_CURRENT_STEP_INDEX'
}
export const decrementCurrentStepIndex = (): DecrementCurrentStepIndex => ({
type: 'DECREMENT_CURRENT_STEP_INDEX',
})
interface SetStepStatus {
type: 'SET_STEP_STATUS'
payload: {index: number; status: StepStatus}

View File

@ -1,6 +1,7 @@
// Libraries
import React, {PureComponent} from 'react'
import _ from 'lodash'
import {withRouter, WithRouterProps} from 'react-router'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -16,27 +17,44 @@ import ConfigureDataSourceSwitcher from 'src/onboarding/components/configureStep
import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard'
import {TelegrafPlugin, DataLoaderType} from 'src/types/v2/dataLoaders'
export interface Props extends OnboardingStepProps {
export interface OwnProps extends OnboardingStepProps {
telegrafPlugins: TelegrafPlugin[]
type: DataLoaderType
}
interface State {
currentDataSourceIndex: number
interface RouterProps {
params: {
stepID: string
substepID: string
}
}
type Props = OwnProps & WithRouterProps & RouterProps
@ErrorHandling
class ConfigureDataSourceStep extends PureComponent<Props, State> {
class ConfigureDataSourceStep extends PureComponent<Props> {
constructor(props: Props) {
super(props)
}
this.state = {
currentDataSourceIndex: 0,
public componentDidMount() {
const {
router,
params: {stepID, substepID},
} = this.props
if (substepID === undefined) {
router.replace(`/onboarding/${stepID}/0`)
}
}
public render() {
const {telegrafPlugins, type, setupParams} = this.props
const {
telegrafPlugins,
type,
params: {substepID},
setupParams,
} = this.props
return (
<div className="onboarding-step">
@ -44,8 +62,8 @@ class ConfigureDataSourceStep extends PureComponent<Props, State> {
bucket={_.get(setupParams, 'bucket', '')}
org={_.get(setupParams, 'org', '')}
telegrafPlugins={telegrafPlugins}
currentIndex={this.state.currentDataSourceIndex}
dataLoaderType={type}
currentIndex={+substepID}
/>
<div className="wizard-button-bar">
<Button
@ -68,26 +86,27 @@ class ConfigureDataSourceStep extends PureComponent<Props, State> {
}
private handleNext = () => {
const {onIncrementCurrentStepIndex, telegrafPlugins} = this.props
const {currentDataSourceIndex} = this.state
const {
onIncrementCurrentStepIndex,
telegrafPlugins,
params: {substepID, stepID},
router,
} = this.props
if (currentDataSourceIndex >= telegrafPlugins.length - 1) {
const index = +substepID
if (index >= telegrafPlugins.length - 1) {
onIncrementCurrentStepIndex()
} else {
this.setState({currentDataSourceIndex: currentDataSourceIndex + 1})
router.push(`/onboarding/${stepID}/${index + 1}`)
}
}
private handlePrevious = () => {
const {onDecrementCurrentStepIndex} = this.props
const {currentDataSourceIndex} = this.state
const {router} = this.props
if (currentDataSourceIndex === 0) {
onDecrementCurrentStepIndex()
} else {
this.setState({currentDataSourceIndex: currentDataSourceIndex - 1})
}
router.goBack()
}
}
export default ConfigureDataSourceStep
export default withRouter<OwnProps>(ConfigureDataSourceStep)

View File

@ -1,5 +1,6 @@
// Libraries
import React, {PureComponent} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
@ -21,7 +22,7 @@ import {
TelegrafPluginName,
} from 'src/types/v2/dataLoaders'
export interface Props extends OnboardingStepProps {
export interface OwnProps extends OnboardingStepProps {
bucket: string
telegrafPlugins: TelegrafPlugin[]
type: DataLoaderType
@ -30,6 +31,15 @@ export interface Props extends OnboardingStepProps {
onSetDataLoadersType: (type: DataLoaderType) => void
}
interface RouterProps {
params: {
stepID: string
substepID: string
}
}
type Props = OwnProps & RouterProps & WithRouterProps
interface State {
showStreamingSources: boolean
}
@ -72,7 +82,7 @@ class SelectDataSourceStep extends PureComponent<Props, State> {
private get title(): string {
const {bucket} = this.props
if (this.state.showStreamingSources) {
if (this.isStreaming) {
return `Select Streaming Data Sources to add to ${bucket ||
'your bucket'}`
}
@ -80,10 +90,7 @@ class SelectDataSourceStep extends PureComponent<Props, State> {
}
private get selector(): JSX.Element {
if (
this.props.type === DataLoaderType.Streaming &&
this.state.showStreamingSources
) {
if (this.props.type === DataLoaderType.Streaming && this.isStreaming) {
return (
<StreamingDataSourceSelector
telegrafPlugins={this.props.telegrafPlugins}
@ -100,11 +107,13 @@ class SelectDataSourceStep extends PureComponent<Props, State> {
}
private handleClickNext = () => {
if (
this.props.type === DataLoaderType.Streaming &&
!this.state.showStreamingSources
) {
this.setState({showStreamingSources: true})
const {
router,
params: {stepID},
} = this.props
if (this.props.type === DataLoaderType.Streaming && !this.isStreaming) {
router.push(`/onboarding/${stepID}/streaming`)
return
}
@ -112,11 +121,6 @@ class SelectDataSourceStep extends PureComponent<Props, State> {
}
private handleClickBack = () => {
if (this.props.type === DataLoaderType.Streaming) {
this.setState({showStreamingSources: false})
return
}
this.props.onDecrementCurrentStepIndex()
}
@ -145,6 +149,10 @@ class SelectDataSourceStep extends PureComponent<Props, State> {
}
this.props.onAddTelegrafPlugin(plugin)
}
private get isStreaming(): boolean {
return this.props.params.substepID === 'streaming'
}
}
export default SelectDataSourceStep
export default withRouter<OwnProps>(SelectDataSourceStep)

View File

@ -16,13 +16,8 @@ import OnboardingStepSwitcher from 'src/onboarding/components/OnboardingStepSwit
// Actions
import {notify as notifyAction} from 'src/shared/actions/notifications'
import {
setSetupParams,
incrementCurrentStepIndex,
decrementCurrentStepIndex,
setCurrentStepIndex,
setStepStatus,
} from 'src/onboarding/actions/steps'
import {setSetupParams, setStepStatus} from 'src/onboarding/actions/steps'
import {
setDataLoadersType,
addTelegrafPlugin,
@ -62,14 +57,15 @@ interface OwnProps {
startStep?: number
stepStatuses?: StepStatus[]
onCompleteSetup: () => void
currentStepIndex: number
onIncrementCurrentStepIndex: () => void
onDecrementCurrentStepIndex: () => void
onSetCurrentStepIndex: (stepNumber: number) => void
}
interface DispatchProps {
notify: (message: Notification | NotificationFunc) => void
onSetSetupParams: typeof setSetupParams
onIncrementCurrentStepIndex: typeof incrementCurrentStepIndex
onDecrementCurrentStepIndex: typeof decrementCurrentStepIndex
onSetCurrentStepIndex: typeof setCurrentStepIndex
onSetStepStatus: typeof setStepStatus
onSetDataLoadersType: typeof setDataLoadersType
onAddTelegrafPlugin: typeof addTelegrafPlugin
@ -84,7 +80,6 @@ interface DataLoadersProps {
interface StateProps {
links: Links
currentStepIndex: number
stepStatuses: StepStatus[]
setupParams: SetupParams
dataLoaders: DataLoadersProps
@ -238,12 +233,11 @@ class OnboardingWizard extends PureComponent<Props> {
const mstp = ({
links,
onboarding: {
steps: {currentStepIndex, stepStatuses, setupParams},
steps: {stepStatuses, setupParams},
dataLoaders,
},
}: AppState): StateProps => ({
links,
currentStepIndex,
stepStatuses,
setupParams,
dataLoaders,
@ -252,9 +246,6 @@ const mstp = ({
const mdtp: DispatchProps = {
notify: notifyAction,
onSetSetupParams: setSetupParams,
onDecrementCurrentStepIndex: decrementCurrentStepIndex,
onIncrementCurrentStepIndex: incrementCurrentStepIndex,
onSetCurrentStepIndex: setCurrentStepIndex,
onSetStepStatus: setStepStatus,
onSetDataLoadersType: setDataLoadersType,
onAddTelegrafPlugin: addTelegrafPlugin,

View File

@ -0,0 +1,109 @@
// Libraries
import React, {ReactElement, PureComponent} from 'react'
import {connect} from 'react-redux'
import {withRouter, WithRouterProps} from 'react-router'
// Actions
import {notify as notifyAction} from 'src/shared/actions/notifications'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import OnboardingWizard from 'src/onboarding/containers/OnboardingWizard'
import Notifications from 'src/shared/components/notifications/Notifications'
// Types
import {Notification, NotificationFunc, RemoteDataState} from 'src/types'
import {Links} from 'src/types/v2/links'
interface State {
loading: RemoteDataState
isSetupComplete: boolean
}
interface PassedProps {
children: ReactElement<any>
params: {
stepID: string
}
}
interface ConnectedStateProps {
links: Links
}
interface ConnectedDispatchProps {
notify: (message: Notification | NotificationFunc) => void
}
type Props = PassedProps &
WithRouterProps &
ConnectedStateProps &
ConnectedDispatchProps
@ErrorHandling
export class OnboardingWizardPage extends PureComponent<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
loading: RemoteDataState.NotStarted,
isSetupComplete: false,
}
}
public render() {
const {params} = this.props
return (
<div className="chronograf-root">
<Notifications inPresentationMode={true} />
<OnboardingWizard
onDecrementCurrentStepIndex={this.handleDecrementStepIndex}
onIncrementCurrentStepIndex={this.handleIncrementStepIndex}
onSetCurrentStepIndex={this.setStepIndex}
currentStepIndex={+params.stepID}
onCompleteSetup={this.handleCompleteSetup}
/>
</div>
)
}
public handleCompleteSetup = () => {
this.setState({isSetupComplete: true})
}
private handleDecrementStepIndex = () => {
const {router} = this.props
router.goBack()
}
private handleIncrementStepIndex = () => {
const {
params: {stepID},
} = this.props
this.setStepIndex(+stepID + 1)
}
private setStepIndex = (index: number) => {
const {router} = this.props
router.push(`/onboarding/${index}`)
}
}
const mstp = ({links}) => ({links})
const mdtp = {
notify: notifyAction,
}
export default connect<
ConnectedStateProps,
ConnectedDispatchProps,
PassedProps
>(
mstp,
mdtp
)(withRouter<Props>(OnboardingWizardPage))

View File

@ -6,13 +6,11 @@ import {Action} from 'src/onboarding/actions/steps'
import {SetupParams} from 'src/onboarding/apis'
export interface OnboardingStepsState {
currentStepIndex: number
stepStatuses: StepStatus[]
setupParams: SetupParams
}
const INITIAL_STATE: OnboardingStepsState = {
currentStepIndex: 0,
stepStatuses: new Array(6).fill(StepStatus.Incomplete),
setupParams: null,
}
@ -24,12 +22,6 @@ export default (
switch (action.type) {
case 'SET_SETUP_PARAMS':
return {...state, setupParams: action.payload.setupParams}
case 'INCREMENT_CURRENT_STEP_INDEX':
return {...state, currentStepIndex: state.currentStepIndex + 1}
case 'DECREMENT_CURRENT_STEP_INDEX':
return {...state, currentStepIndex: state.currentStepIndex - 1}
case 'SET_CURRENT_STEP_INDEX':
return {...state, currentStepIndex: action.payload.index}
case 'SET_STEP_STATUS':
const stepStatuses = [...state.stepStatuses]
stepStatuses[action.payload.index] = action.payload.status

View File

@ -0,0 +1,3 @@
export const isOnboardingURL = () => {
return !!window.location.pathname.match(/\/onboarding/)
}