Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [M3-7311] - Create Load Balancer flow - manage state #9848

Merged
merged 21 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f7bcbb9
feat: [M3-7311] - Create Load Balancer flow - Mange state.
cpathipa Oct 30, 2023
3f38d91
Code cleanup
cpathipa Oct 30, 2023
775bb86
Added changeset: Create Load Balancer flow - Mange state.
cpathipa Oct 30, 2023
af5fd8a
Update pr-9848-upcoming-features-1698675578462.md
cpathipa Oct 30, 2023
69488c2
Merge remote-tracking branch 'origin/develop' into M3-7311
cpathipa Oct 30, 2023
266d259
Update update-database.spec.ts
cpathipa Oct 30, 2023
44027c9
Update packages/manager/.changeset/pr-9848-upcoming-features-16986755…
cpathipa Oct 30, 2023
e0ea1ba
Fix typos
cpathipa Oct 30, 2023
28df367
use useFormikContext instead of custom state management hook
cpathipa Nov 1, 2023
59b3534
PR feedback
cpathipa Nov 1, 2023
994a104
Merge remote-tracking branch 'origin/develop' into M3-7311
cpathipa Nov 1, 2023
f92709a
Update loadbalancers.schema.ts
cpathipa Nov 1, 2023
2d1a7e0
Update packages/validation/src/loadbalancers.schema.ts
cpathipa Nov 1, 2023
2791bc3
Update packages/validation/src/loadbalancers.schema.ts
cpathipa Nov 1, 2023
a4dcb76
Update loadbalancers.schema.ts
cpathipa Nov 1, 2023
5a547bf
Merge branch 'M3-7311' of github.com:cpathipa/manager into M3-7311
cpathipa Nov 1, 2023
106f4d1
Update CreateLoadBalancerSchema and Endpoint Schema
cpathipa Nov 2, 2023
e5109b5
Revert "Update CreateLoadBalancerSchema and Endpoint Schema"
cpathipa Nov 2, 2023
69c3038
Code cleanup and update Endpoint schema.
cpathipa Nov 2, 2023
1468860
Update the field names
cpathipa Nov 2, 2023
d08dfb9
Added changeset: Create Load Balancer flow - manage state
cpathipa Nov 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Create Load Balancer flow - manage state ([#9848](https://github.com/linode/manager/pull/9848))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated linter change that snuck in.

Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,12 @@ describe('Update database clusters', () => {

cy.get('[data-qa-cluster-config]').within(() => {
cy.findByText(configuration.region.label).should('be.visible');
cy.findByText(database.used_disk_size_gb + " GB").should('be.visible');
cy.findByText(database.total_disk_size_gb + " GB").should('be.visible');
cy.findByText(database.used_disk_size_gb + ' GB').should(
'be.visible'
);
cy.findByText(database.total_disk_size_gb + ' GB').should(
'be.visible'
);
});

cy.get('[data-qa-connection-details]').within(() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useFormikContext } from 'formik';
import * as React from 'react';

import { Box } from 'src/components/Box';
import { Button } from 'src/components/Button/Button';

export const LoadBalancerActionPanel = () => {
const { submitForm } = useFormikContext();
return (
<Box
columnGap={1}
display="flex"
flexWrap="wrap"
justifyContent="space-between"
rowGap={3}
>
<Button buttonType="outlined">Add Another Configuration</Button>
<Button
buttonType="primary"
onClick={submitForm}
sx={{ marginLeft: 'auto' }}
>
Review Load Balancer
</Button>
</Box>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('LoadBalancerConfiguration', () => {
)
).toBeNull();
expect(
screen.queryByText('TODO: AGLB - Implement Routes Confiugataion.')
screen.queryByText('TODO: AGLB - Implement Routes Configuration.')
).toBeNull();
expect(screen.getByText('Next: Service Targets')).toBeInTheDocument();
expect(screen.queryByText('Previous: Details')).toBeNull();
Expand All @@ -33,7 +33,7 @@ describe('LoadBalancerConfiguration', () => {
screen.queryByText('TODO: AGLB - Implement Details step content.')
).toBeNull();
expect(
screen.queryByText('TODO: AGLB - Implement Routes Confiugataion.')
screen.queryByText('TODO: AGLB - Implement Routes Configuration.')
).toBeNull();
expect(screen.getByText('Next: Routes')).toBeInTheDocument();
expect(screen.getByText('Previous: Details')).toBeInTheDocument();
Expand All @@ -52,7 +52,7 @@ describe('LoadBalancerConfiguration', () => {
)
).toBeNull();
expect(
screen.getByText('TODO: AGLB - Implement Routes Confiugataion.')
screen.getByText('TODO: AGLB - Implement Routes Configuration.')
).toBeInTheDocument();
expect(screen.getByText('Previous: Service Targets')).toBeInTheDocument();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const configurationSteps = [
label: 'Service Targets',
},
{
content: <div>TODO: AGLB - Implement Routes Confiugataion.</div>,
content: <div>TODO: AGLB - Implement Routes Configuration.</div>,
handler: () => null,
label: 'Routes',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { createLoadBalancerSchema } from '@linode/validation';
import Stack from '@mui/material/Stack';
import { Form, Formik } from 'formik';
import * as React from 'react';

import { Box } from 'src/components/Box';
import { Button } from 'src/components/Button/Button';
import { DocumentTitleSegment } from 'src/components/DocumentTitle/DocumentTitle';
import { LandingHeader } from 'src/components/LandingHeader';

import { LoadBalancerActionPanel } from './LoadBalancerActionPanel';
import { LoadBalancerConfiguration } from './LoadBalancerConfiguration';
import { LoadBalancerLabel } from './LoadBalancerLabel';
import { LoadBalancerRegions } from './LoadBalancerRegions';

import type { CreateLoadbalancerPayload } from '@linode/api-v4';

const initialValues = {
label: '',
regions: [],
};

const LoadBalancerCreate = () => {
return (
<>
Expand All @@ -26,35 +34,23 @@ const LoadBalancerCreate = () => {
}}
title="Create"
/>
<Stack spacing={3}>
<LoadBalancerLabel
labelFieldProps={{
disabled: false,
errorText: '',
label: 'Linode Label',
onChange: () => null,
value: '',
}}
Comment on lines -30 to -37
Copy link
Contributor Author

@cpathipa cpathipa Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prop drilling is not required since we use a custom state management hook useLoadBalancerState useFormikContext.

/>
<LoadBalancerRegions />
<LoadBalancerConfiguration />
{/* TODO: AGLB -
* Implement Review Load Balancer Action Behavior
* Implement Add Another Configuration Behavior
*/}
<Box
columnGap={1}
display="flex"
flexWrap="wrap"
justifyContent="space-between"
rowGap={3}
>
<Button buttonType="outlined">Add Another Configuration</Button>
<Button buttonType="primary" sx={{ marginLeft: 'auto' }}>
Review Load Balancer
</Button>
</Box>
</Stack>
<Formik<CreateLoadbalancerPayload>
onSubmit={(values, actions) => {
// TODO: AGLB - Implement form submit
// console.log('Values ', values);
}}
initialValues={initialValues}
validationSchema={createLoadBalancerSchema}
>
<Form>
<Stack spacing={3}>
<LoadBalancerLabel />
<LoadBalancerRegions />
<LoadBalancerConfiguration />
<LoadBalancerActionPanel />
</Stack>
</Form>
</Formik>
</>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,74 @@
import { fireEvent } from '@testing-library/react';
import { Formik } from 'formik';
import React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';

import { LoadBalancerLabel } from './LoadBalancerLabel';

const loadBalancerLabelValue = 'Test Label';
const loadBalancerTestId = 'textfield-input';

import type { CreateLoadbalancerPayload } from '@linode/api-v4';

type MockFormikContext = {
initialErrors?: {};
initialTouched?: {};
initialValues: CreateLoadbalancerPayload;
};

const initialValues = {
label: loadBalancerLabelValue,
regions: [],
};

const renderWithFormikWrapper = (mockFormikContext: MockFormikContext) =>
renderWithTheme(
<Formik {...mockFormikContext} onSubmit={jest.fn()}>
<LoadBalancerLabel />
</Formik>
);

describe('LoadBalancerLabel', () => {
it('should render the component with a label and no error', () => {
const labelFieldProps = {
disabled: false,
errorText: '',
label: 'Load Balancer Label',
onChange: jest.fn(),
value: 'Test Label',
};

const { getByTestId, queryByText } = renderWithTheme(
<LoadBalancerLabel error="" labelFieldProps={labelFieldProps} />
);

const labelInput = getByTestId('textfield-input');
const { getByTestId, queryByText } = renderWithFormikWrapper({
initialValues,
});

const labelInput = getByTestId(loadBalancerTestId);
const errorNotice = queryByText('Error Text');

expect(labelInput).toBeInTheDocument();
expect(labelInput).toHaveAttribute('placeholder', 'Enter a label');
expect(labelInput).toHaveValue('Test Label');
expect(labelInput).toHaveValue(loadBalancerLabelValue);
expect(errorNotice).toBeNull();
});

it('should render the component with an error message', () => {
const labelFieldProps = {
disabled: false,
errorText: 'This is an error',
label: 'Load Balancer Label',
onChange: jest.fn(),
value: 'Test Label',
};

const { getByTestId, getByText } = renderWithTheme(
<LoadBalancerLabel error="Error Text" labelFieldProps={labelFieldProps} />
);

const labelInput = getByTestId('textfield-input');
const { getByTestId, getByText } = renderWithFormikWrapper({
initialErrors: { label: 'This is an error' },
initialTouched: { label: true },
initialValues,
});

const labelInput = getByTestId(loadBalancerTestId);
const errorNotice = getByText('This is an error');

expect(labelInput).toBeInTheDocument();
expect(errorNotice).toBeInTheDocument();
});

it('should update formik values on input change', () => {
const { getByTestId } = renderWithFormikWrapper({
initialValues,
});

const labelInput = getByTestId(loadBalancerTestId);

// Simulate typing 'New Label' in the input field
fireEvent.change(labelInput, { target: { value: 'New Label' } });

// Expect the input to have the new value
expect(labelInput).toHaveValue('New Label');
});
});
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import { useFormikContext } from 'formik';
import * as React from 'react';

import { Notice } from 'src/components/Notice/Notice';
import { Paper } from 'src/components/Paper';
import { TextField, TextFieldProps } from 'src/components/TextField';
import { TextField } from 'src/components/TextField';

interface LabelProps {
error?: string;
labelFieldProps: TextFieldProps;
}
import type { CreateLoadbalancerPayload } from '@linode/api-v4';

export const LoadBalancerLabel = (props: LabelProps) => {
const { error, labelFieldProps } = props;
export const LoadBalancerLabel = () => {
const {
errors,
handleChange,
touched,
values,
} = useFormikContext<CreateLoadbalancerPayload>();

return (
<Paper
Expand All @@ -20,16 +22,16 @@ export const LoadBalancerLabel = (props: LabelProps) => {
}}
data-qa-label-header
>
{error && <Notice text={error} variant="error" />}
<TextField
data-qa-label-input
disabled={labelFieldProps.disabled}
errorText={labelFieldProps.errorText}
disabled={false}
errorText={touched.label && errors.label ? errors.label : undefined} // Display errors if the field is touched and there's an error
label="Load Balancer Label"
name="label"
noMarginTop
onChange={() => labelFieldProps.onChange}
onChange={handleChange}
placeholder="Enter a label"
value={labelFieldProps.value}
value={values?.label}
/>
</Paper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Flag } from 'src/components/Flag';
import { Paper } from 'src/components/Paper';
import { Typography } from 'src/components/Typography';

const regions = [
const loadBalancerRegions = [
{ country: 'us', id: 'us-iad', label: 'Washington, DC' },
{ country: 'us', id: 'us-lax', label: 'Los Angeles, CA' },
{ country: 'fr', id: 'fr-par', label: 'Paris, FR' },
Expand All @@ -31,7 +31,7 @@ export const LoadBalancerRegions = () => {
</Typography>
</Stack>
<Stack py={0.5} spacing={1.25}>
{regions.map((region) => (
{loadBalancerRegions.map((region) => (
<Stack
alignItems="center"
direction="row"
Expand Down
Loading