Skip to content

Commit

Permalink
Merge pull request #409 from Automattic/add/footer-component
Browse files Browse the repository at this point in the history
Footer: Create new component
  • Loading branch information
brookewp committed Sep 5, 2024
2 parents 65130ed + 74fa7b5 commit 155625f
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 8 deletions.
46 changes: 46 additions & 0 deletions src/system/Footer/Footer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Internal dependencies
*/
import { StoryObj } from '@storybook/react';

import { Footer } from '../Footer/Footer';

export default {
title: 'Navigation/Footer',
component: Footer,
argTypes: {
hasTrailingSeparator: { control: { type: 'boolean' } },
links: { control: { type: 'array' } },
customLogo: { control: { type: null } },
maxWidth: { control: { type: 'text' } },
},
};

type Story = StoryObj< typeof Footer >;

export const Default: Story = {
render: () => (
<Footer
links={ [
{
children: 'About',
href: 'https://wpvip.com/',
screenReaderText: 'WordPress VIP. Learn more about us',
showExternalIcon: false,
},
{
children: 'Docs',
href: 'https://docs.wpvip.com/',
screenReaderText: 'our public documentation on our platform and tools',
},
{
children: 'Status',
href: 'https://wpvipstatus.com',
screenReaderText:
". See real-time availability and performance monitoring for WordPress VIP's services",
newTab: true,
},
] }
/>
),
};
40 changes: 40 additions & 0 deletions src/system/Footer/Footer.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';

/**
* Internal dependencies
*/
import { Footer } from './Footer';

const links = [
{
children: 'Link1',
href: 'https://wpvip.com/',
},
{
children: 'Link2',
href: 'https://docs.wpvip.com/',
},
];

describe( '<Footer />', () => {
it( 'should accept LinkExternal props for Footer links', () => {
const moreLinks = [
{
children: 'Link3',
href: 'https://wpvipstatus.com',
newTab: true,
},
];

const combinedLinks = [ ...links, ...moreLinks ];

render( <Footer links={ combinedLinks } /> );

const link1 = screen.getByRole( 'link', { name: /link1/i } );
const link3 = screen.getByRole( 'link', { name: /link3/i } );

expect( link1 ).toHaveAttribute( 'target', '_self' );
expect( link3 ).toHaveAttribute( 'target', '_blank' );
} );
} );
120 changes: 120 additions & 0 deletions src/system/Footer/Footer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/** @jsxImportSource theme-ui */

/**
* Internal dependencies
*/
import { Box } from '../Box/Box';
import { LinkExternal, LinkExternalProps } from '../LinkExternal/LinkExternal';
import { navItemStyles } from '../Nav/styles';
import { a8cLogo } from '../assets/a8cLogo';

type FooterProps = {
/**
* Add an additional separator after the last element.
*
* @default false
**/
hasTrailingSeparator?: boolean;
/**
* Option to show underlines for links.
*
* @default false
**/
hasUnderlinedLinks?: boolean;
/**
* An array of LinkExternal components as objects.
**/
links: LinkExternalProps[];
/**
* A logo to display in the right footer area. Displays Automattic logo by default.
**/
customLogo?: React.ReactNode;
/**
* The maxiumum width of the footer.
*
* @default 100%
**/
maxWidth?: string | number;
};

export const Footer = ( {
hasTrailingSeparator = false,
hasUnderlinedLinks = false,
links,
customLogo,
maxWidth = '100%',
}: FooterProps ) => {
const FooterLinks = ( { children, ...props } ) => {
return <li { ...props }>{ children }</li>;
};

const trailingSeparator = {
'&:last-of-type::after': {
display: 'inline-block',
margin: '0 0.45em 0 1em ',
transform: 'rotate(15deg)',
borderRightColor: 'text',
borderRightStyle: 'solid',
borderRightWidth: '0.1em',
height: '0.8em',
content: '""',
},
};

return (
<footer
sx={ {
display: 'flex',
flexDirection: [ 'column', 'column', 'row' ],
justifyContent: 'space-between',
gap: 4,
fontSize: 1,
mt: 2,
maxWidth,
textAlign: 'left',
} }
>
{ links?.length && (
<ul
sx={ {
display: 'flex',
alignSelf: 'center',
gap: 2,
listStyle: 'none',
paddingInlineStart: 0,
} }
>
{ links.map( ( linkProps, index ) => {
return (
<FooterLinks
key={ `footer-link_${ index }` }
sx={
hasTrailingSeparator
? {
...navItemStyles( 'horizontal', 'breadcrumbs' ),
...trailingSeparator,
}
: { ...navItemStyles( 'horizontal', 'breadcrumbs' ) }
}
>
<LinkExternal
{ ...linkProps }
sx={ { ml: 1, textDecoration: hasUnderlinedLinks ? undefined : 'none' } }
/>
</FooterLinks>
);
} ) }
</ul>
) }
<Box
sx={ {
backgroundColor: [ 'layer.1', 'layer.1', 'transparent' ],
py: 2,
textAlign: [ 'center', 'center', 'right' ],
} }
>
{ customLogo ? customLogo : a8cLogo }
</Box>
</footer>
);
};
35 changes: 27 additions & 8 deletions src/system/LinkExternal/LinkExternal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,51 @@ import { translate } from 'i18n-calypso';
import { Link } from '../Link';
import ScreenReaderText from '../ScreenReaderText';

