Skip to content

Commit 73a7451

Browse files
authored
Merge pull request #2382 from dxc-technology/PelayoFelgueroso/footer-width
[patch] Change Footer width on ApplicationLayout to span the full width of the page
2 parents 327b6af + 9c9fc6c commit 73a7451

9 files changed

Lines changed: 85 additions & 51 deletions

File tree

apps/website/screens/components/application-layout/overview/ApplicationLayoutOverviewPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ const sections = [
248248
multiples of 8px for visual harmony.
249249
</DxcBulletedList.Item>
250250
<DxcBulletedList.Item>
251-
<strong>imit 4px adjustments:</strong> Reserve finer increments only for edge cases like icon alignment or
251+
<strong>Imit 4px adjustments:</strong> Reserve finer increments only for edge cases like icon alignment or
252252
typography.
253253
</DxcBulletedList.Item>
254254
<DxcBulletedList.Item>

apps/website/screens/components/footer/code/FooterCodePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const bottomLinksTypeString = `{
1010
}[]`;
1111

1212
const logoTypeString = `{
13-
src: string;
13+
src: string | SVG;
1414
alt: string;
1515
}`;
1616

packages/lib/src/footer/Footer.stories.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import DxcFooter from "./Footer";
77
import DxcLink from "../link/Link";
88
import { Meta, StoryObj } from "@storybook/react-vite";
99
import { userEvent, within } from "storybook/internal/test";
10+
import { useEffect } from "react";
1011
import DxcParagraph from "../paragraph/Paragraph";
1112
import DxcHeading from "../heading/Heading";
1213
import DxcApplicationLayout from "../layout/ApplicationLayout";
@@ -17,6 +18,15 @@ import DxcButton from "../button/Button";
1718
export default {
1819
title: "Footer",
1920
component: DxcFooter,
21+
decorators: [
22+
(Story) => {
23+
useEffect(() => {
24+
document.body.style.padding = "0";
25+
}, []);
26+
27+
return <Story />;
28+
},
29+
],
2030
parameters: {
2131
a11y: {
2232
config: {

packages/lib/src/footer/Footer.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useContext, useEffect, useMemo, useRef, useState } from "react";
1+
import { isValidElement, useContext, useEffect, useMemo, useRef, useState } from "react";
22
import styled from "@emotion/styled";
33
import DxcIcon from "../icon/Icon";
44
import { Tooltip } from "../tooltip/Tooltip";
@@ -65,6 +65,11 @@ const LogoContainer = styled.span<{ mode?: FooterPropsType["mode"] }>`
6565
justify-content: flex-start;
6666
align-items: center;
6767
`}
68+
69+
svg {
70+
height: ${(props) => (props.mode === "default" ? "var(--height-m)" : "var(--height-xxs)")};
71+
width: auto;
72+
}
6873
`;
6974

7075
const LogoImg = styled.img<{ mode: FooterPropsType["mode"] }>`
@@ -213,8 +218,10 @@ const DxcFooter = ({
213218
const translatedLabels = useContext(HalstackLanguageContext);
214219

215220
const footerLogo = useMemo(() => {
216-
if (logo) {
221+
if (logo && typeof logo.src === "string") {
217222
return <LogoImg mode={mode} alt={logo.alt} src={logo.src} title={logo.alt} />;
223+
} else if (isValidElement(logo?.src)) {
224+
return logo.src;
218225
} else {
219226
return mode === "default" ? dxcLogo : dxcSmallLogo;
220227
}

packages/lib/src/footer/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type Logo = {
3131
/**
3232
* Source of the logo image.
3333
*/
34-
src: string;
34+
src: string | SVG;
3535
/**
3636
* Alternative text for the logo image.
3737
*/

packages/lib/src/layout/ApplicationLayout.stories.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,20 @@ import { Meta, StoryObj } from "@storybook/react-vite";
22
import Title from "../../.storybook/components/Title";
33
import DxcApplicationLayout from "./ApplicationLayout";
44
import { userEvent, within } from "storybook/internal/test";
5+
import { useEffect } from "react";
56

67
export default {
78
title: "Application Layout",
89
component: DxcApplicationLayout,
10+
decorators: [
11+
(Story) => {
12+
useEffect(() => {
13+
document.body.style.padding = "0";
14+
}, []);
15+
16+
return <Story />;
17+
},
18+
],
919
} satisfies Meta<typeof DxcApplicationLayout>;
1020

1121
const dxcLogo = (

packages/lib/src/layout/ApplicationLayout.tsx

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo, useRef } from "react";
1+
import React, { useMemo, useRef, useState, useCallback } from "react";
22
import styled from "@emotion/styled";
33
import DxcFooter from "../footer/Footer";
44
import DxcHeader from "../header/Header";
@@ -8,62 +8,64 @@ import { bottomLinks, findChildType, socialLinks, year } from "./utils";
88
import ApplicationLayoutContext from "./ApplicationLayoutContext";
99

1010
const ApplicationLayoutContainer = styled.div<{ header?: React.ReactNode }>`
11-
top: 0;
12-
left: 0;
1311
display: grid;
14-
grid-template-rows: ${({ header }) => (header ? "auto 1fr" : "1fr")};
15-
height: 100vh;
16-
width: 100vw;
17-
position: absolute;
18-
overflow: hidden;
12+
grid-template-rows: ${({ header }) => (header ? "auto 1fr auto" : "1fr auto")};
13+
min-height: 100vh;
1914
`;
2015

2116
const HeaderContainer = styled.div`
17+
position: sticky;
18+
top: 0;
2219
width: 100%;
23-
min-height: var(--height-xxxl);
2420
height: fit-content;
2521
z-index: var(--z-app-layout-header);
2622
`;
2723

2824
const BodyContainer = styled.div<{ hasSidenav?: boolean }>`
25+
position: relative;
2926
display: grid;
3027
grid-template-columns: ${({ hasSidenav }) => (hasSidenav ? "auto 1fr" : "1fr")};
3128
grid-template-rows: 1fr;
32-
overflow: hidden;
29+
min-height: 100%;
3330
`;
3431

35-
const SidenavContainer = styled.div`
32+
const SidenavContainer = styled.div<{ headerHeight: string }>`
3633
width: fit-content;
3734
height: 100%;
3835
z-index: var(--z-app-layout-sidenav);
3936
position: sticky;
37+
top: ${({ headerHeight }) => headerHeight || "0"};
4038
overflow: auto;
39+
max-height: ${({ headerHeight }) => `calc(100vh - ${headerHeight || "0"})`};
4140
`;
4241

43-
const MainContainer = styled.div`
44-
display: flex;
45-
flex-grow: 1;
46-
flex-direction: column;
42+
const MainContainer = styled.main`
43+
position: relative;
44+
display: grid;
4745
width: 100%;
4846
height: 100%;
49-
position: relative;
50-
overflow: auto;
5147
`;
5248

5349
const FooterContainer = styled.div`
5450
height: fit-content;
5551
width: 100%;
5652
`;
5753

58-
const MainContentContainer = styled.main`
59-
height: 100%;
60-
display: grid;
61-
grid-template-rows: 1fr auto;
62-
`;
63-
6454
const Main = ({ children }: AppLayoutMainPropsType): JSX.Element => <div>{children}</div>;
6555

6656
const DxcApplicationLayout = ({ logo, header, sidenav, footer, children }: ApplicationLayoutPropsType): JSX.Element => {
57+
const [headerHeight, setHeaderHeight] = useState("0px");
58+
59+
const handleHeaderHeight = useCallback(
60+
(headerElement: HTMLDivElement | null) => {
61+
if (headerElement) {
62+
const height = headerElement.offsetHeight;
63+
setHeaderHeight(`${height}px`);
64+
}
65+
},
66+
[header]
67+
);
68+
6769
const contextValue = useMemo(() => {
6870
return {
6971
logo,
@@ -75,24 +77,20 @@ const DxcApplicationLayout = ({ logo, header, sidenav, footer, children }: Appli
7577
return (
7678
<ApplicationLayoutContainer ref={ref} header={header}>
7779
<ApplicationLayoutContext.Provider value={contextValue}>
78-
{header && <HeaderContainer>{header}</HeaderContainer>}
80+
{header && <HeaderContainer ref={handleHeaderHeight}>{header}</HeaderContainer>}
7981
<BodyContainer hasSidenav={!!sidenav}>
80-
{sidenav && <SidenavContainer>{sidenav}</SidenavContainer>}
81-
<MainContainer>
82-
<MainContentContainer>
83-
{findChildType(children, Main)}
84-
<FooterContainer>
85-
{footer ?? (
86-
<DxcFooter
87-
copyright={`© DXC Technology ${year}. All rights reserved.`}
88-
bottomLinks={bottomLinks}
89-
socialLinks={socialLinks}
90-
/>
91-
)}
92-
</FooterContainer>
93-
</MainContentContainer>
94-
</MainContainer>
82+
{sidenav && <SidenavContainer headerHeight={headerHeight}>{sidenav}</SidenavContainer>}
83+
<MainContainer>{findChildType(children, Main)}</MainContainer>
9584
</BodyContainer>
85+
<FooterContainer>
86+
{footer ?? (
87+
<DxcFooter
88+
copyright={`© DXC Technology ${year}. All rights reserved.`}
89+
bottomLinks={bottomLinks}
90+
socialLinks={socialLinks}
91+
/>
92+
)}
93+
</FooterContainer>
9694
</ApplicationLayoutContext.Provider>
9795
</ApplicationLayoutContainer>
9896
);

packages/lib/src/search-bar/SearchBar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ const DxcSearchBar = ({
7272
const inputRef = useRef<HTMLInputElement>(null);
7373
const [innerValue, setInnerValue] = useState("");
7474

75-
const handleClearActionOnClick = () => {
76-
setInnerValue("");
75+
const handleClearAction = () => {
76+
handleSearchChangeValue("");
7777
inputRef.current?.focus();
7878
};
7979

@@ -90,7 +90,7 @@ const DxcSearchBar = ({
9090
case "Escape":
9191
e.preventDefault();
9292
if (innerValue.length > 0) {
93-
handleClearActionOnClick();
93+
handleClearAction();
9494
}
9595
break;
9696
case "Enter":
@@ -123,7 +123,7 @@ const DxcSearchBar = ({
123123
size="xsmall"
124124
shape="circle"
125125
icon="cancel"
126-
onClick={handleClearActionOnClick}
126+
onClick={handleClearAction}
127127
tabIndex={0}
128128
title={!disabled ? translatedLabels.searchBar.clearFieldActionTitle : undefined}
129129
/>

packages/lib/src/sidenav/Sidenav.stories.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,20 @@ import DxcAvatar from "../avatar/Avatar";
1010
import { userEvent, within } from "storybook/internal/test";
1111
import disabledRules from "../../test/accessibility/rules/specific/sidenav/disabledRules";
1212
import preview from "../../.storybook/preview";
13-
import { useState } from "react";
13+
import { useEffect, useState } from "react";
1414

1515
export default {
1616
title: "Sidenav",
1717
component: DxcSidenav,
18+
decorators: [
19+
(Story) => {
20+
useEffect(() => {
21+
document.body.style.padding = "0";
22+
}, []);
23+
24+
return <Story />;
25+
},
26+
],
1827
parameters: {
1928
a11y: {
2029
config: {
@@ -34,15 +43,15 @@ const DetailedAvatar = () => {
3443
<DxcAvatar color="primary" status={{ mode: "error", position: "bottom" }} title="Michael Ramirez" />
3544
<DxcFlex direction="column">
3645
<DxcTypography
37-
color="var(--color-fg-neutral-dark"
46+
color="var(--color-fg-neutral-dark)"
3847
fontFamily="var(--typography-font-family)"
3948
fontSize="var(--typography-label-l)"
4049
fontWeight="var(--typography-label-regular)"
4150
>
4251
Michael Ramirez
4352
</DxcTypography>
4453
<DxcTypography
45-
color="var(--color-fg-neutral-stronger"
54+
color="var(--color-fg-neutral-stronger)"
4655
fontFamily="var(--typography-font-family)"
4756
fontSize="var(--typography-label-s)"
4857
fontWeight="var(--typography-label-regular)"

0 commit comments

Comments
 (0)