- Content shown in the header. Take into account that the component applies styles for the first child in
- the content, so we recommend the use of React.Fragment to be applied correctly. Otherwise,
- the styles can be modified.
-
-
-
-
-
- logo
+
+ branding
-
- {"Logo"}
-
- being Logo an object with the following properties:
-
- {logoTypeString}
-
+ {brandingTypeString}
-
Logo to be displayed inside the header.
+
Object used to configure the header branding, including logo and application title.
Size of the bottom margin to be applied to the header.
-
-
-
-
-
onClick
- {"() => void"}
+ Array of navigation items to be displayed in the header navigation menu. Each item can be a single/simple
+ item or a group item.
+
+ Being Item an object with the following properties:
+
+ {itemTypeString}
+
+ and GroupItem an object with the following properties:
+
+ {groupItemTypeString}
+ Group items will ignore any nested group items to maintain a maximum of two levels in the navigation menu.
+ When responsive, navigation items will be displayed in a vertical menu below the header in a vertical
+ layout.
-
This function will be called when the user clicks the header logo.
- Content shown in responsive version. It receives the close menu handler that can be used to add that
- functionality when a element is clicked.
+ The content rendered in the bottom part of the header menu under the navigation items when in responsive
+ mode.
-
-
tabIndex
+
sideContent
- number
+ {"React.ReactNode | (isResponsive: boolean) => React.ReactNode"}
-
Value of the tabindex for all interactive elements, except those inside the custom area.
- 0
-
-
-
-
underlined
-
- boolean
-
-
Whether a contrast line should appear at the bottom of the header.
-
- false
+ Content to be displayed on the right side of the header. It can be a React node or a function that
+ receives a boolean indicating if the header is in responsive mode and returns a React node.
),
},
- {
- title: "DxcHeader.Dropdown",
- content: (
-
- Everything between the tags will be displayed as a dropdown. If you want to show a{" "}
-
- DxcDropdown
-
- , as a shortcut, you can also use it as a direct child of the DxcHeader without the tags, but we recommend to
- use it with the tags since some styles will be applied for a better fit in the header.
-
- ),
- },
- {
- title: "Examples",
- subSections: [
- {
- title: "Header in application layout",
- content: (
-
- ),
- },
- ],
- },
+ // UPDATE to new sandbox link when available
+ // {
+ // title: "Examples",
+ // subSections: [
+ // {
+ // title: "Header in application layout",
+ // content: (
+ //
+ // ),
+ // },
+ // ],
+ // },
];
const HeaderCodePage = () => {
diff --git a/apps/website/screens/components/header/overview/HeaderOverviewPage.tsx b/apps/website/screens/components/header/overview/HeaderOverviewPage.tsx
index 1558e31f0..aa30d8e96 100644
--- a/apps/website/screens/components/header/overview/HeaderOverviewPage.tsx
+++ b/apps/website/screens/components/header/overview/HeaderOverviewPage.tsx
@@ -1,10 +1,6 @@
import { DxcBulletedList, DxcFlex, DxcParagraph } from "@dxc-technology/halstack-react";
import DocFooter from "@/common/DocFooter";
-import Figure from "@/common/Figure";
-import Image from "@/common/Image";
import QuickNavContainer from "@/common/QuickNavContainer";
-import anatomy from "./images/header_anatomy.png";
-import variants from "./images/header_variants.png";
const sections = [
{
@@ -12,8 +8,8 @@ const sections = [
content: (
The Header serves as the primary navigation and identity element for an application. It includes branding, quick
- access to key sections via navigation links, and a user account menu. Its consistent presence reinforces brand
- recognition and improves usability by offering easy navigation and access to user-related actions.
+ access to key sections via navigation links, and a customizable side content. Its consistent presence reinforces
+ brand recognition and improves usability by offering easy navigation and access to quick actions.
),
},
@@ -21,7 +17,7 @@ const sections = [
title: "Anatomy",
content: (
<>
-
+ {/* */}
Container: a layout structure that wraps all Header elements, ensuring consistent
@@ -40,41 +36,13 @@ const sections = [
Navigation Links(Optional): key links to main sections of the application.
- Header Dropdown(Optional): a dropdown menu for user-specific actions such as
+ Side Content(Optional): a customizable area for user-specific actions such as
profile, settings, or logout, triggered by click or keyboard focus.
-
- Divider(Optional): horizontal line that visually separates the Header from the
- page content below, enhancing layout clarity.
-
>
),
},
- {
- title: "Variants",
- content: (
- <>
-
- To maintain consistency with the way variants are structured across components, the Header offers two primary
- styles: default and underlined.
-
-
-
- The default variant features a clean header without a visual separation from the page
- content, ideal for minimalistic or immersive layouts.
-
-
- The underlined variant includes a subtle bottom divider, creating a clear visual boundary
- between the header and the rest of the page content, enhancing structure and clarity.
-
-
-
-
-
- >
- ),
- },
{
title: "Responsive version",
content: (
@@ -87,7 +55,7 @@ const sections = [
"On smaller screens, the header content is replaced by a button. Triggering this button opens a menu that
- displays custom content."
+ displays navigation links and a bottom section."
>
),
@@ -100,17 +68,12 @@ const sections = [
Keep the Header minimal and functional: include only essential elements.
- Select the correct variant according to visual needs: Use the default{" "}
- variant for simple pages and underlined variant to visually separate the Header from the content when
- necessary.
-
-
- Use dropdowns correctly for complex navigation: Only use Header dropdowns when necessary to
- organize multiple links logically without overwhelming the top navigation.
+ Use navigation links correctly: Only use navigation groups when necessary to organize
+ multiple links logically without overwhelming the top navigation.
Avoid overcrowding the Header: Limit the number of top-level navigation links. Group
- secondary links inside dropdowns if needed to maintain a clean and user-friendly interface.
+ secondary links inside navigation groups if needed to maintain a clean and user-friendly interface.
Display the application name clearly and concisely: The application name should be readable,
@@ -118,7 +81,8 @@ const sections = [
Design the Header to respond gracefully to smaller screens: When adapting the Header to
- mobile or tablet layouts, restructure the content to preserve both visual clarity and functional hierarchy.
+ mobile or tablet layouts, restructure the side content to preserve both visual clarity and functional
+ hierarchy.
),
diff --git a/packages/lib/src/base-menu/types.ts b/packages/lib/src/base-menu/types.ts
index 88ab76958..86d70c0f9 100644
--- a/packages/lib/src/base-menu/types.ts
+++ b/packages/lib/src/base-menu/types.ts
@@ -97,6 +97,7 @@ type BaseMenuContextProps = {
};
export type {
+ CommonItemProps,
BaseMenuContextProps,
GroupItem,
GroupItemProps,
diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx
index e048ab18a..494bb0da6 100644
--- a/packages/lib/src/header/Header.tsx
+++ b/packages/lib/src/header/Header.tsx
@@ -100,7 +100,10 @@ const Overlay = styled.div`
/**
* Prepares the navigation items to be rendered in the header.
+ * Even though the typing does not allow this, the navigation tree does.
+ * So this function limits the levels of navigation to the limit by ignoring any nested group items over that limit.
* @param navItems prop with the navigation items.
+ * @param level current level of recursion.
* @return Processed navigation items with limited levels.
*/
const sanitizeNavItems = (navItems: HeaderProps["navItems"], level?: number): (GroupItem | Item)[] => {
@@ -153,7 +156,11 @@ const DxcHeader = ({ branding, navItems, sideContent, responsiveBottomContent }:
0
- ? ["auto", `minmax(auto, ${MAX_MAIN_NAV_SIZE})`, "auto"]
+ ? [
+ `minmax(auto, calc((100% - ${MAX_MAIN_NAV_SIZE}) / 2))`,
+ `minmax(auto, ${MAX_MAIN_NAV_SIZE})`,
+ `minmax(auto, calc((100% - ${MAX_MAIN_NAV_SIZE}) / 2))`,
+ ]
: ["auto", "auto"]
}
templateRows={["var(--height-xxxl)"]}
@@ -197,7 +204,7 @@ const DxcHeader = ({ branding, navItems, sideContent, responsiveBottomContent }:
)}
{sideContent && (
- {typeof sideContent === "function" ? sideContent(isResponsive) : isResponsive && sideContent}{" "}
+ {typeof sideContent === "function" ? sideContent(isResponsive) : sideContent}{" "}
{isResponsive && }
)}
diff --git a/packages/lib/src/header/types.tsx b/packages/lib/src/header/types.tsx
index 124c94125..10f9da349 100644
--- a/packages/lib/src/header/types.tsx
+++ b/packages/lib/src/header/types.tsx
@@ -1,5 +1,5 @@
import { ReactNode } from "react";
-import { GroupItem, Item } from "../base-menu/types";
+import { CommonItemProps, Item } from "../base-menu/types";
import { SVG } from "../common/utils";
type LogoPropsType = {
@@ -14,14 +14,17 @@ type BrandingPropsType = {
appTitle?: string;
};
+type GroupItem = CommonItemProps & {
+ items: Item[];
+};
+
type MainNavPropsType = (GroupItem | Item)[];
type Props = {
branding: BrandingPropsType;
- responsiveBranding?: BrandingPropsType;
navItems?: MainNavPropsType;
- sideContent?: ReactNode | ((isResponsive: boolean) => ReactNode);
responsiveBottomContent?: ReactNode;
+ sideContent?: ReactNode | ((isResponsive: boolean) => ReactNode);
};
export default Props;
From f4236917455d4f09877a0f64bb568dc0fefd924e Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 12:06:42 +0100
Subject: [PATCH 06/23] Header accessibility test
---
.../src/header/Header.accessibilty.test.tsx | 84 +++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 packages/lib/src/header/Header.accessibilty.test.tsx
diff --git a/packages/lib/src/header/Header.accessibilty.test.tsx b/packages/lib/src/header/Header.accessibilty.test.tsx
new file mode 100644
index 000000000..6a95d78e6
--- /dev/null
+++ b/packages/lib/src/header/Header.accessibilty.test.tsx
@@ -0,0 +1,84 @@
+import { render } from "@testing-library/react";
+import { axe, formatRules } from "../../test/accessibility/axe-helper";
+import DxcHeader from "./Header";
+import rules from "../../test/accessibility/rules/specific/header/disabledRules";
+import { vi } from "vitest";
+import DxcBadge from "../badge/Badge";
+import DxcButton from "../button/Button";
+
+global.ResizeObserver = vi.fn().mockImplementation(() => ({
+ observe: vi.fn(),
+ unobserve: vi.fn(),
+ disconnect: vi.fn(),
+}));
+
+const disabledRules = {
+ rules: formatRules(rules),
+};
+
+const iconSVG = (
+
+);
+
+const iconUrl = "https://iconape.com/wp-content/files/yd/367773/svg/logo-linkedin-logo-icon-png-svg.png";
+
+const branding = {
+ logo: {
+ src: iconSVG,
+ alt: "DXC Logo",
+ href: iconUrl,
+ },
+ appTitle:
+ "Application Title with a very long name that exceeds the normal length to test how the header manages overflow situations",
+};
+
+const items = [
+ {
+ label: "Grouped Item 1",
+ icon: "favorite",
+ items: [
+ { label: "Item 1", icon: "person", selected: true },
+ {
+ label: "Grouped Item 2",
+ items: [
+ {
+ label: "Item 2",
+ icon: "bookmark",
+ badge: ,
+ },
+ { label: "Selected Item 3" },
+ ],
+ },
+ ],
+ badge: ,
+ },
+ { label: "Item 4", icon: "key" },
+ { label: "Item 5", icon: "person" },
+ { label: "Grouped Item 6", items: [{ label: "Item 7", icon: "person" }, { label: "Item 8" }] },
+ { label: "Item 9" },
+];
+
+describe("Header component accessibility tests", () => {
+ beforeAll(() => {
+ Object.defineProperty(window, "matchMedia", {
+ writable: true,
+ value: vi.fn().mockImplementation(() => ({
+ matches: false,
+ })),
+ });
+ });
+ it("Should not have basic accessibility issues", async () => {
+ const { container } = render(
+ {}} />}
+ />
+ );
+ const results = await axe(container, disabledRules);
+ expect(results.violations).toHaveLength(0);
+ });
+});
From ed16f2a60f994ff765c71050feda080794477d2e Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 12:38:37 +0100
Subject: [PATCH 07/23] Trying to fix storybook interaction exception
---
packages/lib/src/header/Header.stories.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index c015ec14f..4028e25af 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -218,7 +218,7 @@ export const Responsive: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- const menuButton = await waitFor(() => canvas.getAllByRole("button")[1]);
- if (menuButton) userEvent.click(menuButton);
+ const menuButtons = await waitFor(() => canvas.getAllByRole("button"));
+ if (menuButtons[1]) userEvent.click(menuButtons[1]);
},
};
From f9bb5e23e9d2f76cb5688d4dc6a38b17fada8c02 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 12:40:42 +0100
Subject: [PATCH 08/23] Title added to button
---
packages/lib/src/header/Header.accessibilty.test.tsx | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/packages/lib/src/header/Header.accessibilty.test.tsx b/packages/lib/src/header/Header.accessibilty.test.tsx
index 6a95d78e6..7ee54278a 100644
--- a/packages/lib/src/header/Header.accessibilty.test.tsx
+++ b/packages/lib/src/header/Header.accessibilty.test.tsx
@@ -75,7 +75,9 @@ describe("Header component accessibility tests", () => {
{}} />}
+ sideContent={
+ {}} />
+ }
/>
);
const results = await axe(container, disabledRules);
From 0546382dd2ad6f815c927488a2dfe65218e5fea5 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 13:03:13 +0100
Subject: [PATCH 09/23] accessibility test correctly named
---
...Header.accessibilty.test.tsx => Header.accessibility.test.tsx} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename packages/lib/src/header/{Header.accessibilty.test.tsx => Header.accessibility.test.tsx} (100%)
diff --git a/packages/lib/src/header/Header.accessibilty.test.tsx b/packages/lib/src/header/Header.accessibility.test.tsx
similarity index 100%
rename from packages/lib/src/header/Header.accessibilty.test.tsx
rename to packages/lib/src/header/Header.accessibility.test.tsx
From c2a305dbc785df50857348cf110a2f758ae04aff Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 13:08:55 +0100
Subject: [PATCH 10/23] title added to buttons in test
---
packages/lib/src/header/Header.stories.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 4028e25af..9f2d7a257 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -130,11 +130,11 @@ const HeaderInLayout = () => (
sideContent={(isResponsive) =>
isResponsive ? (
<>
-
+
>
) : (
<>
-
+
>
From c8a6f6182fcaca17672240e4f8f796adcaaf2b00 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 13:21:34 +0100
Subject: [PATCH 11/23] addin rules exceptions
---
packages/lib/src/header/Header.stories.tsx | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 9f2d7a257..02cc833e8 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -10,6 +10,8 @@ import { dxcLogo } from "./Icons";
import DxcButton from "../button/Button";
import { waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
+import preview from "../../.storybook/preview";
+import disabledRules from "../../test/accessibility/rules/specific/header/disabledRules";
export default {
title: "Header",
@@ -28,6 +30,16 @@ export default {
return ;
},
],
+ parameters: {
+ a11y: {
+ config: {
+ rules: [
+ ...(preview?.parameters?.a11y?.config?.rules || []),
+ ...disabledRules.map((ruleId) => ({ id: ruleId, enabled: false })),
+ ],
+ },
+ },
+ },
} satisfies Meta;
const branding = {
From 55a0c2e144ab7a390182aac63d883f923fde2c7c Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 13:36:20 +0100
Subject: [PATCH 12/23] adding title to missing title in header
---
packages/lib/src/header/Header.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx
index 494bb0da6..43e201c87 100644
--- a/packages/lib/src/header/Header.tsx
+++ b/packages/lib/src/header/Header.tsx
@@ -73,7 +73,7 @@ const MainNavContainer = styled.div`
`;
const HamburguerButton = ({ onClick }: { onClick: () => void }) => {
- return ;
+ return ;
};
const ResponsiveMenuContainer = styled.div`
From d919166ab4dc64b6c4818e65445530ace5c6fb3b Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 13:48:42 +0100
Subject: [PATCH 13/23] changing userEvent to storybook's one
---
packages/lib/src/header/Header.stories.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 02cc833e8..34448da43 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -8,8 +8,7 @@ import DxcApplicationLayout from "../layout/ApplicationLayout";
import DxcParagraph from "../paragraph/Paragraph";
import { dxcLogo } from "./Icons";
import DxcButton from "../button/Button";
-import { waitFor, within } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
+import { waitFor, userEvent, within } from "storybook/internal/test";
import preview from "../../.storybook/preview";
import disabledRules from "../../test/accessibility/rules/specific/header/disabledRules";
@@ -231,6 +230,6 @@ export const Responsive: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const menuButtons = await waitFor(() => canvas.getAllByRole("button"));
- if (menuButtons[1]) userEvent.click(menuButtons[1]);
+ if (menuButtons[1]) await userEvent.click(menuButtons[1]);
},
};
From d9b5760e5bea9083893eda8e3156252231d1ba5c Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 13:54:36 +0100
Subject: [PATCH 14/23] adding wait to render menu
---
packages/lib/src/header/Header.stories.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 34448da43..9537a4542 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -231,5 +231,6 @@ export const Responsive: Story = {
const canvas = within(canvasElement);
const menuButtons = await waitFor(() => canvas.getAllByRole("button"));
if (menuButtons[1]) await userEvent.click(menuButtons[1]);
+ await waitFor(() => canvas.getByText("Bottom content button"));
},
};
From 3469d39b2eef84ae83f36a2994d8a3e4cd6b61fc Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 14:01:52 +0100
Subject: [PATCH 15/23] trying to fix inconsistencies in actions
---
packages/lib/src/header/Header.stories.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 9537a4542..84c6376fc 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -230,7 +230,11 @@ export const Responsive: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const menuButtons = await waitFor(() => canvas.getAllByRole("button"));
- if (menuButtons[1]) await userEvent.click(menuButtons[1]);
+ if (menuButtons[1]) {
+ await userEvent.click(menuButtons[1]);
+ await userEvent.click(menuButtons[1]);
+ await userEvent.click(menuButtons[1]);
+ }
await waitFor(() => canvas.getByText("Bottom content button"));
},
};
From 214988bcab3193173911033e7f9aceb83c2b9ffa Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 14:13:53 +0100
Subject: [PATCH 16/23] adding steps to test
---
packages/lib/src/header/Header.stories.tsx | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 84c6376fc..7337d4125 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -8,7 +8,7 @@ import DxcApplicationLayout from "../layout/ApplicationLayout";
import DxcParagraph from "../paragraph/Paragraph";
import { dxcLogo } from "./Icons";
import DxcButton from "../button/Button";
-import { waitFor, userEvent, within } from "storybook/internal/test";
+import { userEvent, within } from "storybook/internal/test";
import preview from "../../.storybook/preview";
import disabledRules from "../../test/accessibility/rules/specific/header/disabledRules";
@@ -227,14 +227,17 @@ export const Responsive: Story = {
globals: {
viewport: { value: "iphonex", isRotated: false },
},
- play: async ({ canvasElement }) => {
+ play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
- const menuButtons = await waitFor(() => canvas.getAllByRole("button"));
- if (menuButtons[1]) {
- await userEvent.click(menuButtons[1]);
- await userEvent.click(menuButtons[1]);
- await userEvent.click(menuButtons[1]);
- }
- await waitFor(() => canvas.getByText("Bottom content button"));
+ await step("Open Menu", async () => {
+ const buttons = await canvas.findAllByRole("button");
+ if (buttons[1]) {
+ await userEvent.click(buttons[1]);
+ }
+ });
+
+ await step("Wait for content", async () => {
+ await canvas.findByText("Bottom content button");
+ });
},
};
From 235ed9f9b9c8386e276f667450e2709f3b24b43a Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 14:24:33 +0100
Subject: [PATCH 17/23] Trying to improve inconsistencies in the test
---
packages/lib/src/header/Header.stories.tsx | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 7337d4125..736ffd44f 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -8,7 +8,7 @@ import DxcApplicationLayout from "../layout/ApplicationLayout";
import DxcParagraph from "../paragraph/Paragraph";
import { dxcLogo } from "./Icons";
import DxcButton from "../button/Button";
-import { userEvent, within } from "storybook/internal/test";
+import { screen, userEvent, within } from "storybook/internal/test";
import preview from "../../.storybook/preview";
import disabledRules from "../../test/accessibility/rules/specific/header/disabledRules";
@@ -227,17 +227,14 @@ export const Responsive: Story = {
globals: {
viewport: { value: "iphonex", isRotated: false },
},
- play: async ({ canvasElement, step }) => {
+ play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- await step("Open Menu", async () => {
- const buttons = await canvas.findAllByRole("button");
- if (buttons[1]) {
- await userEvent.click(buttons[1]);
- }
- });
-
- await step("Wait for content", async () => {
- await canvas.findByText("Bottom content button");
- });
+ const buttons = await canvas.findAllByRole("button");
+ const secondButton = buttons[1];
+ if (!secondButton) {
+ throw new Error("Button not found");
+ }
+ await userEvent.click(secondButton);
+ await screen.findByText("Bottom content button");
},
};
From c814b76a502fba3b3e877719fdda880cbf6bd777 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 14:39:22 +0100
Subject: [PATCH 18/23] changing actions to tabs
---
packages/lib/src/header/Header.stories.tsx | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 736ffd44f..2aa101ff8 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -8,7 +8,7 @@ import DxcApplicationLayout from "../layout/ApplicationLayout";
import DxcParagraph from "../paragraph/Paragraph";
import { dxcLogo } from "./Icons";
import DxcButton from "../button/Button";
-import { screen, userEvent, within } from "storybook/internal/test";
+import { userEvent, within } from "storybook/internal/test";
import preview from "../../.storybook/preview";
import disabledRules from "../../test/accessibility/rules/specific/header/disabledRules";
@@ -229,12 +229,9 @@ export const Responsive: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- const buttons = await canvas.findAllByRole("button");
- const secondButton = buttons[1];
- if (!secondButton) {
- throw new Error("Button not found");
- }
- await userEvent.click(secondButton);
- await screen.findByText("Bottom content button");
+ await userEvent.tab();
+ await userEvent.tab();
+ await userEvent.keyboard("{Enter}");
+ await canvas.findByText("Bottom content button");
},
};
From 5226a0addb25df1e8ee6c23e1baa291b08f9b774 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 14:48:20 +0100
Subject: [PATCH 19/23] added settimeout
---
packages/lib/src/header/Header.stories.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 2aa101ff8..4edc1712b 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -230,6 +230,7 @@ export const Responsive: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.tab();
+ await new Promise((resolve) => setTimeout(resolve, 100));
await userEvent.tab();
await userEvent.keyboard("{Enter}");
await canvas.findByText("Bottom content button");
From 942adb311371cbff9816f220e9b61f92897854ef Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 14:56:59 +0100
Subject: [PATCH 20/23] increasing wait
---
packages/lib/src/header/Header.stories.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 4edc1712b..34885e585 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -230,7 +230,7 @@ export const Responsive: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.tab();
- await new Promise((resolve) => setTimeout(resolve, 100));
+ await new Promise((resolve) => setTimeout(resolve, 1000));
await userEvent.tab();
await userEvent.keyboard("{Enter}");
await canvas.findByText("Bottom content button");
From 2f9c3410498b95ee9fe2479e345d46fc33f4aef9 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Thu, 13 Nov 2025 15:03:44 +0100
Subject: [PATCH 21/23] additional wait time
---
packages/lib/src/header/Header.stories.tsx | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/lib/src/header/Header.stories.tsx b/packages/lib/src/header/Header.stories.tsx
index 34885e585..ff4c847ee 100644
--- a/packages/lib/src/header/Header.stories.tsx
+++ b/packages/lib/src/header/Header.stories.tsx
@@ -229,8 +229,9 @@ export const Responsive: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
+ await new Promise((resolve) => setTimeout(resolve, 100));
await userEvent.tab();
- await new Promise((resolve) => setTimeout(resolve, 1000));
+ await new Promise((resolve) => setTimeout(resolve, 100));
await userEvent.tab();
await userEvent.keyboard("{Enter}");
await canvas.findByText("Bottom content button");
From fefeceef571ed0ff4d0608b546673823f885217e Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Fri, 14 Nov 2025 11:25:30 +0100
Subject: [PATCH 22/23] submenu styles on popover + conditional header check
---
packages/lib/src/base-menu/SubMenu.tsx | 6 ++++--
packages/lib/src/layout/ApplicationLayout.tsx | 6 +++---
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/packages/lib/src/base-menu/SubMenu.tsx b/packages/lib/src/base-menu/SubMenu.tsx
index 106656e9c..f7114b96c 100644
--- a/packages/lib/src/base-menu/SubMenu.tsx
+++ b/packages/lib/src/base-menu/SubMenu.tsx
@@ -18,10 +18,12 @@ const SubMenuContainer = styled.ul<{
${({ isPopOver }) =>
isPopOver &&
`
- max-width: 240px;
+ min-width: 200px;
+ max-width: 320px;
+ padding: var(--spacing-padding-xs);
background-color: var(--color-bg-neutral-lightest);
border-radius: var(--border-radius-m);
- box-shadow: var(--shadow-200);
+ box-shadow: var(--shadow-100);
`}
${({ depthLevel, displayGroupLines }) =>
displayGroupLines &&
diff --git a/packages/lib/src/layout/ApplicationLayout.tsx b/packages/lib/src/layout/ApplicationLayout.tsx
index 844189871..890c5b5c7 100644
--- a/packages/lib/src/layout/ApplicationLayout.tsx
+++ b/packages/lib/src/layout/ApplicationLayout.tsx
@@ -6,11 +6,11 @@ import DxcSidenav from "../sidenav/Sidenav";
import ApplicationLayoutPropsType, { AppLayoutMainPropsType } from "./types";
import { bottomLinks, findChildType, socialLinks, year } from "./utils";
-const ApplicationLayoutContainer = styled.div`
+const ApplicationLayoutContainer = styled.div<{ header?: React.ReactNode }>`
top: 0;
left: 0;
display: grid;
- grid-template-rows: auto 1fr;
+ grid-template-rows: ${({ header }) => (header ? "auto 1fr" : "1fr")};
height: 100vh;
width: 100vw;
position: absolute;
@@ -67,7 +67,7 @@ const DxcApplicationLayout = ({ header, sidenav, footer, children }: Application
const ref = useRef(null);
return (
-
+
{header && {header}}
{sidenav && {sidenav}}
From 52152a152af2807b24c1abd43758e9f485ed4b49 Mon Sep 17 00:00:00 2001
From: Jialecl
Date: Fri, 14 Nov 2025 12:44:35 +0100
Subject: [PATCH 23/23] changes based on feedback
---
.../components/header/code/HeaderCodePage.tsx | 18 ++++++++----------
.../lib/src/header/{types.tsx => types.ts} | 0
2 files changed, 8 insertions(+), 10 deletions(-)
rename packages/lib/src/header/{types.tsx => types.ts} (100%)
diff --git a/apps/website/screens/components/header/code/HeaderCodePage.tsx b/apps/website/screens/components/header/code/HeaderCodePage.tsx
index 4122fb4ca..2f1b3d484 100644
--- a/apps/website/screens/components/header/code/HeaderCodePage.tsx
+++ b/apps/website/screens/components/header/code/HeaderCodePage.tsx
@@ -10,26 +10,24 @@ const brandingTypeString = `{
alt: string;
href?: string;
onClick?: () => void;
- };
+ };
appTitle?: string;
}`;
const navItemsTypeString = `(GroupItem | Item)[]`;
-const commonItemTypeString = `{
+const itemTypeString = `{
badge?: ReactElement;
icon?: string | SVG;
label: string;
-}`;
-
-const itemTypeString = `{
- ${commonItemTypeString}
onSelect?: () => void;
selected?: boolean;
}`;
-const groupItemTypeString = `{
- ${commonItemTypeString}
+const groupItemTypeString = `{
+ badge?: ReactElement;
+ icon?: string | SVG;
+ label: string;
items: (Item)[];
}`;
@@ -99,8 +97,8 @@ const sections = [
{"React.ReactNode | (isResponsive: boolean) => React.ReactNode"}
- Content to be displayed on the right side of the header. It can be a React node or a function that
- receives a boolean indicating if the header is in responsive mode and returns a React node.
+ Content to be displayed on the right side of the header. It can be a ReactNode or a function that receives
+ a boolean indicating if the header is in responsive mode and returns a ReactNode.
diff --git a/packages/lib/src/header/types.tsx b/packages/lib/src/header/types.ts
similarity index 100%
rename from packages/lib/src/header/types.tsx
rename to packages/lib/src/header/types.ts