-
Notifications
You must be signed in to change notification settings - Fork 361
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: [M3-7058] - Add AGLB Create Page - Stepper component (#9520)
* feat: stepper component POC * VerticalLinearStepper unit tests * Stepper component story * Code cleanup * Added changeset: AGLB - Stepper component * Update packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts Co-authored-by: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> * Update packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts Co-authored-by: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> * Update packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts Co-authored-by: Connie Liu <139280159+coliu-akamai@users.noreply.github.com> * Fix console warnings in storybook * Update VerticalLinearStepper.test.tsx * Update unit tests * Update VerticalLinearStepper.test.tsx * Update pr-9520-upcoming-features-1692378386106.md --------- Co-authored-by: Connie Liu <139280159+coliu-akamai@users.noreply.github.com>
- Loading branch information
1 parent
8e07891
commit ddb5d4b
Showing
5 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
packages/manager/.changeset/pr-9520-upcoming-features-1692378386106.md
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,5 @@ | ||
--- | ||
"@linode/manager": Upcoming Features | ||
--- | ||
|
||
Add AGLB Create Page - Stepper component ([#9520](https://github.com/linode/manager/pull/9520)) |
54 changes: 54 additions & 0 deletions
54
packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.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,54 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { Typography } from '../Typography'; | ||
import { VerticalLinearStepper } from './VerticalLinearStepper'; | ||
|
||
const meta: Meta<typeof VerticalLinearStepper> = { | ||
component: VerticalLinearStepper, | ||
title: 'Components/VerticalLinearStepper', | ||
}; | ||
|
||
type Story = StoryObj<typeof VerticalLinearStepper>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
steps: [ | ||
{ | ||
content: ( | ||
<Typography> | ||
This is some test copy which acts as content for this Stepper | ||
component step 1.{' '} | ||
</Typography> | ||
), | ||
label: 'Step1', | ||
}, | ||
{ | ||
content: ( | ||
<Typography> | ||
This is some test copy which acts as content for this Stepper | ||
component step 2.{' '} | ||
</Typography> | ||
), | ||
label: 'Step2', | ||
}, | ||
{ | ||
content: ( | ||
<Typography> | ||
This is some test copy which acts as content for this Stepper | ||
component step 3.{' '} | ||
</Typography> | ||
), | ||
label: 'Step3', | ||
}, | ||
], | ||
}, | ||
render: (args) => { | ||
const VerticalLinearStepperExample = () => { | ||
return <VerticalLinearStepper {...args} />; | ||
}; | ||
return <VerticalLinearStepperExample />; | ||
}, | ||
}; | ||
|
||
export default meta; |
53 changes: 53 additions & 0 deletions
53
packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.styles.ts
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,53 @@ | ||
import { StepConnector, StepIcon } from '@mui/material'; | ||
import { styled } from '@mui/material/styles'; | ||
|
||
import { isPropValid } from 'src/utilities/isPropValid'; | ||
|
||
type StyledCircleIconProps = { | ||
activeStep: number; | ||
index: number; | ||
}; | ||
|
||
export const StyledCircleIcon = styled('div', { | ||
label: 'StyledCircleIcon', | ||
shouldForwardProp: (prop) => isPropValid(['activeStep', 'index'], prop), | ||
})<StyledCircleIconProps>(({ theme, ...props }) => ({ | ||
alignItems: 'center', | ||
backgroundColor: | ||
props.index === props.activeStep | ||
? theme.palette.primary.main | ||
: props.index < props.activeStep | ||
? '#ADD8E6' // TODO: need UX confirmation on color code | ||
: theme.bg.bgPaper, // Adjust colors as needed | ||
border: | ||
props.index < props.activeStep || props.index === props.activeStep | ||
? `2px solid ${theme.palette.primary.main}` | ||
: `2px solid ${theme.borderColors.borderTable}`, // Adjust border styles as needed | ||
borderRadius: '50%', | ||
display: 'flex', | ||
height: theme.spacing(3), | ||
justifyContent: 'center', | ||
width: theme.spacing(3), | ||
})); | ||
|
||
export const CustomStepIcon = styled(StepIcon, { label: 'StyledCircleIcon' })( | ||
() => ({ | ||
active: {}, | ||
completed: {}, | ||
root: { | ||
'&$completed': { | ||
display: 'none', // Hide the checkmark icon on completed steps | ||
}, | ||
}, | ||
}) | ||
); | ||
|
||
export const StyledColorlibConnector = styled(StepConnector, { | ||
label: 'StyledColorlibConnector', | ||
})(() => ({ | ||
'& .MuiStepConnector-line': { | ||
borderColor: '#eaeaf0', | ||
borderLeftWidth: '3px', | ||
minHeight: '28px', | ||
}, | ||
})); |
65 changes: 65 additions & 0 deletions
65
packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.test.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,65 @@ | ||
import '@testing-library/jest-dom/extend-expect'; | ||
import { screen } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
import React from 'react'; | ||
|
||
import { renderWithTheme } from 'src/utilities/testHelpers'; | ||
|
||
import { VerticalLinearStepper } from './VerticalLinearStepper'; | ||
|
||
const steps = [ | ||
{ | ||
content: <div>Step 1 Content</div>, | ||
handler: jest.fn(), // Mock function for testing | ||
label: 'Details', | ||
}, | ||
{ | ||
content: <div>Step 2 Content</div>, | ||
handler: jest.fn(), | ||
label: 'Summary', | ||
}, | ||
{ | ||
content: <div>Step 3 Content</div>, | ||
handler: jest.fn(), | ||
label: 'Next', | ||
}, | ||
]; | ||
|
||
describe('VerticalLinearStepper', () => { | ||
test('Should render initial step content', () => { | ||
renderWithTheme(<VerticalLinearStepper steps={steps} />); | ||
expect(screen.getByText('Step 1 Content')).toBeInTheDocument(); | ||
expect(screen.queryByText('Step 2 Content')).toBeNull(); | ||
expect(screen.queryByText('Step 3 Content')).toBeNull(); | ||
expect(screen.getByText('Next: Summary')).toBeInTheDocument(); | ||
expect(screen.queryByText('Previous: Details')).toBeNull(); | ||
}); | ||
test('Should navigate to second step conent', () => { | ||
renderWithTheme(<VerticalLinearStepper steps={steps} />); | ||
userEvent.click(screen.getByTestId('summary')); | ||
expect(steps[0].handler).toHaveBeenCalledTimes(1); | ||
expect(screen.getByText('Step 2 Content')).toBeInTheDocument(); | ||
expect(screen.queryByText('Step 1 Content')).toBeNull(); | ||
expect(screen.queryByText('Step 3 Content')).toBeNull(); | ||
expect(screen.getByText('Next: Next')).toBeInTheDocument(); | ||
expect(screen.queryByText('Previous: Summary')).toBeNull(); | ||
}); | ||
test('Should navigate to final step conent', () => { | ||
renderWithTheme(<VerticalLinearStepper steps={steps} />); | ||
userEvent.click(screen.getByTestId('summary')); | ||
userEvent.click(screen.getByTestId('next')); | ||
expect(steps[1].handler).toHaveBeenCalledTimes(1); | ||
expect(screen.queryByText('Step 1 Content')).toBeNull(); | ||
expect(screen.queryByText('Step 2 Content')).toBeNull(); | ||
expect(screen.getByText('Step 3 Content')).toBeInTheDocument(); | ||
expect(screen.queryByText('Next: Summary')).toBeNull(); | ||
expect(screen.getByText('Previous: Summary')).toBeInTheDocument(); | ||
}); | ||
test('Should be able to go previous step', () => { | ||
renderWithTheme(<VerticalLinearStepper steps={steps} />); | ||
userEvent.click(screen.getByTestId('summary')); | ||
userEvent.click(screen.getByText('Previous: Details')); | ||
expect(steps[1].handler).toHaveBeenCalledTimes(1); | ||
expect(screen.getByText('Step 1 Content')).toBeInTheDocument(); | ||
}); | ||
}); |
152 changes: 152 additions & 0 deletions
152
packages/manager/src/components/VerticalLinearStepper/VerticalLinearStepper.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,152 @@ | ||
import { | ||
Step, | ||
StepConnector, | ||
StepContent, | ||
StepLabel, | ||
Stepper, | ||
} from '@mui/material'; | ||
import Box from '@mui/material/Box'; | ||
import { Theme } from '@mui/material/styles'; | ||
import React, { useState } from 'react'; | ||
|
||
import { ActionsPanel } from 'src/components/ActionsPanel/ActionsPanel'; | ||
|
||
import { | ||
CustomStepIcon, | ||
StyledCircleIcon, | ||
StyledColorlibConnector, | ||
} from './VerticalLinearStepper.styles'; | ||
|
||
type VerticalLinearStep = { | ||
content: JSX.Element; | ||
handler?: () => void; | ||
label: string; | ||
}; | ||
|
||
interface VerticalLinearStepperProps { | ||
steps: VerticalLinearStep[]; | ||
} | ||
|
||
export const VerticalLinearStepper = ({ | ||
steps, | ||
}: VerticalLinearStepperProps) => { | ||
const [activeStep, setActiveStep] = useState(0); | ||
|
||
const handleNext = () => { | ||
setActiveStep((prevActiveStep) => prevActiveStep + 1); | ||
}; | ||
|
||
const handleBack = () => { | ||
setActiveStep((prevActiveStep) => prevActiveStep - 1); | ||
}; | ||
|
||
return ( | ||
<Box | ||
sx={(theme: Theme) => ({ | ||
backgroundColor: theme.bg.bgPaper, | ||
display: 'flex', | ||
margin: 'auto', | ||
maxWidth: 800, | ||
p: `${theme.spacing(2)}`, | ||
})} | ||
> | ||
{/* Left Column - Vertical Steps */} | ||
<Box> | ||
<Stepper | ||
activeStep={activeStep} | ||
connector={<StyledColorlibConnector />} | ||
orientation="vertical" | ||
> | ||
{steps.map((step: VerticalLinearStep, index: number) => ( | ||
<Step key={step.label}> | ||
<StepLabel | ||
icon={ | ||
<CustomStepIcon | ||
icon={ | ||
<StyledCircleIcon activeStep={activeStep} index={index} /> | ||
} | ||
/> | ||
} | ||
sx={{ | ||
'& .MuiStepIcon-text': { | ||
display: 'none', | ||
}, | ||
p: 0, | ||
}} | ||
> | ||
{step.label} | ||
</StepLabel> | ||
</Step> | ||
))} | ||
</Stepper> | ||
</Box> | ||
{/* Right Column - Stepper Content */} | ||
<Box sx={{ flex: 2 }}> | ||
<Stepper | ||
connector={ | ||
<StepConnector | ||
sx={{ | ||
'& .MuiStepConnector-line': { | ||
display: 'none', | ||
}, | ||
'& .MuiStepConnector-vertical': { | ||
display: 'none', | ||
}, | ||
}} | ||
/> | ||
} | ||
activeStep={activeStep} | ||
orientation="vertical" | ||
> | ||
{steps.map(({ content, handler, label }, index) => ( | ||
<Step key={label}> | ||
{index === activeStep ? ( | ||
<StepContent sx={{ border: 'none' }}> | ||
<Box | ||
sx={(theme) => ({ | ||
bgcolor: theme.bg.app, | ||
p: theme.spacing(2), | ||
})} | ||
> | ||
{content} | ||
</Box> | ||
|
||
<Box sx={{ mb: 2 }}> | ||
<ActionsPanel | ||
primaryButtonProps={ | ||
index !== 2 | ||
? { | ||
'data-testid': steps[ | ||
index + 1 | ||
]?.label.toLocaleLowerCase(), | ||
label: `Next: ${steps[index + 1]?.label}`, | ||
onClick: () => { | ||
handleNext(); | ||
handler?.(); | ||
}, | ||
sx: { mr: 1, mt: 1 }, | ||
} | ||
: undefined | ||
} | ||
secondaryButtonProps={ | ||
index !== 0 | ||
? { | ||
buttonType: 'outlined', | ||
label: `Previous: ${steps[index - 1]?.label}`, | ||
onClick: handleBack, | ||
sx: { mr: 1, mt: 1 }, | ||
} | ||
: undefined | ||
} | ||
style={{ justifyContent: 'flex-start' }} | ||
/> | ||
</Box> | ||
</StepContent> | ||
) : null} | ||
</Step> | ||
))} | ||
</Stepper> | ||
</Box> | ||
</Box> | ||
); | ||
}; |