Skip to content

Commit

Permalink
Refactor contact info input
Browse files Browse the repository at this point in the history
And add a test for wrapping the contact info depending on that the input
fields should be shown inline or not.
  • Loading branch information
spaceo committed Oct 7, 2023
1 parent 07e9ad9 commit 15728c3
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 73 deletions.
52 changes: 52 additions & 0 deletions src/components/contact-info-section/ContactInfoEmail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as React from "react";
import { FC } from "react";
import TextInput from "../atoms/input/TextInput";
import { PatronSettingsV3, PatronV5 } from "../../core/fbs/model";
import { useText } from "../../core/utils/text";
import CheckBox from "../checkbox/Checkbox";
import { ChangePatronProps } from "./types";

export interface ContactInfoEmailProps {
className?: string;
patron: PatronV5 | PatronSettingsV3 | null;
changePatron: ChangePatronProps;
showCheckboxes: boolean;
isRequired?: boolean;
}

const ContactInfoEmail: FC<ContactInfoEmailProps> = ({
className = "",
patron,
changePatron,
showCheckboxes,
isRequired = false
}) => {
const t = useText();
return (
<>
<TextInput
className={className}
id="email-address-input"
type="email"
required={isRequired}
onChange={(newEmail) => changePatron(newEmail, "emailAddress")}
value={patron?.emailAddress}
label={t("patronContactEmailLabelText")}
/>
{showCheckboxes && (
<CheckBox
className="mt-8 mb-16"
onChecked={(newReceiveEmail: boolean) =>
changePatron(newReceiveEmail, "receiveEmail")
}
id="email-messages"
selected={patron?.receiveEmail}
disabled={false}
label={t("patronContactEmailCheckboxText")}
/>
)}
</>
);
};

export default ContactInfoEmail;
48 changes: 48 additions & 0 deletions src/components/contact-info-section/ContactInfoInputs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { configure, render } from "@testing-library/react";

Check warning on line 2 in src/components/contact-info-section/ContactInfoInputs.tsx

View workflow job for this annotation

GitHub Actions / Lint .js and .jsx

'configure' is defined but never used

Check warning on line 2 in src/components/contact-info-section/ContactInfoInputs.tsx

View workflow job for this annotation

GitHub Actions / Lint .js and .jsx

'render' is defined but never used
import clsx from "clsx";
import * as React from "react";
import { FC } from "react";

export interface ContactInfoInputsProps {
isInline: boolean;
children: React.ReactNode;
dataCy?: string;
className?: string;
}

// This component wraps the input fields for the contact info section
// depending on the isInline prop.
const ContactInfoInputs: FC<ContactInfoInputsProps> = ({
isInline,
children,
dataCy = "contact-info-input",
className = undefined
}) => {
if (!isInline) {
return (
<div className={className} data-cy={dataCy}>
{children}
</div>
);
}

const renderableChildren = React.Children.toArray(children);
return (
<div
className={clsx(className, {
"contact-info-flex": isInline
})}
data-cy={dataCy}
>
{renderableChildren.map((child, i) => {
const childClassName = clsx("patron__input--desktop", {
"mr-16": i < renderableChildren.length - 1
});
return <div className={childClassName}>{child}</div>;
})}
</div>
);
};

export default ContactInfoInputs;
52 changes: 52 additions & 0 deletions src/components/contact-info-section/ContactInfoPhone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as React from "react";
import { FC } from "react";
import TextInput from "../atoms/input/TextInput";
import { PatronSettingsV3, PatronV5 } from "../../core/fbs/model";
import CheckBox from "../checkbox/Checkbox";
import { useText } from "../../core/utils/text";
import { ChangePatronProps } from "./types";

export interface ContactInfoPhoneProps {
patron: PatronV5 | PatronSettingsV3 | null;
changePatron: ChangePatronProps;
showCheckboxes: boolean;
className?: string;
}

const ContactInfoPhone: FC<ContactInfoPhoneProps> = ({
patron,
changePatron,
showCheckboxes,
className = ""
}) => {
const t = useText();
return (
<>
<TextInput
className={className}
id="phone-input"
required
type="number"
onChange={(newPhoneNumber) =>
changePatron(newPhoneNumber, "phoneNumber")
}
value={patron?.phoneNumber}
label={t("patronContactPhoneLabelText")}
/>
{showCheckboxes && (
<CheckBox
className="mt-8 mb-16"
onChecked={(newReceiveSms: boolean) =>
changePatron(newReceiveSms, "receiveSms")
}
id="phone-messages"
selected={patron?.receiveSms}
disabled={false}
label={t("patronContactPhoneCheckboxText")}
/>
)}
</>
);
};

export default ContactInfoPhone;
94 changes: 21 additions & 73 deletions src/components/contact-info-section/ContactInfoSection.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import React, { FC } from "react";
import clsx from "clsx";
import { PatronV5, PatronSettingsV3 } from "../../core/fbs/model";
import TextInput from "../atoms/input/TextInput";
import CheckBox from "../checkbox/Checkbox";
import { useText } from "../../core/utils/text";
import { useConfig } from "../../core/utils/config";

export interface ChangePatronProps {
(newValue: string | boolean, key: string): void;
}
import ContactInfoInputs from "./ContactInfoInputs";
import ContactInfoPhone from "./ContactInfoPhone";
import ContactInfoEmail from "./ContactInfoEmail";
import { ChangePatronProps } from "./types";

