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

adds the PromoBar component and a new custom hook useNotificationMessage to the PatronPage #606

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
11 changes: 10 additions & 1 deletion src/apps/patron-page/PatronPage.dev.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -240,11 +240,20 @@ export default {
defaultValue: "You used @this ebooks out of you quota of @that ebooks",
control: { type: "text" }
},
phoneInputMessageText: {
patronPagePhoneInputMessageText: {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you have refactored this data prop, what about dpl-cms then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

name: "Phone input validation message",
defaultValue:
"The phone number must be 6 to 15 characters in length and should be comprised solely of numbers or begin with a +",
control: { type: "text" }
},
patronPageHandleResponseInformationText: {
defaultValue: "Your changes are saved.",
control: { type: "text" }
},
patronPageLoadingText: {
name: "Loading",
defaultValue: "Loading..",
control: { type: "text" }
}
}
} as ComponentMeta<typeof PatronPage>;
Expand Down
4 changes: 3 additions & 1 deletion src/apps/patron-page/PatronPage.entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ interface PatronPageTextProps {
patronPageStatusSectionOutOfText: string;
patronPageStatusSectionOutOfAriaLabelAudioBooksText: string;
patronPageStatusSectionOutOfAriaLabelEbooksText: string;
phoneInputMessageText: string;
patronPagePhoneInputMessageText: string;
patronPageHandleResponseInformationText: string;
patronPageLoadingText: string;
}

export interface PatronPageProps
Expand Down
11 changes: 10 additions & 1 deletion src/apps/patron-page/PatronPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import PauseReservation from "../reservation-list/modal/pause-reservation/pause-
import { getModalIds } from "../../core/utils/helpers/general";
import { useUrls } from "../../core/utils/url";
import { usePatronData } from "../../components/material/helper";
import { useNotificationMessage } from "../../core/utils/useNotificationMessage";

