Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion apps/website/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default function App({ Component, pageProps, emotionCache = clientSideEmo
sidenav={
<DxcApplicationLayout.Sidenav
navItems={navItems}
appTitle={isExpanded && <SidenavLogo />}
appTitle={<SidenavLogo />}
searchBar={{ placeholder: "Search docs", onChange: (value) => setFilter(value) }}
expanded={isExpanded}
onExpandedChange={() => {
Expand Down
30 changes: 27 additions & 3 deletions packages/lib/src/base-menu/GroupItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ const GroupItem = ({ items, ...props }: GroupItemProps) => {
const contextValue = useContext(BaseMenuContext) ?? {};
const { groupSelected, isOpen, toggleOpen, hasPopOver, isHorizontal } = useGroupItem(items, contextValue);

// TODO: SET A FIXED WIDTH TO PREVENT MOVING CONTENT WHEN EXPANDING/COLLAPSING IN RESPONSIVEVIEW
return hasPopOver ? (
<>
<Popover.Root open={isOpen}>
Expand Down Expand Up @@ -47,10 +46,35 @@ const GroupItem = ({ items, ...props }: GroupItemProps) => {
}}
align="start"
side={isHorizontal ? "bottom" : "right"}
style={{ zIndex: "var(--z-contextualmenu)" }}
sideOffset={isHorizontal ? 16 : 0}
style={{
zIndex: "var(--z-contextualmenu)",
padding: "var(--spacing-padding-xs)",
boxShadow: "var(--shadow-100)",
backgroundColor: "var(--color-bg-neutral-lightest)",
borderRadius: "var(--border-radius-m)",
...(isHorizontal
? {}
: {
display: "flex",
flexDirection: "column",
gap: "var(--spacing-gap-xxs)",
}),
}}
sideOffset={16}
onInteractOutside={isHorizontal ? () => toggleOpen() : undefined}
>
{!isHorizontal && props.depthLevel === 0 && (
<ItemAction
aria-controls={isOpen ? groupMenuId : undefined}
aria-expanded={isOpen ? true : undefined}
aria-pressed={groupSelected && !isOpen}
collapseIcon={isOpen ? <DxcIcon icon="filled_expand_less" /> : <DxcIcon icon="filled_expand_more" />}
onClick={() => toggleOpen()}
selected={groupSelected && !isOpen}
{...props}
icon={undefined}
/>
)}
<SubMenu id={groupMenuId} depthLevel={props.depthLevel} isPopOver={true}>
{items.map((item, index) => (
<MenuItem
Expand Down
2 changes: 1 addition & 1 deletion packages/lib/src/base-menu/MenuItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isGroupItem } from "./utils";

const MenuItemContainer = styled.li`
display: grid;
gap: var(--spacing-gap-xs);
margin-right: var(--spacing-padding-xxs);
`;

export default function MenuItem({ item, depthLevel = 0 }: MenuItemProps) {
Expand Down
19 changes: 7 additions & 12 deletions packages/lib/src/base-menu/SubMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,18 @@ const SubMenuContainer = styled.ul<{
flex-direction: ${({ isHorizontal }) => (isHorizontal ? "row" : "column")};
gap: ${({ isHorizontal }) => (isHorizontal ? "var(--spacing-gap-s)" : "var(--spacing-gap-xs)")};
list-style: none;
${({ isPopOver }) =>
isPopOver &&
`
${({ depthLevel, displayGroupLines, isPopOver }) =>
isPopOver
? `
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-100);
`}
${({ depthLevel, displayGroupLines }) =>
displayGroupLines &&
depthLevel >= 0 &&
`
: displayGroupLines &&
depthLevel >= 0 &&
`
margin-left: calc(var(--spacing-padding-m) + ${depthLevel} * var(--spacing-padding-xs));
border-left: var(--border-width-s) solid var(--border-color-neutral-lighter);
`};
`}
`;

export default function SubMenu({
Expand Down
17 changes: 14 additions & 3 deletions packages/lib/src/layout/ApplicationLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import DxcSidenav from "../sidenav/Sidenav";
import ApplicationLayoutPropsType, { AppLayoutMainPropsType } from "./types";
import { bottomLinks, findChildType, socialLinks, year } from "./utils";
import ApplicationLayoutContext from "./ApplicationLayoutContext";
import { responsiveSizes } from "../common/variables";

const ApplicationLayoutContainer = styled.div<{ header?: React.ReactNode }>`
top: 0;
Expand All @@ -25,19 +26,29 @@ const HeaderContainer = styled.div`
z-index: var(--z-app-layout-header);
`;

const BodyContainer = styled.div<{ hasSidenav?: boolean }>`
const BodyContainer = styled.div<{ hasSidenav: boolean }>`
display: grid;
grid-template-columns: ${({ hasSidenav }) => (hasSidenav ? "auto 1fr" : "1fr")};
grid-template-rows: 1fr;
overflow: hidden;

@media (max-width: ${responsiveSizes.medium}rem) {
grid-template-columns: 1fr;
grid-template-rows: auto 1fr;
overflow-y: auto;
}
`;

const SidenavContainer = styled.div`
width: fit-content;
height: 100%;
z-index: var(--z-app-layout-sidenav);
position: sticky;
overflow: auto;
z-index: var(--z-app-layout-sidenav);
height: 100%;

@media (max-width: ${responsiveSizes.medium}rem) {
width: 100%;
}
`;

const MainContainer = styled.div`
Expand Down
1 change: 0 additions & 1 deletion packages/lib/src/navigation-tree/NavigationTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const NavigationTreeContainer = styled.div<{ displayBorder: boolean }>`
margin: 0;
display: grid;
gap: var(--spacing-gap-xs);
/* min-width: 248px; */
max-height: 100%;
background-color: var(--color-bg-neutral-lightest);
overflow-y: auto;
Expand Down
10 changes: 10 additions & 0 deletions packages/lib/src/sidenav/Sidenav.accessibility.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ global.ResizeObserver = vi.fn().mockImplementation(() => ({
}));

describe("Sidenav component accessibility tests", () => {
beforeAll(() => {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: vi.fn().mockImplementation(() => ({
matches: false,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
})),
});
});
it("Should not have basic accessibility issues", async () => {
const groupItems = [
{
Expand Down
114 changes: 112 additions & 2 deletions packages/lib/src/sidenav/Sidenav.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { userEvent, within } from "storybook/internal/test";
import disabledRules from "../../test/accessibility/rules/specific/sidenav/disabledRules";
import preview from "../../.storybook/preview";
import { useState } from "react";
import DxcApplicationLayout from "../layout/ApplicationLayout";
import DxcParagraph from "../paragraph/Paragraph";

export default {
title: "Sidenav",
Expand All @@ -34,15 +36,15 @@ const DetailedAvatar = () => {
<DxcAvatar color="primary" status={{ mode: "error", position: "bottom" }} title="Michael Ramirez" />
<DxcFlex direction="column">
<DxcTypography
color="var(--color-fg-neutral-dark"
color="var(--color-fg-neutral-dark)"
fontFamily="var(--typography-font-family)"
fontSize="var(--typography-label-l)"
fontWeight="var(--typography-label-regular)"
>
Michael Ramirez
</DxcTypography>
<DxcTypography
color="var(--color-fg-neutral-stronger"
color="var(--color-fg-neutral-stronger)"
fontFamily="var(--typography-font-family)"
fontSize="var(--typography-label-s)"
fontWeight="var(--typography-label-regular)"
Expand Down Expand Up @@ -133,6 +135,31 @@ const selectedGroupItems = [
},
];

const dxcLogo = (
<svg xmlns="http://www.w3.org/2000/svg" width="73" height="40" viewBox="0 0 73 40">
<title>DXC Logo</title>
<g transform="translate(0)">
<g>
<path
d="M91.613-28.177v2.514H90.231V-28.15l-2.415-3.82h1.616l1.5,2.532,1.526-2.532h1.571ZM83.9-25.555A3.15,3.15,0,0,1,80.6-28.8v-.018a3.231,3.231,0,0,1,3.294-3.262,3.442,3.442,0,0,1,2.469.865l-.87,1.054a2.311,2.311,0,0,0-1.643-.64,1.891,1.891,0,0,0-1.8,1.964v.018a1.886,1.886,0,0,0,1.9,2,2.2,2.2,0,0,0,1.3-.378v-.9H83.858v-1.2h2.729v2.738A4.071,4.071,0,0,1,83.9-25.555Zm-6.416-3.261a1.913,1.913,0,0,0-1.9-1.982A1.883,1.883,0,0,0,73.7-28.835v.018a1.913,1.913,0,0,0,1.9,1.982A1.883,1.883,0,0,0,77.486-28.8Zm-1.9,3.261a3.225,3.225,0,0,1-3.33-3.243v-.018A3.255,3.255,0,0,1,75.6-32.078a3.225,3.225,0,0,1,3.331,3.243v.018A3.255,3.255,0,0,1,75.583-25.555Zm-9.173-.108V-31.97h1.382v5.045h3.133v1.261Zm-3.433-3.153a1.913,1.913,0,0,0-1.9-1.982,1.883,1.883,0,0,0-1.886,1.964v.018a1.913,1.913,0,0,0,1.9,1.982A1.883,1.883,0,0,0,62.978-28.8Zm-1.9,3.261a3.225,3.225,0,0,1-3.33-3.243v-.018a3.255,3.255,0,0,1,3.348-3.262,3.225,3.225,0,0,1,3.331,3.243v.018A3.255,3.255,0,0,1,61.075-25.555Zm-6.508-.108-3.043-4.009v4.009H50.159V-31.97h1.275l2.944,3.883V-31.97h1.364v6.306Zm-8.246,0v-2.531h-2.55v2.531H42.389V-31.97h1.382v2.5h2.55v-2.5H47.7v6.306Zm-8.432.108A3.178,3.178,0,0,1,34.666-28.8v-.018a3.2,3.2,0,0,1,3.276-3.262,3.237,3.237,0,0,1,2.478.973l-.88,1.018a2.315,2.315,0,0,0-1.606-.712,1.866,1.866,0,0,0-1.822,1.964v.018a1.87,1.87,0,0,0,1.822,1.982,2.265,2.265,0,0,0,1.651-.739l.88.891A3.206,3.206,0,0,1,37.889-25.555Zm-9.805-.108V-31.97h4.739v1.235H29.458v1.279h2.962v1.234H29.458V-26.9h3.411v1.234ZM24.322-30.69v5.027H22.939V-30.69H21.028v-1.28h5.206v1.28H24.322"
transform="translate(-21.028 65.555)"
fill="#010101"
/>
<path
d="M75.836-76.712a8.975,8.975,0,0,1,2.246-3.9,8.393,8.393,0,0,1,6.058-2.457h9.824v-5.67H84.139a14.611,14.611,0,0,0-10.232,4.221,14.509,14.509,0,0,0-3.076,4.536,11.913,11.913,0,0,0-.973,3.271Zm0,4.325a8.978,8.978,0,0,0,2.246,3.9,8.394,8.394,0,0,0,6.058,2.457h9.824v5.67H84.139A14.611,14.611,0,0,1,73.907-64.58a14.506,14.506,0,0,1-3.076-4.536,11.91,11.91,0,0,1-.973-3.271ZM57.522-69.832l-7.5,9.473H42.581L53.818-74.55,42.581-88.739H50.02l7.5,9.472,7.5-9.472h7.439L61.225-74.55l11.237,14.19H65.023Zm-12.336-6.88a11.935,11.935,0,0,0-.973-3.271,14.515,14.515,0,0,0-3.076-4.536A14.612,14.612,0,0,0,30.9-88.739H21.081v5.67H30.9a8.394,8.394,0,0,1,6.058,2.457,8.978,8.978,0,0,1,2.246,3.9Zm0,4.325a11.932,11.932,0,0,1-.973,3.271,14.511,14.511,0,0,1-3.076,4.536A14.611,14.611,0,0,1,30.9-60.359H21.081v-5.67H30.9a8.4,8.4,0,0,0,6.058-2.457,8.981,8.981,0,0,0,2.246-3.9h5.978"
transform="translate(-21.049 88.739)"
fill="#603494"
/>
</g>
</g>
</svg>
);

const dxcBrandedLogo = {
src: dxcLogo,
alt: "DXC Logo",
};

const Sidenav = () => (
<>
<ExampleContainer>
Expand Down Expand Up @@ -437,6 +464,89 @@ const SelectedGroup = () => (
/>
</ExampleContainer>
);

const SidenavInLayout = () => (
<DxcApplicationLayout
logo={dxcBrandedLogo}
sidenav={
<DxcApplicationLayout.Sidenav
appTitle="Application Layout with Sidenav"
navItems={groupItems}
searchBar={{ placeholder: "Search..." }}
/>
}
>
<DxcApplicationLayout.Main>
<DxcParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultrices fermentum ante et pharetra. Integer
ullamcorper ante non laoreet suscipit. Integer pharetra viverra nunc, quis fermentum urna eleifend eget.
Maecenas dolor justo, ullamcorper ac posuere tincidunt, dictum id urna. Suspendisse est metus, euismod et felis
eget, condimentum elementum eros. Curabitur ut lorem ut odio volutpat lacinia. Interdum et malesuada fames ac
ante ipsum primis in faucibus. Sed leo quam, lobortis in ultricies ac, interdum in sem. Suspendisse magna enim,
rhoncus eget lectus vitae, rutrum interdum ligula. Nunc efficitur neque ac orci pretium lacinia. Proin sagittis
condimentum mi, eu dapibus quam faucibus eget. Aenean fermentum nisl ut mauris convallis, in imperdiet neque
porttitor. Aliquam erat volutpat. Fusce tincidunt arcu id arcu dignissim viverra. Sed imperdiet vitae odio eget
consequat. Vivamus eu dictum orci.
</DxcParagraph>
<DxcParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultrices fermentum ante et pharetra. Integer
ullamcorper ante non laoreet suscipit. Integer pharetra viverra nunc, quis fermentum urna eleifend eget.
Maecenas dolor justo, ullamcorper ac posuere tincidunt, dictum id urna. Suspendisse est metus, euismod et felis
eget, condimentum elementum eros. Curabitur ut lorem ut odio volutpat lacinia. Interdum et malesuada fames ac
ante ipsum primis in faucibus. Sed leo quam, lobortis in ultricies ac, interdum in sem. Suspendisse magna enim,
rhoncus eget lectus vitae, rutrum interdum ligula. Nunc efficitur neque ac orci pretium lacinia. Proin sagittis
condimentum mi, eu dapibus quam faucibus eget. Aenean fermentum nisl ut mauris convallis, in imperdiet neque
porttitor. Aliquam erat volutpat. Fusce tincidunt arcu id arcu dignissim viverra. Sed imperdiet vitae odio eget
consequat. Vivamus eu dictum orci.
</DxcParagraph>
<DxcParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultrices fermentum ante et pharetra. Integer
ullamcorper ante non laoreet suscipit. Integer pharetra viverra nunc, quis fermentum urna eleifend eget.
Maecenas dolor justo, ullamcorper ac posuere tincidunt, dictum id urna. Suspendisse est metus, euismod et felis
eget, condimentum elementum eros. Curabitur ut lorem ut odio volutpat lacinia. Interdum et malesuada fames ac
ante ipsum primis in faucibus. Sed leo quam, lobortis in ultricies ac, interdum in sem. Suspendisse magna enim,
rhoncus eget lectus vitae, rutrum interdum ligula. Nunc efficitur neque ac orci pretium lacinia. Proin sagittis
condimentum mi, eu dapibus quam faucibus eget. Aenean fermentum nisl ut mauris convallis, in imperdiet neque
porttitor. Aliquam erat volutpat. Fusce tincidunt arcu id arcu dignissim viverra. Sed imperdiet vitae odio eget
consequat. Vivamus eu dictum orci.
</DxcParagraph>
<DxcParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec ultrices fermentum ante et pharetra. Integer
ullamcorper ante non laoreet suscipit. Integer pharetra viverra nunc, quis fermentum urna eleifend eget.
Maecenas dolor justo, ullamcorper ac posuere tincidunt, dictum id urna. Suspendisse est metus, euismod et felis
eget, condimentum elementum eros. Curabitur ut lorem ut odio volutpat lacinia. Interdum et malesuada fames ac
ante ipsum primis in faucibus. Sed leo quam, lobortis in ultricies ac, interdum in sem. Suspendisse magna enim,
rhoncus eget lectus vitae, rutrum interdum ligula. Nunc efficitur neque ac orci pretium lacinia. Proin sagittis
condimentum mi, eu dapibus quam faucibus eget. Aenean fermentum nisl ut mauris convallis, in imperdiet neque
porttitor. Aliquam erat volutpat. Fusce tincidunt arcu id arcu dignissim viverra. Sed imperdiet vitae odio eget
consequat. Vivamus eu dictum orci.
</DxcParagraph>
</DxcApplicationLayout.Main>
</DxcApplicationLayout>
);

export const InLayout: Story = {
render: SidenavInLayout,
};

export const Responsive: Story = {
render: SidenavInLayout,
parameters: {
chromatic: { viewports: [375] },
},
globals: {
viewport: { value: "iphonex", isRotated: false },
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await new Promise<void>((resolve) => setTimeout(resolve, 100));
await canvas.findByLabelText("Expand");
await userEvent.tab();
await userEvent.keyboard("{Enter}");
await canvas.findByLabelText("Collapse");
},
};

type Story = StoryObj<typeof DxcSidenav>;

export const Chromatic: Story = {
Expand Down
15 changes: 14 additions & 1 deletion packages/lib/src/sidenav/Sidenav.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,21 @@ global.ResizeObserver = jest.fn().mockImplementation(() => ({
}));

describe("DxcSidenav component", () => {
const mockMatchMedia = jest.fn();

beforeAll(() => {
Object.defineProperty(window, "matchMedia", {
writable: true,
value: mockMatchMedia,
});
});

beforeEach(() => {
jest.clearAllMocks();
mockMatchMedia.mockImplementation(() => ({
matches: false,
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
}));
});

test("Sidenav renders title and children correctly", () => {
Expand Down
Loading