Skip to content

Commit 67d96a1

Browse files
authored
feat: adds wizard nav and tests to the cli onboarding (#5176)
* feat: adds wizard nav and tests to the cli onboarding
1 parent 7847bd5 commit 67d96a1

File tree

3 files changed

+304
-5
lines changed

3 files changed

+304
-5
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Libraries
2+
import React from 'react'
3+
import {fireEvent, screen} from '@testing-library/react'
4+
import {renderWithRedux} from 'src/mockState'
5+
6+
// Components
7+
import {CliWizard} from 'src/homepageExperience/containers/CliWizard'
8+
9+
jest.mock('canvas-confetti')
10+
jest.mock('src/homepageExperience/assets/sample.csv', () => {
11+
return 'csv, csv, csv'
12+
})
13+
jest.mock('assets/images/sample-csv.png', () => {
14+
return 'csv screenshot'
15+
})
16+
17+
const setup = () => {
18+
return renderWithRedux(<CliWizard />)
19+
}
20+
21+
describe('Navigation', () => {
22+
describe('Next and Previous Buttons', () => {
23+
it('cannot click next on final step', async () => {
24+
setup()
25+
await fireEvent.click(screen.getByText('Finished!'))
26+
const nextButton = screen.getByTestId('cli-next-button')
27+
expect(nextButton).toHaveAttribute('disabled')
28+
})
29+
it('cannot click previous on first step', async () => {
30+
setup()
31+
await fireEvent.click(screen.getByText('Overview'))
32+
const prevButton = screen.getByTestId('cli-prev-button')
33+
expect(prevButton).toHaveAttribute('disabled')
34+
})
35+
it('can click next on the first step', async () => {
36+
setup()
37+
await fireEvent.click(screen.getByText('Overview'))
38+
const prevButton = screen.getByTestId('cli-next-button')
39+
expect(prevButton).not.toHaveAttribute('disabled')
40+
})
41+
it('can click previous on the last step', async () => {
42+
setup()
43+
await fireEvent.click(screen.getByText('Finished!'))
44+
const prevButton = screen.getByTestId('cli-prev-button')
45+
expect(prevButton).not.toHaveAttribute('disabled')
46+
})
47+
})
48+
})
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
// Libraries
2+
import React, {PureComponent} from 'react'
3+
import classnames from 'classnames'
4+
5+
// Components
6+
import {
7+
Button,
8+
ComponentColor,
9+
ComponentSize,
10+
ComponentStatus,
11+
Page,
12+
SubwayNav,
13+
} from '@influxdata/clockface'
14+
import {CLIIcon} from 'src/homepageExperience/components/HomepageIcons'
15+
16+
// Steps
17+
import {ExecuteAggregateQuery} from 'src/homepageExperience/components/steps/cli/ExecuteAggregateQuery'
18+
import {ExecuteQuery} from 'src/homepageExperience/components/steps/cli/ExecuteQuery'
19+
import {Finish} from 'src/homepageExperience/components/steps/Finish'
20+
import {InitializeClient} from 'src/homepageExperience/components/steps/cli/InitializeClient'
21+
import {InstallDependencies} from 'src/homepageExperience/components/steps/cli/InstallDependencies'
22+
import {Overview} from 'src/homepageExperience/components/steps/Overview'
23+
import {WriteData} from 'src/homepageExperience/components/steps/cli/WriteData'
24+
import WriteDataDetailsContextProvider from 'src/writeData/components/WriteDataDetailsContext'
25+
26+
// Utils
27+
import {event} from 'src/cloud/utils/reporting'
28+
import {HOMEPAGE_NAVIGATION_STEPS_CLI} from 'src/homepageExperience/utils'
29+
import RateLimitAlert from 'src/cloud/components/RateLimitAlert'
30+
31+
interface State {
32+
currentStep: number
33+
selectedBucket: string
34+
finishStepCompleted: boolean
35+
tokenValue: string
36+
finalFeedback: number
37+
}
38+
39+
export class CliWizard extends PureComponent<{}, State> {
40+
state = {
41+
currentStep: 1,
42+
selectedBucket: 'sample-bucket',
43+
finishStepCompleted: false,
44+
tokenValue: null,
45+
finalFeedback: null,
46+
}
47+
48+
private handleSelectBucket = (bucketName: string) => {
49+
this.setState({selectedBucket: bucketName})
50+
}
51+
52+
private handleMarkStepAsCompleted = () => {
53+
this.setState({finishStepCompleted: true})
54+
}
55+
56+
private setTokenValue = (tokenValue: string) => {
57+
this.setState({tokenValue: tokenValue})
58+
}
59+
60+
private setFinalFeedback = (feedbackValue: number) => {
61+
this.setState({finalFeedback: feedbackValue})
62+
}
63+
64+
handleNextClick = () => {
65+
this.setState(
66+
{
67+
currentStep: Math.min(
68+
this.state.currentStep + 1,
69+
HOMEPAGE_NAVIGATION_STEPS_CLI.length
70+
),
71+
},
72+
() => {
73+
event(
74+
'firstMile.cliWizard.next.clicked',
75+
{},
76+
{
77+
clickedButtonAtStep: this.state.currentStep - 1,
78+
currentStep: this.state.currentStep,
79+
}
80+
)
81+
}
82+
)
83+
}
84+
85+
handlePreviousClick = () => {
86+
this.setState(
87+
{currentStep: Math.max(this.state.currentStep - 1, 1)},
88+
() => {
89+
event(
90+
'firstMile.cliWizard.previous.clicked',
91+
{},
92+
{
93+
clickedButtonAtStep: this.state.currentStep + 1,
94+
currentStep: this.state.currentStep,
95+
}
96+
)
97+
}
98+
)
99+
}
100+
101+
handleNavClick = (clickedStep: number) => {
102+
this.setState({currentStep: clickedStep})
103+
}
104+
105+
renderStep = () => {
106+
switch (this.state.currentStep) {
107+
case 1: {
108+
return <Overview wizard="cliWizard" />
109+
}
110+
case 2: {
111+
return <InstallDependencies />
112+
}
113+
case 3: {
114+
return (
115+
<InitializeClient
116+
wizardEventName="cliWizard"
117+
setTokenValue={this.setTokenValue}
118+
tokenValue={this.state.tokenValue}
119+
onSelectBucket={this.handleSelectBucket}
120+
/>
121+
)
122+
}
123+
case 4: {
124+
return <WriteData bucket={this.state.selectedBucket} />
125+
}
126+
case 5: {
127+
return <ExecuteQuery bucket={this.state.selectedBucket} />
128+
}
129+
case 6: {
130+
return <ExecuteAggregateQuery bucket={this.state.selectedBucket} />
131+
}
132+
case 7: {
133+
return (
134+
<Finish
135+
wizardEventName="cliWizard"
136+
markStepAsCompleted={this.handleMarkStepAsCompleted}
137+
finishStepCompleted={this.state.finishStepCompleted}
138+
finalFeedback={this.state.finalFeedback}
139+
setFinalFeedback={this.setFinalFeedback}
140+
/>
141+
)
142+
}
143+
default: {
144+
return <Overview wizard="cliWizard" />
145+
}
146+
}
147+
}
148+
149+
render() {
150+
return (
151+
<Page>
152+
<Page.Header fullWidth={false}>
153+
{/* Need an empty div so the upgrade button aligns to the right. (Because clockface uses space-between to justifyContent)*/}
154+
<div />
155+
<RateLimitAlert location="firstMile.cliWizard" />
156+
</Page.Header>
157+
<Page.Contents scrollable={true}>
158+
<div className="homepage-wizard-container">
159+
<aside className="homepage-wizard-container--subway">
160+
<div style={{width: '100%'}} data-testid="subway-nav">
161+
<SubwayNav
162+
currentStep={this.state.currentStep}
163+
onStepClick={this.handleNavClick}
164+
navigationSteps={HOMEPAGE_NAVIGATION_STEPS_CLI}
165+
settingUpIcon={CLIIcon}
166+
settingUpText="InfluxCLI"
167+
setupTime="5 minutes"
168+
/>
169+
</div>
170+
</aside>
171+
<div className="homepage-wizard-container--main">
172+
<div
173+
className={classnames(
174+
'homepage-wizard-container--main-wrapper',
175+
{
176+
verticallyCentered:
177+
this.state.currentStep === 1 ||
178+
this.state.currentStep ===
179+
HOMEPAGE_NAVIGATION_STEPS_CLI.length,
180+
}
181+
)}
182+
>
183+
<WriteDataDetailsContextProvider>
184+
{this.renderStep()}
185+
</WriteDataDetailsContextProvider>
186+
</div>
187+
188+
<div className="homepage-wizard-container-footer">
189+
<Button
190+
onClick={this.handlePreviousClick}
191+
text="Previous"
192+
size={ComponentSize.Large}
193+
color={ComponentColor.Tertiary}
194+
status={
195+
this.state.currentStep > 1
196+
? ComponentStatus.Default
197+
: ComponentStatus.Disabled
198+
}
199+
testID="cli-prev-button"
200+
/>
201+
<Button
202+
onClick={this.handleNextClick}
203+
text="Next"
204+
size={ComponentSize.Large}
205+
color={ComponentColor.Primary}
206+
status={
207+
this.state.currentStep < 7
208+
? ComponentStatus.Default
209+
: ComponentStatus.Disabled
210+
}
211+
testID="cli-next-button"
212+
/>
213+
</div>
214+
</div>
215+
</div>
216+
</Page.Contents>
217+
</Page>
218+
)
219+
}
220+
}

src/homepageExperience/utils.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,62 @@ export const HOMEPAGE_NAVIGATION_STEPS = [
66
glyph: IconFont.BookOutline,
77
},
88
{
9-
name: 'Install \n Dependencies',
9+
name: 'Install\nDependencies',
1010
glyph: IconFont.Install,
1111
},
1212
{
1313
name: 'Tokens',
1414
glyph: IconFont.CopperCoin,
1515
},
1616
{
17-
name: 'Initialize \n Client',
17+
name: 'Initialize\nClient',
1818
glyph: IconFont.FileSettings,
1919
},
2020
{
21-
name: 'Write \n Data',
21+
name: 'Write\nData',
2222
glyph: IconFont.Pencil,
2323
},
2424
{
25-
name: 'Execute a \n Simple Query',
25+
name: 'Execute a\nSimple Query',
2626
glyph: IconFont.PlayOutline,
2727
},
2828
{
29-
name: 'Execute an \n Aggregate Query',
29+
name: 'Execute an\nAggregate Query',
3030
glyph: IconFont.PlayOutline,
3131
},
3232
{
3333
name: 'Finish',
3434
glyph: IconFont.StarSmile,
3535
},
3636
]
37+
38+
export const HOMEPAGE_NAVIGATION_STEPS_CLI = [
39+
{
40+
name: 'Overview',
41+
glyph: IconFont.BookOutline,
42+
},
43+
{
44+
name: 'Install\nDependencies',
45+
glyph: IconFont.Install,
46+
},
47+
{
48+
name: 'Initialize\nClient',
49+
glyph: IconFont.FileSettings,
50+
},
51+
{
52+
name: 'Write\nData',
53+
glyph: IconFont.Pencil,
54+
},
55+
{
56+
name: 'Execute\nFlux Query',
57+
glyph: IconFont.PlayOutline,
58+
},
59+
{
60+
name: 'Execute\nAggregate',
61+
glyph: IconFont.PlayOutline,
62+
},
63+
{
64+
name: 'Finished!',
65+
glyph: IconFont.StarSmile,
66+
},
67+
]

0 commit comments

Comments
 (0)