Skip to content

Commit

Permalink
feat: [M3-7311] - Create Load Balancer flow - manage state (#9848)
Browse files Browse the repository at this point in the history
* feat: [M3-7311] - Create Load Balancer flow - Mange state.

* Code cleanup

* Added changeset: Create Load Balancer flow - Mange state.

* Update pr-9848-upcoming-features-1698675578462.md

* Update update-database.spec.ts

* Update packages/manager/.changeset/pr-9848-upcoming-features-1698675578462.md

Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>

* Fix typos

* use useFormikContext instead of custom state management hook

* PR feedback

* Update loadbalancers.schema.ts

* Update packages/validation/src/loadbalancers.schema.ts

Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>

* Update packages/validation/src/loadbalancers.schema.ts

Co-authored-by: jdamore-linode <97627410+jdamore-linode@users.noreply.github.com>

* Update loadbalancers.schema.ts

* Update CreateLoadBalancerSchema and Endpoint Schema

* Revert "Update CreateLoadBalancerSchema and Endpoint Schema"

This reverts commit 106f4d1.

* Code cleanup and update Endpoint schema.

* Update the field names

* Added changeset: Create Load Balancer flow - manage state

---------

Co-authored-by: Mariah Jacobs <114685994+mjac0bs@users.noreply.github.com>
Co-authored-by: jdamore-linode <97627410+jdamore-linode@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 2, 2023
1 parent ec48b0e commit 3ea7090
Show file tree
Hide file tree
Showing 11 changed files with 239 additions and 82 deletions.
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))
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: '',
}}
/>
<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
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/validation": Upcoming Features
---

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

0 comments on commit 3ea7090

Please sign in to comment.