diff --git a/services/ui-src/src/assets/logos/logo_mdct_hcbs.png b/services/ui-src/src/assets/logos/logo_mdct_hcbs.png
new file mode 100644
index 00000000..6e6e4453
Binary files /dev/null and b/services/ui-src/src/assets/logos/logo_mdct_hcbs.png differ
diff --git a/services/ui-src/src/assets/logos/logo_mdct_mfp.png b/services/ui-src/src/assets/logos/logo_mdct_mfp.png
deleted file mode 100644
index 74e6101d..00000000
Binary files a/services/ui-src/src/assets/logos/logo_mdct_mfp.png and /dev/null differ
diff --git a/services/ui-src/src/components/app/App.tsx b/services/ui-src/src/components/app/App.tsx
index f31d55d1..8ddbcbb5 100644
--- a/services/ui-src/src/components/app/App.tsx
+++ b/services/ui-src/src/components/app/App.tsx
@@ -1,12 +1,19 @@
-import { useStore } from "utils";
+import { useContext } from "react";
import { Routes, Route } from "react-router-dom";
-import { LoginCognito, LoginIDM, PostLogoutRedirect, Footer } from "components";
-import { makeMediaQueryClasses } from "utils/other/useBreakpoint";
import { Container, Divider, Flex, Heading, Stack } from "@chakra-ui/react";
+import {
+ Header,
+ LoginCognito,
+ LoginIDM,
+ PostLogoutRedirect,
+ Footer,
+} from "components";
+import { makeMediaQueryClasses, UserContext, useStore } from "utils";
export const App = () => {
const mqClasses = makeMediaQueryClasses();
- // const context = useContext(UserContext);
+ const context = useContext(UserContext);
+ const { logout } = context;
const { user, showLocalLogins } = useStore();
// const { pathname } = useLocation();
@@ -26,6 +33,7 @@ export const App = () => {
<>
{user && (
+
)}
diff --git a/services/ui-src/src/components/index.ts b/services/ui-src/src/components/index.ts
index 83e21c46..29efb63f 100644
--- a/services/ui-src/src/components/index.ts
+++ b/services/ui-src/src/components/index.ts
@@ -4,11 +4,15 @@ export { ErrorAlert } from "./alerts/ErrorAlert";
// app
export { App } from "./app/App";
export { Error } from "./app/Error";
-// logins
-export { LoginCognito } from "./logins/LoginCognito";
-export { LoginIDM } from "./logins/LoginIDM";
// layout
+export { Header } from "./layout/Header";
export { PageTemplate } from "./layout/PageTemplate";
export { Footer } from "./layout/Footer";
+// logins
+export { LoginCognito } from "./logins/LoginCognito";
+export { LoginIDM } from "./logins/LoginIDM";
+// menus
+export { Menu } from "./menus/Menu";
+export { MenuOption } from "./menus/MenuOption";
// Redirects
export { PostLogoutRedirect } from "./PostLogoutRedirect/index";
diff --git a/services/ui-src/src/components/layout/Header.test.tsx b/services/ui-src/src/components/layout/Header.test.tsx
new file mode 100644
index 00000000..2cc0a31c
--- /dev/null
+++ b/services/ui-src/src/components/layout/Header.test.tsx
@@ -0,0 +1,59 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { RouterWrappedComponent } from "utils/testing/setupJest";
+import { Header, MenuOption } from "components";
+import { testA11y } from "utils/testing/commonTests";
+
+const headerComponent = (
+
+
+);
+
+describe("", () => {
+ describe("Test Visibility of Header", () => {
+ beforeEach(() => {
+ render(headerComponent);
+ });
+
+ test("Navigation, Logo, Help and Menu is visible on Header", () => {
+ const header = screen.getByRole("navigation");
+ expect(header).toBeVisible();
+ expect(screen.getByAltText("HCBS logo")).toBeVisible();
+ expect(screen.getByAltText("Help")).toBeVisible();
+ expect(screen.getByAltText("Arrow down")).toBeVisible();
+ });
+
+ test("Renders My Account menu and is clickable", () => {
+ render(
+
+ );
+
+ const menuButton = screen.getByRole("button", { name: /my account/i });
+ expect(menuButton).toBeInTheDocument();
+
+ userEvent.click(menuButton);
+ });
+
+ test("Logs out user", () => {
+ render(
+
+ );
+
+ const logoutButton = screen.getByRole("img", { name: /Logout/i });
+ expect(logoutButton).toBeInTheDocument();
+
+ userEvent.click(logoutButton);
+ });
+ });
+
+ testA11y(headerComponent);
+});
diff --git a/services/ui-src/src/components/layout/Header.tsx b/services/ui-src/src/components/layout/Header.tsx
new file mode 100644
index 00000000..6772130d
--- /dev/null
+++ b/services/ui-src/src/components/layout/Header.tsx
@@ -0,0 +1,91 @@
+import { Link as RouterLink } from "react-router-dom";
+import { UsaBanner } from "@cmsgov/design-system";
+import { Box, Container, Flex, Image, Link } from "@chakra-ui/react";
+import { Menu, MenuOption } from "components";
+import { useBreakpoint } from "utils";
+import appLogo from "assets/logos/logo_mdct_hcbs.png";
+import getHelpIcon from "assets/icons/icon_help.png";
+
+export const Header = ({ handleLogout }: Props) => {
+ const { isMobile } = useBreakpoint();
+
+ return (
+
+ );
+};
+
+interface Props {
+ handleLogout: () => void;
+}
+
+const sx = {
+ root: {
+ position: "sticky",
+ top: 0,
+ zIndex: "sticky",
+ boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.25)",
+ "@media print": {
+ display: "none",
+ },
+ },
+ usaBannerContainer: {
+ width: "100%",
+ flexDirection: "column",
+ alignItems: "center",
+ backgroundColor: "palette.gray_lightest",
+ ".desktop &": {
+ padding: "0 1rem",
+ },
+ },
+ headerBar: {
+ minHeight: "4rem",
+ alignItems: "center",
+ bg: "palette.primary_darkest",
+ },
+ headerContainer: {
+ maxW: "appMax",
+ ".desktop &": {
+ padding: "0 2rem",
+ },
+ },
+ headerFlex: {
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+ menuFlex: {
+ alignItems: "center",
+ },
+ appLogo: {
+ maxWidth: "200px",
+ },
+};
diff --git a/services/ui-src/src/components/menus/Menu.test.tsx b/services/ui-src/src/components/menus/Menu.test.tsx
new file mode 100644
index 00000000..05164d84
--- /dev/null
+++ b/services/ui-src/src/components/menus/Menu.test.tsx
@@ -0,0 +1,20 @@
+import { render, screen } from "@testing-library/react";
+import { RouterWrappedComponent } from "utils/testing/setupJest";
+import { Menu } from "components";
+import { testA11y } from "utils/testing/commonTests";
+
+const menuComponent = (
+
+
+);
+
+describe("
", () => {
+ test("Menu button is visible", () => {
+ render(menuComponent);
+
+ expect(screen.getByRole("button", { name: "my account" })).toBeVisible();
+ });
+
+ testA11y(menuComponent);
+});
diff --git a/services/ui-src/src/components/menus/Menu.tsx b/services/ui-src/src/components/menus/Menu.tsx
new file mode 100644
index 00000000..85d22b16
--- /dev/null
+++ b/services/ui-src/src/components/menus/Menu.tsx
@@ -0,0 +1,91 @@
+import {
+ Box,
+ Button,
+ Image,
+ Menu as MenuRoot,
+ MenuButton,
+ MenuItem,
+ MenuList,
+} from "@chakra-ui/react";
+import { MenuOption } from "components";
+import { useBreakpoint } from "utils";
+import accountCircleIcon from "assets/icons/icon_account_circle.png";
+import chevronDownIcon from "assets/icons/icon_arrow_down.png";
+import logoutIcon from "assets/icons/icon_arrow_right_square.png";
+
+export const Menu = ({ handleLogout }: Props) => {
+ const { isMobile } = useBreakpoint();
+ return (
+
+
+
+ }
+ sx={sx.menuButton}
+ aria-label="my account"
+ >
+
+
+
+
+
+
+
+ );
+};
+
+interface Props {
+ handleLogout: () => void;
+}
+
+const sx = {
+ menuButton: {
+ padding: 0,
+ paddingRight: ".5rem",
+ marginLeft: ".5rem",
+ borderRadius: 0,
+ background: "none",
+ color: "palette.white",
+ fontWeight: "bold",
+ _hover: { color: "palette.secondary_light", background: "none !important" },
+ _active: { background: "none" },
+ _focus: {
+ boxShadow: "none",
+ outline: "0px solid transparent !important",
+ },
+ ".mobile &": {
+ marginLeft: 0,
+ },
+ "& .chakra-button__icon": {
+ marginInlineStart: "0rem",
+ },
+ },
+ menuList: {
+ padding: "0",
+ border: "none",
+ background: "palette.primary_darkest",
+ boxShadow: "0px 5px 16px rgba(0, 0, 0, 0.14)",
+ },
+ menuItem: {
+ borderRadius: ".375rem",
+ _focus: { background: "palette.primary_darker" },
+ _hover: { background: "palette.primary_darker" },
+ },
+ menuIcon: {
+ width: "0.75rem",
+ },
+};
diff --git a/services/ui-src/src/components/menus/MenuOption.tsx b/services/ui-src/src/components/menus/MenuOption.tsx
new file mode 100644
index 00000000..3039ee49
--- /dev/null
+++ b/services/ui-src/src/components/menus/MenuOption.tsx
@@ -0,0 +1,34 @@
+import { Flex, Image, Text } from "@chakra-ui/react";
+
+export const MenuOption = ({ text, icon, altText, role, hideText }: Props) => {
+ return (
+
+
+ {!hideText && {text}}
+
+ );
+};
+
+interface Props {
+ text: string;
+ icon: string;
+ altText: string;
+ role?: string;
+ hideText?: boolean;
+}
+
+const sx = {
+ text: {
+ fontWeight: "bold",
+ color: "palette.white",
+ _groupHover: { color: "palette.gray_lighter" },
+ },
+ menuIcon: {
+ width: "1.5rem",
+ margin: "0.5rem",
+ },
+};