import type { LinkProps } from '../Link/Link';

// Screen reader announcements
const DEFAULT_EXTERNAL_LINK_TEXT = translate( ', external link' ); // reads as: link, <link text>, external link
const NEW_TAB_TEXT = translate( ', opens in a new tab' ); // reads as: link, <link text>, external link, opens in a new tab
const DEFAULT_EXTERNAL_LINK_TEXT = translate( ', external link' );
const NEW_TAB_TEXT = translate( ', opens in a new tab' );

type Props = {
export type LinkExternalProps = LinkProps & {
/**
* Element to be linked.
**/
children?: React.ReactNode;
/**
* Additional text to include after `defaultScreenReaderText` if enabled.
**/
screenReaderText?: string | number;
href: string;
/**
* Optional arrow icon.
*
* @default true
**/
showExternalIcon?: boolean;
/**
* Include default text which reads as: `link, <link text>, external link`
* or if `newTab` is `true`, reads as: `link, <link text>, external link, opens in a new tab`
**/
defaultScreenReaderText?: boolean;
/**
* If link should open in a new tab.
*
* @default false
**/
newTab?: boolean;
};

export const LinkExternal: React.FC< Props > = ( {
export const LinkExternal = ( {
children = null,
screenReaderText = '',
href,
showExternalIcon = true,
newTab = false,
...rest
} ) => (
}: LinkExternalProps ) => (
<Link
as="a"
target={ newTab ? '_blank' : '_self' }
rel={ newTab ? 'noopener noreferrer' : '' }
href={ href }
{ ...rest }
>
{ children }
Expand Down
30 changes: 30 additions & 0 deletions src/system/assets/a8cLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* External dependencies
*/
import { translate } from 'i18n-calypso';

/**
* Internal dependencies
*/
import { LinkExternal } from '../LinkExternal/LinkExternal';
import { Text } from '../Text';

export const a8cLogo = (
<Text as="span" sx={ { display: 'inline-flex', alignItems: 'center', gap: 2, mb: 0 } }>
{ translate( 'An' ) }
<LinkExternal
href="https://automattic.com"
showExternalIcon={ false }
screenReaderText={ translate( 'Automattic' ) }
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 15.4" style={ { minWidth: 100 } }>
<path
d="M72.5 15.4c-5.1 0-8.4-3.7-8.4-7.5v-.4c0-3.9 3.3-7.5 8.4-7.5 5.1 0 8.4 3.6 8.4 7.5V8c0 3.8-3.3 7.4-8.4 7.4zm5.7-7.9c0-2.8-2-5.3-5.7-5.3s-5.7 2.5-5.7 5.3v.3c0 2.8 2 5.3 5.7 5.3s5.7-2.5 5.7-5.3v-.3z"
fill="#3298CB"
/>
<path d="M15 14.9l-1.9-3.6H4.7l-1.8 3.6H0L7.8.5H10l7.9 14.4H15zM8.8 3.3l-3.1 6h6.4l-3.3-6zm21.4 12.1c-5.2 0-7.6-2.8-7.6-6.5V.5h2.7V9c0 2.7 1.7 4.2 5.1 4.2 3.4 0 4.8-1.6 4.8-4.2V.5h2.7v8.4c0 3.6-2.3 6.5-7.7 6.5zM52.9 2.8v12.1h-2.7V2.8h-6.3V.5h15.3v2.2h-6.3zM105 14.9V3.5l-.7 1.3-6 10.1H97L91 4.8l-.7-1.3v11.4h-2.6V.5h3.7l5.7 9.9.7 1.2.7-1.2 5.6-9.9h3.7v14.4H105zm23.1 0l-1.9-3.6h-8.4l-1.8 3.6h-3L120.8.5h2.2l7.9 14.4h-2.8zm-6.2-11.6l-3.1 6h6.4l-3.3-6zm19.9-.5v12.1h-2.7V2.8h-6.3V.5h15.3v2.2h-6.3zm19.8 0v12.1h-2.7V2.8h-6.3V.5h15.3v2.2h-6.3zm12.9 12.1v-13c1.1 0 1.5-.6 1.5-1.4h1.1v14.4h-2.6zm23.8-10.3c-1.3-1.2-3.2-2.3-5.8-2.3-3.8 0-6 2.6-6 5.4V8c0 2.7 2.2 5.3 6.2 5.3 2.4 0 4.4-1.1 5.6-2.3l1.6 1.7c-1.6 1.6-4.3 2.9-7.4 2.9-5.4 0-8.7-3.5-8.7-7.4v-.6c0-3.9 3.6-7.6 8.9-7.6 3 0 5.8 1.3 7.3 2.9l-1.7 1.7zM74.3 5c.5.3.6 1 .3 1.5l-2.5 3.8c-.3.5-1 .6-1.5.3s-.6-1-.3-1.5l2.5-3.8c.4-.5 1-.6 1.5-.3z" />
</svg>
</LinkExternal>
{ translate( 'Creation' ) }
</Text>
);

0 comments on commit 155625f

Please sign in to comment.