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

Production deploy #3317

Merged
merged 7 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions e2e/tests/ui-driven/src/create-flow/create-flow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ test.describe("Navigation", () => {

Promise.all([await page.goto("/"), await initialRequest]);

const team = page.locator("h2", { hasText: context.team.name });
const team = page.locator("h3", { hasText: context.team.name });

let isRepeatedRequestMade = false;
page.on(
Expand All @@ -74,7 +74,7 @@ test.describe("Navigation", () => {
});

await page.goto("/");
const team = page.locator("h2", { hasText: context.team.name });
const team = page.locator("h3", { hasText: context.team.name });
await team.click();

const teamSlugInHeader = page.getByRole("link", {
Expand Down
2 changes: 1 addition & 1 deletion e2e/tests/ui-driven/src/create-flow/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ export async function getTeamPage({
teamName: string;
}): Promise<Page> {
const page = await getAdminPage({ browser, userId });
await page.locator("h2", { hasText: teamName }).click();
await page.locator("h3", { hasText: teamName }).click();
return page;
}
6 changes: 3 additions & 3 deletions e2e/tests/ui-driven/src/login.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ test.describe("Login", () => {
return response.url().includes("/graphql");
});

const team = page.locator("h2", { hasText: context.team.name });
const team = page.locator("h3", { hasText: context.team.name });
await expect(team).toBeVisible();
});

Expand All @@ -50,7 +50,7 @@ test.describe("Login", () => {
});
await page.goto("/");

const teamLink = page.locator("h2").filter({ hasText: context.team.name });
const teamLink = page.locator("h3").filter({ hasText: context.team.name });
await teamLink.waitFor(); // wait for this to be visible

// drop graphql requests
Expand All @@ -67,7 +67,7 @@ test.describe("Login", () => {
route.continue();
});
await expect(
page.locator("h1").filter({ hasText: "My services" }),
page.locator("h1").filter({ hasText: "Services" }),
).toBeVisible();
await expect(page.getByText(toastText)).toBeHidden();
});
Expand Down
13 changes: 8 additions & 5 deletions editor.planx.uk/src/@planx/components/DateInput/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ describe("dateSchema", () => {
expect(await dateSchema().isValid("2021-23-03")).toBe(false);
});

const validate = async (date?: string) => await dateSchema().validate(date).catch((err) => err.errors);
const validate = async (date?: string) =>
await dateSchema()
.validate(date)
.catch((err) => err.errors);

it("throws an error for an undefined value (empty form)", async () => {
const errors = await validate(undefined);
Expand All @@ -58,12 +61,12 @@ describe("dateSchema", () => {
const errors = await validate("ab-cd-efgh");
expect(errors[0]).toMatch(/Date must include a day/);
});

it("throws an error for a missing day", async () => {
const errors = await validate("2024-12-");
expect(errors[0]).toMatch(/Date must include a day/);
});

it("throws an error for a missing month", async () => {
const errors = await validate("2024--25");
expect(errors[0]).toMatch(/Date must include a month/);
Expand Down Expand Up @@ -112,7 +115,7 @@ describe("dateRangeSchema", () => {
"1980-06-15",
),
).toBe(false);
})
});
});

test("padding on input", () => {
Expand Down Expand Up @@ -145,4 +148,4 @@ test("padding on blur", () => {
// Leaves single 0 alone
expect(paddedDate("2021-0-2", "blur")).toBe("2021-0-02");
expect(paddedDate("2021-10-0", "blur")).toBe("2021-10-0");
});
});
98 changes: 43 additions & 55 deletions editor.planx.uk/src/@planx/components/DateInput/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,47 +60,33 @@ const displayDate = (date: string): string | undefined => {
};

export const parseDate = (date?: string) => {
const [year, month, day] = date?.split("-").map((val) => parseInt(val) || undefined) || [];
const [year, month, day] =
date?.split("-").map((val) => parseInt(val) || undefined) || [];
return { year, month, day };
}
};

