Skip to content

Commit a096966

Browse files
authored
Merge branch 'main' into adjust-max-repeating-sets
2 parents 31f692f + 31e8263 commit a096966

File tree

33 files changed

+330
-143
lines changed

33 files changed

+330
-143
lines changed

app/(gcforms)/[locale]/(app administration)/admin/(with nav)/accounts/[id]/manage-user-features/components/client/AddUserFeatureModal.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useTranslation } from "@i18n/client";
44
import { useState } from "react";
55
import { AppUser } from "@lib/types/user-types";
66
import { setUserFlags } from "../../actions";
7-
7+
import { useSession } from "next-auth/react";
88
import { UserFeatureFlags, UserFeatureFlagKeys } from "@lib/cache/types";
99

1010
export const AddUserFeatureModal = ({
@@ -19,6 +19,7 @@ export const AddUserFeatureModal = ({
1919
const { t } = useTranslation("admin-flags");
2020
const [showModal, setShowModal] = useState(false);
2121
const [selectedFlags, setSelectedFlags] = useState<string[]>([]);
22+
const { update: updateSession } = useSession();
2223

2324
// Filter the list of flags to only include user level feature flags
2425
const availableFeatureFlags = flags.filter((flag) =>
@@ -31,10 +32,11 @@ export const AddUserFeatureModal = ({
3132
);
3233
};
3334

34-
const handleAdd = () => {
35-
setUserFlags(formUser.id, selectedFlags);
35+
const handleAdd = async () => {
36+
await setUserFlags(formUser.id, selectedFlags);
3637
setShowModal(false);
3738
setSelectedFlags([]);
39+
await updateSession();
3840
};
3941

4042
const handleCancel = () => {

app/(gcforms)/[locale]/(app administration)/admin/(with nav)/accounts/[id]/manage-user-features/components/client/RemoveFeatureButton.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import { Button } from "@clientComponents/globals";
33
import { AppUser } from "@lib/types/user-types";
44
import { useTranslation } from "@i18n/client";
55
import { removeUserFlag } from "../../actions";
6+
import { useSession } from "next-auth/react";
67

78
export const RemoveFeatureButton = ({ formUser, flag }: { formUser: AppUser; flag: string }) => {
89
const { t } = useTranslation("admin-flags");
10+
const { update: updateSession } = useSession();
911

1012
const handleRemove = async () => {
1113
await removeUserFlag(formUser.id, flag);
14+
await updateSession();
1215
};
1316

1417
return (

app/(gcforms)/[locale]/(form administration)/form-builder/Start.tsx

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { validateTemplateSize } from "@lib/utils/validateTemplateSize";
1313
import { ga } from "@lib/client/clientHelpers";
1414
import { validateUniqueQuestionIds } from "@lib/utils/validateUniqueQuestionIds";
1515
import { transformFormProperties } from "@lib/store/helpers/elements/transformFormProperties";
16+
import { BetaComponentsError, checkForBetaComponents } from "@lib/validation/betaCheck";
17+
import { useFeatureFlags } from "@lib/hooks/useFeatureFlags";
1618

1719
export const Start = () => {
1820
const {
@@ -26,6 +28,7 @@ export const Start = () => {
2628
}));
2729

2830
const [errors, setErrors] = useState<errorMessage[]>();
31+
const { getFlag } = useFeatureFlags();
2932

3033
// Prevent prototype pollution in JSON.parse https://stackoverflow.com/a/63927372
3134
const cleaner = (key: string, value: string) =>
@@ -78,10 +81,18 @@ export const Start = () => {
7881
return;
7982
}
8083

81-
importTemplate(data);
84+
try {
85+
checkForBetaComponents(data.elements, getFlag);
8286

83-
ga("open_form_file");
84-
router.push(`/${language}/form-builder/0000/preview`);
87+
importTemplate(data);
88+
89+
ga("open_form_file");
90+
router.push(`/${language}/form-builder/0000/preview`);
91+
} catch (e) {
92+
if (e instanceof BetaComponentsError) {
93+
setErrors([{ message: t("beta.loadingError") }]);
94+
}
95+
}
8596
};
8697
} catch (e) {
8798
if (e instanceof Error) {
@@ -99,9 +110,11 @@ export const Start = () => {
99110
<div role="alert">
100111
{errors && (
101112
<div className="m-auto mb-8 flex w-5/12 bg-red-100 p-6">
102-
<WarningIcon />
103113
<div>
104-
<h3 className="mb-2 ml-6 mt-1">{t("failedToReadFormFile")}</h3>
114+
<WarningIcon className="mt-1" />
115+
</div>
116+
<div>
117+
<h3 className="mb-2 ml-6">{t("failedToReadFormFile")}</h3>
105118
<ul className="mb-4 list-none pl-6">
106119
{errors.map((error, index) => {
107120
return (

app/(gcforms)/[locale]/(form administration)/form-builder/[id]/edit/components/elements/element-dialog/descriptions/FileInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ export const FileInputTrialDescription = () => {
5353
<li>
5454
<Link href={link}>{t("addElementDialog.fileInputWithApi.trialFeature.bullet1")}</Link>
5555
</li>
56+
<li>{t("addElementDialog.fileInputWithApi.trialFeature.bullet2")}</li>
5657
<li>{t("addElementDialog.fileInputWithApi.trialFeature.bullet3")}</li>
57-
<li>{t("addElementDialog.fileInputWithApi.trialFeature.bullet4")}</li>
5858
</ul>
5959
</div>
6060
);

app/(gcforms)/[locale]/(form administration)/form-builder/actions.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ export const createOrUpdateTemplate = AuthenticatedAction(
6262
error?: string;
6363
}> => {
6464
try {
65-
revalidatePath("/[locale]/forms", "page");
66-
6765
if (id) {
6866
return await updateTemplate({
6967
id,
@@ -88,6 +86,9 @@ export const createOrUpdateTemplate = AuthenticatedAction(
8886
if (!formRecord) {
8987
throw new Error("Failed to create template");
9088
}
89+
// revalidatePath must be at the end of a function. It leverages the error object to trigger
90+
// and internal refresh and can have awkward results if used before an error is thrown by a following fn.
91+
revalidatePath("/[locale]/forms", "page");
9192

9293
return { formRecord: { id: formRecord.id, updatedAt: formRecord.updatedAt } };
9394
} catch (_) {

app/(gcforms)/[locale]/(form filler)/id/[...props]/actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export async function submitForm(
5656

5757
const hCaptchaBlockingMode = await checkOne(FeatureFlags.hCaptcha);
5858
// Skip hCaptcha verification for form-builder Preview (drafts)
59-
if (template?.isPublished) {
59+
if (template?.isPublished && process.env.APP_ENV !== "test") {
6060
// hCaptcha runs regardless but only block submissions if the feature flag is enabled
6161
const captchaVerified = await verifyHCaptchaToken(captchaToken || "");
6262
if (hCaptchaBlockingMode && !captchaVerified) {

app/(gcforms)/[locale]/(user authentication)/auth/mfa/components/client/MFAForm.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { ToastContainer } from "@formBuilder/components/shared/Toast";
1313
import { useFocusIt } from "@lib/hooks/useFocusIt";
1414
import { Loader } from "@clientComponents/globals/Loader";
1515
import { useRouter } from "next/navigation";
16-
import { updateSessionProvider } from "@lib/hooks/auth/updateSessionProvider";
16+
import { useSession } from "next-auth/react";
1717

1818
export const MFAForm = () => {
1919
const {
@@ -30,6 +30,7 @@ export const MFAForm = () => {
3030
const [isReady, setIsReady] = useState(false);
3131
const [isSubmittingStep1, setIsSubmittingStep1] = useState(false);
3232
const [isSubmittingStep2, setIsSubmittingStep2] = useState(false);
33+
const { update: sessionUpdate } = useSession();
3334

3435
const authToken = useRef<{ email?: string; authenticationFlowToken?: string }>({});
3536

@@ -52,7 +53,7 @@ export const MFAForm = () => {
5253
if (mfaState.success) {
5354
setIsSubmittingStep2(true);
5455
// Let the Session Provider know that the user has been authenticated
55-
updateSessionProvider();
56+
sessionUpdate();
5657
sessionStorage.removeItem("authFlowToken");
5758
const result = await getRedirectPath(language);
5859
if (result.callback) router.push(result.callback);

app/(gcforms)/[locale]/(user authentication)/auth/policy/components/client/AcceptButton.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,36 @@ import { useState } from "react";
77
import { useSession } from "next-auth/react";
88
import { SubmitButton } from "@clientComponents/globals/Buttons/SubmitButton";
99

10+
// Needed for Cypress E2E testing for some reason
11+
// The broadcast should be called as part of the useSession update() fn but doesn't seem to be registering
12+
13+
let broadcastChannel: BroadcastChannel | null = null;
14+
15+
function broadcast() {
16+
if (typeof BroadcastChannel === "undefined") {
17+
return {
18+
postMessage: () => {},
19+
addEventListener: () => {},
20+
removeEventListener: () => {},
21+
};
22+
}
23+
24+
if (broadcastChannel === null) {
25+
broadcastChannel = new BroadcastChannel("next-auth");
26+
}
27+
28+
return broadcastChannel;
29+
}
30+
31+
export const updateSessionProvider = () => {
32+
broadcast().postMessage({
33+
event: "session",
34+
data: { trigger: "getSession" },
35+
});
36+
};
37+
38+
// End of code needed for Cypress Testing
39+
1040
export const AcceptButton = () => {
1141
const router = useRouter();
1242
const [isLoading, setIsLoading] = useState(false);
@@ -29,6 +59,9 @@ export const AcceptButton = () => {
2959
user: { acceptableUse: true },
3060
});
3161

62+
// Needed for cypress e2e testing
63+
updateSessionProvider();
64+
3265
if (session?.user.newlyRegistered) {
3366
router.push(`/${language}/auth/account-created`);
3467
} else {

app/(gcforms)/[locale]/layout.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
import { auth } from "@lib/auth";
22
import { ClientContexts } from "@clientComponents/globals/ClientContexts";
33
import { ReactHydrationCheck } from "@clientComponents/globals";
4-
import { getSomeFlags } from "@lib/cache/flags";
5-
import { FeatureFlags } from "@lib/cache/types";
4+
import { checkAll } from "@lib/cache/flags";
65

76
export default async function Layout({ children }: { children: React.ReactNode }) {
87
const session = await auth();
9-
const featureFlags = await getSomeFlags([
10-
FeatureFlags.addressComplete,
11-
FeatureFlags.formTimer,
12-
FeatureFlags.hCaptcha,
13-
FeatureFlags.topBanner,
14-
FeatureFlags.fileUpload,
15-
]);
8+
const featureFlags = await checkAll();
169

1710
return (
1811
<>

components/clientComponents/forms/FormattedDate/FormattedDate.cy.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import { Formik } from "formik";
33
import { FormattedDate } from "./FormattedDate";
4+
import { DateFormat } from "./types";
45

56
describe("<FormattedDate />", () => {
67
it("mounts", () => {
@@ -106,7 +107,7 @@ describe("<FormattedDate />", () => {
106107
throw new Error("Function not implemented.");
107108
}}
108109
>
109-
<FormattedDate name="formattedDate" dateFormat="XXXX-XX-XX" />
110+
<FormattedDate name="formattedDate" dateFormat={"XXXX-XX-XX" as unknown as DateFormat} />
110111
</Formik>
111112
);
112113

0 commit comments

Comments
 (0)