Skip to content

Commit

Permalink
feat: adds the app layout components
Browse files Browse the repository at this point in the history
Adds the AppLayout component for laying out the app pages

closes #148
  • Loading branch information
anguspiv committed Nov 15, 2022
1 parent 7bfe887 commit 946b4df
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 3 deletions.
6 changes: 4 additions & 2 deletions src/components/organisms/SiteBanner/SiteBanner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const headerCss = css`
}
${media.lg} {
padding: 20vh ${spacing(4)};
padding: ${spacing(4)};
&:after {
position: absolute;
Expand Down Expand Up @@ -130,7 +130,9 @@ export function SiteBanner({ className }) {
<Avatar src={image} css={avatarCss} />
</Link>
<span css={titleCss}>Angus Perkerson</span>
<p css={subtitleCss}>Software Engineer specializing in Web and Applicaton development.</p>
<p css={subtitleCss}>
Software Engineer and Manager specializing in Web Applicaton development.
</p>
<a
href="mailto:angusp@angusp.com"
aria-label="Email angusp@angusp.com"
Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/SiteBanner/SiteBanner.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('<SiteBanner />', () => {
expect(screen.getByRole('img')).toBeInTheDocument();
expect(screen.getByText('Angus Perkerson')).toBeInTheDocument();
expect(
screen.getByText('Software Engineer specializing in Web and Applicaton development.'),
screen.getByText('Software Engineer and Manager specializing in Web Applicaton development.'),
).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Email angusp@angusp.com' })).toHaveAttribute(
'href',
Expand Down
95 changes: 95 additions & 0 deletions src/components/templates/AppLayout/AppLayout.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import PropTypes from 'prop-types';
import { css } from '@emotion/react';
import { rem } from 'polished';
import { SiteBanner } from '@components/organisms/SiteBanner';
import { media, spacing } from '@styles/utils';

const pageCss = css`
display: grid;
grid-template-areas: 'header' 'content' 'footer';
grid-template-rows: auto 1fr auto;
grid-template-columns: 1fr;
max-width: ${rem(800 + 640)};
min-height: 100vh;
margin: 0 auto;
${media.lg} {
grid-template-areas: 'header content' 'empty footer';
grid-template-rows: 1fr auto;
grid-template-columns: minmax(auto, 320px) 1fr;
}
${media.xl} {
grid-template-areas: 'header content spacer' 'empty footer spacer';
grid-template-rows: 1fr auto;
grid-template-columns: minmax(auto, 320px) minmax(800px, 1fr) minmax(auto, 320px);
}
`;

const headerWrapperCss = css`
position: relative;
grid-area: header;
`;

const headerCss = css`
${media.lg} {
position: sticky;
top: 0;
}
`;

const mainCss = css`
grid-area: content;
width: 100%;
max-width: ${rem(800)};
margin: 0 auto;
padding: ${spacing(2)};
${media.lg} {
align-self: center;
margin: 0;
padding: ${spacing(4)};
}
`;

const footerCss = css`
grid-area: footer;
max-width: ${rem(800)};
width: 100%;
margin: 0 auto;
padding: ${spacing(2)};
text-align: center;
${media.lg} {
padding: ${spacing(4)};
}
`;

export function AppLayout({ children }) {
const year = new Date().getFullYear();
return (
<div css={pageCss}>
<div css={headerWrapperCss}>
<SiteBanner css={headerCss} />
</div>
<main role="main" css={mainCss}>
{children}
</main>
<footer role="contentinfo" css={footerCss}>
<p>
© {year}, Built with <a href="https://nextjs.org/">Next.js</a>
</p>
</footer>
</div>
);
}

AppLayout.propTypes = {
children: PropTypes.node,
};

AppLayout.defaultProps = {
children: null,
};

export default AppLayout;
26 changes: 26 additions & 0 deletions src/components/templates/AppLayout/AppLayout.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import styled from '@emotion/styled';
import { AppLayout } from './AppLayout';

export default {
component: AppLayout,
title: 'Components/Templates/AppLayout',
};

const Content = styled.div`
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
min-height: 1400px;
background-color: pink;
`;

const Template = (args) => (
<AppLayout {...args}>
<Content>Content</Content>
</AppLayout>
);

export const Default = Template.bind({});
Default.args = {};
23 changes: 23 additions & 0 deletions src/components/templates/AppLayout/AppLayout.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { render, screen } from '@testing-library/react';
import { AppLayout } from './AppLayout';

describe('<AppLayout />', () => {
it('should render the layout', () => {
expect.assertions(4);

render(
<AppLayout>
<div>Content</div>
</AppLayout>,
);

const year = new Date().getFullYear();
const footerText = ${year}`;
const footerRegex = new RegExp(footerText, 'ig');

expect(screen.getByRole('banner')).toBeInTheDocument();
expect(screen.getByText('Content')).toBeInTheDocument();
expect(screen.getByText(footerRegex)).toBeInTheDocument();
expect(screen.getByRole('link', { name: 'Next.js' })).toBeInTheDocument();
});
});
5 changes: 5 additions & 0 deletions src/components/templates/AppLayout/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AppLayout } from './AppLayout';

export * from './AppLayout';

export default AppLayout;

0 comments on commit 946b4df

Please sign in to comment.