export const dateSchema = () => {
return string()
.test(
"missing day",
"Date must include a day",
(date?: string) => {
const { day } = parseDate(date);
return day !== undefined;
})
.test(
"missing month",
"Date must include a month",
(date?: string) => {
const { month } = parseDate(date);
return month !== undefined;
})
.test(
"missing year",
"Date must include a year",
(date?: string) => {
const { year } = parseDate(date);
return year !== undefined;
})
.test(
"invalid day",
"Day must be valid",
(date?: string) => {
const { day } = parseDate(date);
return Boolean(day && day <= 31)
})
.test(
"invalid month",
"Month must be valid",
(date?: string) => {
const { month } = parseDate(date);
return Boolean(month && month <= 12);
})
.test("missing day", "Date must include a day", (date?: string) => {
const { day } = parseDate(date);
return day !== undefined;
})
.test("missing month", "Date must include a month", (date?: string) => {
const { month } = parseDate(date);
return month !== undefined;
})
.test("missing year", "Date must include a year", (date?: string) => {
const { year } = parseDate(date);
return year !== undefined;
})
.test("invalid day", "Day must be valid", (date?: string) => {
const { day } = parseDate(date);
return Boolean(day && day <= 31);
})
.test("invalid month", "Month must be valid", (date?: string) => {
const { month } = parseDate(date);
return Boolean(month && month <= 12);
})
.test(
"valid",
"Enter a valid date in DD.MM.YYYY format",
Expand All @@ -115,24 +101,26 @@ export const dateRangeSchema: (params: {
min?: string;
max?: string;
}) => SchemaOf<string> = (params) =>
dateSchema()
.required("Enter a valid date in DD.MM.YYYY format")
.test({
name: "too soon",
message: `Enter a date later than ${params.min && displayDate(params.min)
}`,
test: (date: string | undefined) => {
return Boolean(date && !(params.min && date < params.min));
},
})
.test({
name: "too late",
message: `Enter a date earlier than ${params.max && displayDate(params.max)
}`,
test: (date: string | undefined) => {
return Boolean(date && !(params.max && date > params.max));
},
});
dateSchema()
.required("Enter a valid date in DD.MM.YYYY format")
.test({
name: "too soon",
message: `Enter a date later than ${
params.min && displayDate(params.min)
}`,
test: (date: string | undefined) => {
return Boolean(date && !(params.min && date < params.min));
},
})
.test({
name: "too late",
message: `Enter a date earlier than ${
params.max && displayDate(params.max)
}`,
test: (date: string | undefined) => {
return Boolean(date && !(params.max && date > params.max));
},
});

export const parseDateInput = (
data: Record<string, any> | undefined,
Expand Down
2 changes: 1 addition & 1 deletion editor.planx.uk/src/@planx/components/List/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import InputRowLabel from "ui/shared/InputRowLabel";
import { EditorProps, ICONS, InternalNotes, MoreInformation } from "../ui";
import { List, parseContent } from "./model";
import { ProposedAdvertisements } from "./schemas/Adverts";
import { NonResidentialFloorspace } from "./schemas/Floorspace";
import { BuildingDetailsGLA } from "./schemas/GLA/BuildingDetails";
import { CommunalSpaceGLA } from "./schemas/GLA/CommunalSpace";
import { ExistingAndProposedUsesGLA } from "./schemas/GLA/ExistingAndProposedUses";
Expand All @@ -24,7 +25,6 @@ import { ResidentialUnitsExisting } from "./schemas/ResidentialUnits/Existing";
import { ResidentialUnitsGLAGained } from "./schemas/ResidentialUnits/GLA/Gained";
import { ResidentialUnitsGLALost } from "./schemas/ResidentialUnits/GLA/Lost";
import { ResidentialUnitsProposed } from "./schemas/ResidentialUnits/Proposed";
import { NonResidentialFloorspace } from "./schemas/Floorspace";

type Props = EditorProps<TYPES.List, List>;

Expand Down
43 changes: 36 additions & 7 deletions editor.planx.uk/src/@planx/components/List/Public/Context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
List,
Schema,
UserData,
UserResponse,
} from "../model";
import {
flatten,
Expand All @@ -35,6 +36,14 @@ interface ListContextValue {
formik: FormikProps<UserData>;
validateAndSubmitForm: () => void;
listProps: PublicProps<List>;
/**
* @deprecated
* @description
* Hide features if the schema is temporarily mocking a "Page" component
* @todo
* Refactor and allow a single-item "Page" component to properly manage this
*/
isPageComponent: boolean;
errors: {
addItem: boolean;
unsavedItem: boolean;
Expand All @@ -54,6 +63,10 @@ export const ListProvider: React.FC<ListProviderProps> = (props) => {
props.previouslySubmittedData ? -1 : 0,
);

const [activeItemInitialState, setActiveItemInitialState] = useState<
UserResponse | undefined
>(undefined);

const [addItemError, setAddItemError] = useState<boolean>(false);
const [unsavedItemError, setUnsavedItemError] = useState<boolean>(false);
const [minError, setMinError] = useState<boolean>(false);
Expand Down Expand Up @@ -83,20 +96,17 @@ export const ListProvider: React.FC<ListProviderProps> = (props) => {
const errors = await formik.validateForm();
const isValid = !errors.userData?.length;
if (isValid) {
setActiveIndex(-1);
exitEditMode();
setAddItemError(false);
}
};

const removeItem = (index: number) => {
resetErrors();

// If item is before currently active card, retain active card
if (activeIndex && index < activeIndex) {
// If item is before currently active card, retain active card
setActiveIndex((prev) => (prev === -1 ? 0 : prev - 1));
} else if (index === activeIndex || index === 0) {
// If item is currently in Edit mode, exit Edit mode
cancelEditItem();
}

// Remove item from userData
Expand All @@ -120,9 +130,20 @@ export const ListProvider: React.FC<ListProviderProps> = (props) => {
formik.handleSubmit();
};

const cancelEditItem = () => setActiveIndex(-1);
const cancelEditItem = () => {
activeItemInitialState
? resetItemToPreviousState()
: removeItem(activeIndex);

setActiveItemInitialState(undefined);

exitEditMode();
};

const editItem = (index: number) => setActiveIndex(index);
const editItem = (index: number) => {
setActiveItemInitialState(formik.values.userData[index]);
setActiveIndex(index);
};

const getInitialValues = () => {
const previousValues = getPreviouslySubmittedData(props);
Expand All @@ -131,6 +152,13 @@ export const ListProvider: React.FC<ListProviderProps> = (props) => {
return schema.min ? [generateInitialValues(schema)] : [];
};

const exitEditMode = () => setActiveIndex(-1);

const resetItemToPreviousState = () =>
formik.setFieldValue(`userData[${activeIndex}]`, activeItemInitialState);

const isPageComponent = schema.max === 1;

const formik = useFormik<UserData>({
initialValues: {
userData: getInitialValues(),
Expand Down Expand Up @@ -189,6 +217,7 @@ export const ListProvider: React.FC<ListProviderProps> = (props) => {
cancelEditItem,
formik,
validateAndSubmitForm,
isPageComponent,
errors: {
addItem: addItemError,
unsavedItem: unsavedItemError,
Expand Down
37 changes: 37 additions & 0 deletions editor.planx.uk/src/@planx/components/List/Public/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,43 @@ describe("Building a list", () => {
within(cards[0]!).getByLabelText(/What's their name?/),
).toBeInTheDocument();
});

test("Cancelling an invalid (new) item removes it", async () => {
const { getAllByTestId, getByText, user, queryAllByTestId } = setup(
<ListComponent {...mockZooProps} />,
);

let cards = getAllByTestId(/list-card/);
expect(cards).toHaveLength(1);

const cancelButton = getByText(/Cancel/, { selector: "button" });
await user.click(cancelButton);

cards = queryAllByTestId(/list-card/);
expect(cards).toHaveLength(0);
});

test("Cancelling a valid (existing) item resets previous state", async () => {
const { getByLabelText, getByText, user, queryByText } = setup(
<ListComponent {...mockZooProps} />,
);

await fillInResponse(user);

expect(getByText("[email protected]")).toBeInTheDocument();

const editButton = getByText(/Edit/, { selector: "button" });
await user.click(editButton);

const emailInput = getByLabelText(/email/);
await user.type(emailInput, "[email protected]");

const cancelButton = getByText(/Cancel/, { selector: "button" });
await user.click(cancelButton);

expect(queryByText("[email protected]")).not.toBeInTheDocument();
expect(getByText("[email protected]")).toBeInTheDocument();
});
});

describe("Form validation and error handling", () => {
Expand Down
Loading
Loading