const PatronPage: FC = () => {
const queryClient = useQueryClient();
Expand All @@ -33,6 +34,8 @@ const PatronPage: FC = () => {
const [successPinMessage, setSuccessPinMessage] = useState<string | null>(
null
);
const [NotificationComponent, handleNotificationMessage] =
useNotificationMessage();

useEffect(() => {
if (patronData && patronData.patron) {
Expand Down Expand Up @@ -86,6 +89,9 @@ const PatronPage: FC = () => {
setSuccessPinMessage(t("patronPinSavedSuccessText"));
}
setDisableSubmitButton(false);
handleNotificationMessage(
t("patronPageHandleResponseInformationText")
);
},
// todo error handling, missing in figma
onError: () => {
Expand All @@ -105,6 +111,7 @@ const PatronPage: FC = () => {
<>
<form className="dpl-patron-page" onSubmit={(e) => handleSubmit(e)}>
<h1 className="text-header-h1 my-32">{t("patronPageHeaderText")}</h1>
<NotificationComponent />
kasperbirch1 marked this conversation as resolved.
Show resolved Hide resolved
{patron && <BasicDetailsSection patron={patron} />}
<div className="patron-page-info">
{patron && (
Expand Down Expand Up @@ -135,7 +142,9 @@ const PatronPage: FC = () => {
type="submit"
disabled={disableSubmitButton}
>
{t("patronPageSaveButtonText")}
{disableSubmitButton
? t("patronPageLoadingText")
: t("patronPageSaveButtonText")}
</button>

<div className="text-body-small-regular mt-32">
Expand Down
4 changes: 2 additions & 2 deletions src/components/contact-info-section/ContactInfoPhone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ const ContactInfoPhone: FC<ContactInfoPhoneProps> = ({
required={isRequired}
type="tel"
pattern="\+?[0-9]{6,15}"
title={t("phoneInputMessageText")}
title={t("patronPagePhoneInputMessageText")}
onChange={(newPhoneNumber) =>
changePatron(newPhoneNumber, "phoneNumber")
}
value={patron?.phoneNumber}
label={t("patronContactPhoneLabelText")}
placeholder={t("phoneInputMessageText")}
placeholder={t("patronPagePhoneInputMessageText")}
/>
{showCheckboxes && (
<CheckBox
Expand Down
14 changes: 14 additions & 0 deletions src/components/notification/NotificationComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";
import PromoBar from "../promo-bar/PromoBar";

interface NotificationComponentProps {
notificationMessage: string;
}

const NotificationComponent: React.FC<NotificationComponentProps> = ({
notificationMessage
}) => {
return <PromoBar text={notificationMessage} type="info" />;
};

export default NotificationComponent;
2 changes: 1 addition & 1 deletion src/components/promo-bar/PromoBarIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const PromoBarIcon: React.FunctionComponent<PromoBarIconProps> = ({
type
}) => {
if (type === "info") {
return <img src={IconInfo} alt="" />;
return <img src={IconInfo} alt="" className="ml-4" />;
}
return null;
};
39 changes: 39 additions & 0 deletions src/core/utils/useNotificationMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from "react";
import NotificationComponent from "../../components/notification/NotificationComponent";

export type UseNotificationOptionsType = {
timeout?: number;
scrollToTop?: boolean;
};
type UseNotificationReturnType = [React.FC, (text: string) => void];

export const useNotificationMessage = ({
timeout = 5000,
scrollToTop = true
}: UseNotificationOptionsType = {}): UseNotificationReturnType => {
const [notificationMessage, setNotificationMessage] = useState<string | null>(
null
);

const handleNotificationMessage = (text: string) => {
setNotificationMessage(text);
if (scrollToTop) {
window.scrollTo(0, 0);
}
if (timeout) {
setTimeout(() => {
setNotificationMessage(null);
}, timeout);
}
};

return [
() =>
notificationMessage ? (
<NotificationComponent notificationMessage={notificationMessage} />
) : null,
handleNotificationMessage
];
};

export default {};
136 changes: 136 additions & 0 deletions src/tests/unit/notification-message.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import React from "react";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import {
UseNotificationOptionsType,
useNotificationMessage
} from "../../core/utils/useNotificationMessage";

// Define a test component that utilizes the useNotificationMessage hook
const ComponentWithNotificationMessage = ({
timeout,
scrollToTop
}: UseNotificationOptionsType) => {
const [NotificationMessage, handler] = useNotificationMessage({
timeout,
scrollToTop
});

return (
<div data-testid="wrapper">
<NotificationMessage />
<button
data-testid="button"
type="button"
onClick={() => handler("Some message")}
>
Click me
</button>
</div>
);
};

describe("useNotificationMessage hook", () => {
afterEach(() => {
cleanup();
});

it("should not display a message before the button is clicked", () => {
const { getByTestId } = render(<ComponentWithNotificationMessage />);
const wrapper = getByTestId("wrapper");

// Expectations before the button is clicked
expect(screen.queryByText(/Some message/)).toBeNull(); // Expect the message to not be displayed
kasperbirch1 marked this conversation as resolved.
Show resolved Hide resolved

// Assert that the wrapper does not contain the message initially
expect(wrapper).toMatchInlineSnapshot(`
<div
data-testid="wrapper"
spaceo marked this conversation as resolved.
Show resolved Hide resolved
>
<button
data-testid="button"
type="button"
>
Click me
</button>
</div>
`);
});

it("should display a message after button activation with default timeout and scrollToTop settings initialized", () => {
vi.spyOn(window, "scrollTo");
vi.spyOn(window, "setTimeout");

const { getByTestId } = render(<ComponentWithNotificationMessage />);
const wrapper = getByTestId("wrapper");
const button = getByTestId("button");

// Simulate button click
fireEvent.click(button);

// Expectations after the button is clicked
expect(window.scrollTo).toHaveBeenCalledWith(0, 0); // Expect page to scroll to top
expect(window.setTimeout).toHaveBeenCalledTimes(1); // Expect setTimeout to be called once
expect(screen.queryByText(/Some message/)).toBeTruthy(); // Expect the message to be displayed

// Assert final state of the wrapper
expect(wrapper).toMatchInlineSnapshot(`
<div
data-testid="wrapper"
>
<section
class="promo-bar"
>
<img
alt=""
class="ml-4"
src="/node_modules/@danskernesdigitalebibliotek/dpl-design-system/build/icons/basic/icon-info.svg"
/>
<p
class="text-small-caption"
>
Some message
</p>
</section>
<button
data-testid="button"
type="button"
>
Click me
</button>
</div>
`);
});

it("should keep displaying a message indefinitely if timeout is removed", () => {
vi.spyOn(window, "setTimeout");

const { getByTestId } = render(
<ComponentWithNotificationMessage timeout={0} />
);
const button = getByTestId("button");

// Simulate button click
fireEvent.click(button);

// Expectations
expect(window.setTimeout).not.toHaveBeenCalled(); // Expect setTimeout to not be called
expect(screen.queryByText(/Some message/)).toBeTruthy(); // Expect the message to be displayed indefinitely
});

it("should not scroll to top if scrollToTop is false", () => {
vi.spyOn(window, "scrollTo");

const { getByTestId } = render(
<ComponentWithNotificationMessage scrollToTop={false} />
);
const button = getByTestId("button");

// Simulate button click
fireEvent.click(button);

// Expectations
expect(window.scrollTo).not.toHaveBeenCalled(); // Expect page to not scroll to top
expect(screen.queryByText(/Some message/)).toBeTruthy(); // Expect the message to be displayed
});
});
Loading