-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ui): Add a reusable GuidedSteps component (#67939)
- Loading branch information
Showing
3 changed files
with
403 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary'; | ||
|
||
import {GuidedSteps} from 'sentry/components/guidedSteps/guidedSteps'; | ||
|
||
describe('GuidedSteps', function () { | ||
it('can navigate through steps and shows previous ones as completed', async function () { | ||
render( | ||
<GuidedSteps> | ||
<GuidedSteps.Step title="Step 1 Title"> | ||
This is the first step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 2 Title"> | ||
This is the second step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 3 Title"> | ||
This is the third step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
</GuidedSteps> | ||
); | ||
|
||
expect(screen.getByText('This is the first step.')).toBeInTheDocument(); | ||
expect(screen.queryByText('This is the second step.')).not.toBeInTheDocument(); | ||
expect(screen.queryByText('This is the third step.')).not.toBeInTheDocument(); | ||
|
||
await userEvent.click(screen.getByText('Next')); | ||
|
||
expect(screen.queryByText('This is the first step.')).not.toBeInTheDocument(); | ||
expect(screen.getByText('This is the second step.')).toBeInTheDocument(); | ||
expect(screen.queryByText('This is the third step.')).not.toBeInTheDocument(); | ||
|
||
// First step is shown as completed | ||
expect( | ||
within(screen.getByTestId('guided-step-1')).getByTestId('icon-check-mark') | ||
).toBeInTheDocument(); | ||
}); | ||
|
||
it('starts at the first incomplete step', function () { | ||
render( | ||
<GuidedSteps> | ||
<GuidedSteps.Step title="Step 1 Title" isCompleted> | ||
This is the first step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 2 Title" isCompleted={false}> | ||
This is the second step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 3 Title" isCompleted={false}> | ||
This is the third step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
</GuidedSteps> | ||
); | ||
|
||
// First step is shown as completed | ||
expect( | ||
within(screen.getByTestId('guided-step-1')).getByTestId('icon-check-mark') | ||
).toBeInTheDocument(); | ||
|
||
// Second step is shown as active | ||
expect(screen.queryByText('This is the first step.')).not.toBeInTheDocument(); | ||
expect(screen.getByText('This is the second step.')).toBeInTheDocument(); | ||
expect(screen.queryByText('This is the third step.')).not.toBeInTheDocument(); | ||
}); | ||
}); |
114 changes: 114 additions & 0 deletions
114
static/app/components/guidedSteps/guidedSteps.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import {Fragment} from 'react'; | ||
|
||
import {Button} from 'sentry/components/button'; | ||
import { | ||
GuidedSteps, | ||
useGuidedStepsContext, | ||
} from 'sentry/components/guidedSteps/guidedSteps'; | ||
import JSXNode from 'sentry/components/stories/jsxNode'; | ||
import SizingWindow from 'sentry/components/stories/sizingWindow'; | ||
import storyBook from 'sentry/stories/storyBook'; | ||
|
||
export default storyBook(GuidedSteps, story => { | ||
story('Default', () => ( | ||
<Fragment> | ||
<p> | ||
To create a GuideStep component, you should use <JSXNode name="GuidedSteps" /> as | ||
the container and <JSXNode name="GuidedSteps.Step" /> as direct children. | ||
</p> | ||
<p> | ||
You have complete control over what to render in the step titles and step content. | ||
You may use <JSXNode name="GuidedSteps.StepButtons" /> to render the default | ||
back/next buttons, but can also render your own. | ||
</p> | ||
<SizingWindow display="block"> | ||
<GuidedSteps> | ||
<GuidedSteps.Step title="Step 1 Title"> | ||
This is the first step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 2 Title"> | ||
This is the second step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 3 Title"> | ||
This is the third step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
</GuidedSteps> | ||
</SizingWindow> | ||
</Fragment> | ||
)); | ||
|
||
story('Custom button behavior', () => { | ||
function SkipToLastButton() { | ||
const {setCurrentStep, totalSteps} = useGuidedStepsContext(); | ||
return ( | ||
<GuidedSteps.ButtonWrapper> | ||
<Button size="sm" onClick={() => setCurrentStep(totalSteps)}> | ||
Skip to Last Step | ||
</Button> | ||
</GuidedSteps.ButtonWrapper> | ||
); | ||
} | ||
|
||
return ( | ||
<Fragment> | ||
<p> | ||
A hook is provided to access and control the step state:{' '} | ||
<code>useGuidedStepsContext()</code>. This can be used to create step buttons | ||
with custom behavior. | ||
</p> | ||
<SizingWindow display="block"> | ||
<GuidedSteps> | ||
<GuidedSteps.Step title="Step 1 Title"> | ||
This is the first step. | ||
<SkipToLastButton /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 2 Title"> | ||
This is the second step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 3 Title"> | ||
This is the third step. | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
</GuidedSteps> | ||
</SizingWindow> | ||
</Fragment> | ||
); | ||
}); | ||
|
||
story('Controlling completed state', () => { | ||
return ( | ||
<Fragment> | ||
<p> | ||
By default, previous steps are considered completed. However, if the completed | ||
state is known it can be controlled using the <code>isCompleted</code> property | ||
on <JSXNode name="GuidedSteps.Step" />. The GuidedStep component will begin on | ||
the first incomplete step. | ||
</p> | ||
<SizingWindow display="block"> | ||
<GuidedSteps> | ||
<GuidedSteps.Step title="Step 1 Title" isCompleted> | ||
Congrats, you finished the first step! | ||
<GuidedSteps.StepButtons /> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 2 Title" isCompleted={false}> | ||
You haven't completed the second step yet, here's how you do it. | ||
<GuidedSteps.ButtonWrapper> | ||
<GuidedSteps.BackButton /> | ||
</GuidedSteps.ButtonWrapper> | ||
</GuidedSteps.Step> | ||
<GuidedSteps.Step title="Step 3 Title" isCompleted={false}> | ||
You haven't completed the third step yet, here's how you do it. | ||
<GuidedSteps.ButtonWrapper> | ||
<GuidedSteps.BackButton /> | ||
</GuidedSteps.ButtonWrapper> | ||
</GuidedSteps.Step> | ||
</GuidedSteps> | ||
</SizingWindow> | ||
</Fragment> | ||
); | ||
}); | ||
}); |
Oops, something went wrong.