interface ContactInfoSectionProps {
patron: PatronV5 | PatronSettingsV3 | null;
Expand All @@ -26,64 +24,9 @@ const ContactInfoSection: FC<ContactInfoSectionProps> = ({
const t = useText();
const inputsClass = clsx("dpl-input", { input__desktop: inLine });
const config = useConfig();
const textNotificationsEnabled =
const textNotificationsEnabledConfig =
config("textNotificationsEnabledConfig") === "1";

const phoneNode = (
<>
<TextInput
className={inputsClass}
id="phone-input"
required
type="number"
onChange={(newPhoneNumber) =>
changePatron(newPhoneNumber, "phoneNumber")
}
value={patron?.phoneNumber}
label={t("patronContactPhoneLabelText")}
/>
{showCheckboxes && textNotificationsEnabled && (
<CheckBox
className="mt-8 mb-16"
onChecked={(newReceiveSms: boolean) =>
changePatron(newReceiveSms, "receiveSms")
}
id="phone-messages"
selected={patron?.receiveSms}
disabled={false}
label={t("patronContactPhoneCheckboxText")}
/>
)}
</>
);
const emailNode = (
<>
<TextInput
className={clsx(inputsClass, {
"mt-32": !textNotificationsEnabled && !inLine
})}
id="email-address-input"
type="email"
required
onChange={(newEmail) => changePatron(newEmail, "emailAddress")}
value={patron?.emailAddress}
label={t("patronContactEmailLabelText")}
/>
{showCheckboxes && (
<CheckBox
className="mt-8 mb-16"
onChecked={(newReceiveEmail: boolean) =>
changePatron(newReceiveEmail, "receiveEmail")
}
id="email-messages"
selected={patron?.receiveEmail}
disabled={false}
label={t("patronContactEmailCheckboxText")}
/>
)}
</>
);

return (
<section data-cy="patron-page-contact-info">
<h2 className="text-header-h4 mt-32 mb-16">
Expand All @@ -94,17 +37,22 @@ const ContactInfoSection: FC<ContactInfoSectionProps> = ({
{t("patronContactInfoBodyText")}
</p>
)}
{inLine && (
<div className={`${inLine ? "contact-info-flex" : ""}`}>
<div className="patron__input--desktop mr-16">{phoneNode}</div>
<div className="patron__input--desktop">{emailNode}</div>
</div>
)}
{!inLine && (
<>
{phoneNode} {emailNode}
</>
)}
<ContactInfoInputs isInline={inLine}>
<ContactInfoPhone
className={inputsClass}
changePatron={changePatron}
patron={patron}
showCheckboxes={showCheckboxes && textNotificationsEnabledConfig}
/>
<ContactInfoEmail
className={clsx(inputsClass, {
"mt-32": !textNotificationsEnabledConfig && !inLine
})}
changePatron={changePatron}
patron={patron}
showCheckboxes={showCheckboxes}
/>
</ContactInfoInputs>
</section>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Vitest Snapshot v1

exports[`ContactInfoInputs > Should NOT wrap the input fields if it is NOT inline 1`] = `
<div
data-cy="contact-info-input-inline"
>
<p>
One input component
</p>
<p>
Another input component
</p>
</div>
`;

exports[`ContactInfoInputs > Should wrap the input fields if it is inline 1`] = `
<div
class="contact-info-flex"
data-cy="contact-info-input"
>
<div
class="patron__input--desktop mr-16"
>
<p>
One input component
</p>
</div>
<div
class="patron__input--desktop"
>
<p>
Another input component
</p>
</div>
</div>
`;
3 changes: 3 additions & 0 deletions src/components/contact-info-section/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface ChangePatronProps {
(newValue: string | boolean, key: string): void;
}
36 changes: 36 additions & 0 deletions src/tests/unit/__snapshots__/contact-info.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Vitest Snapshot v1

exports[`ContactInfoInputs > Should NOT wrap the input fields if it is NOT inline 1`] = `
<div
data-cy="contact-info-input-inline"
>
<p>
One input component
</p>
<p>
Another input component
</p>
</div>
`;

exports[`ContactInfoInputs > Should wrap the input fields if it is inline 1`] = `
<div
class="contact-info-flex"
data-cy="contact-info-input"
>
<div
class="patron__input--desktop mr-16"
>
<p>
One input component
</p>
</div>
<div
class="patron__input--desktop"
>
<p>
Another input component
</p>
</div>
</div>
`;
36 changes: 36 additions & 0 deletions src/tests/unit/contact-info.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { describe, expect, it } from "vitest";
import React from "react";
import { render } from "@testing-library/react";
import { configure } from "@testing-library/dom";
import ContactInfoInputs from "../../components/contact-info-section/ContactInfoInputs";

configure({
testIdAttribute: "data-cy"
});

describe("ContactInfoInputs", () => {
it("Should wrap the input fields if it is inline", async () => {
const { getByTestId } = render(
<ContactInfoInputs isInline>
<p>One input component</p>
<p>Another input component</p>
</ContactInfoInputs>
);

const contactInfoInputs = getByTestId("contact-info-input");

expect(contactInfoInputs).toMatchSnapshot();
});
it("Should NOT wrap the input fields if it is NOT inline", async () => {
const { getByTestId } = render(
<ContactInfoInputs dataCy="contact-info-input-inline" isInline={false}>
<p>One input component</p>
<p>Another input component</p>
</ContactInfoInputs>
);

const contactInfoInputs = getByTestId("contact-info-input-inline");

expect(contactInfoInputs).toMatchSnapshot();
});
});

0 comments on commit 15728c3

Please sign in to comment.