Skip to content

Commit

Permalink
Merge pull request #265 from kronostechnologies/dev/DS-201
Browse files Browse the repository at this point in the history
feat(ApplicationMenu): add SkipLink
  • Loading branch information
meriouma authored Jul 9, 2021
2 parents b0e6177 + 775c522 commit 1b55de6
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 105 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { shallow } from 'enzyme';
import React from 'react';
import { getByTestId } from '../../test-utils/enzyme-selectors';
import { renderWithProviders } from '../../test-utils/renderer';
import { ApplicationMenu } from './application-menu';

describe('Application Menu', () => {
test('Matches the snapshot (desktop)', () => {
it('Matches the snapshot (desktop)', () => {
const tree = renderWithProviders(
<ApplicationMenu mobileDrawerContent={(<p>Test</p>)}>
Hello, World!
Expand All @@ -13,7 +15,7 @@ describe('Application Menu', () => {
expect(tree).toMatchSnapshot();
});

test('Matches the snapshot (mobile)', () => {
it('Matches the snapshot (mobile)', () => {
const tree = renderWithProviders(
<ApplicationMenu mobileDrawerContent={(<p>Test</p>)}>
Hello, World!
Expand All @@ -24,7 +26,7 @@ describe('Application Menu', () => {
expect(tree).toMatchSnapshot();
});

test('mobileDrawerContent prop adds a side drawer and burger button in mobile', () => {
it('mobileDrawerContent prop adds a side drawer and burger button in mobile', () => {
const tree = renderWithProviders(
<ApplicationMenu mobileDrawerContent={(<p>Test</p>)}>
Hello, World!
Expand All @@ -34,4 +36,16 @@ describe('Application Menu', () => {

expect(tree).toMatchSnapshot();
});

it('should have a SkipLink when skipLinkHref is provided', () => {
const wrapper = shallow(
<ApplicationMenu skipLinkHref="some href">
Hello, World!
</ApplicationMenu>,
);

const skipLink = getByTestId(wrapper, 'skip-link');

expect(skipLink.exists()).toBe(true);
});
});

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { ReactElement, ReactNode } from 'react';
import React, { ComponentProps, ReactElement, ReactNode } from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { focus } from '../../utils/css-state';
import { DeviceType, useDeviceContext } from '../device-context-provider/device-context-provider';
import { SkipLink } from '../skip-link/skip-link';
import { Content } from './application-menu-content';
import { Logo, LogoName } from './logo';

Expand All @@ -23,7 +24,9 @@ const Header = styled.header<{ device: DeviceType }>`
display: flex;
height: ${({ device }) => (device === 'desktop' ? 48 : 56)}px;
justify-content: space-between;
overflow: hidden;
padding: ${({ device }) => getPadding(device)};
position: relative;
`;

const LogoWrapper = styled(Link)`
Expand All @@ -32,13 +35,31 @@ const LogoWrapper = styled(Link)`
font-size: 1.5rem;
font-weight: var(--font-bold);
height: 100%;
${focus}
> * {
height: 100%;
}
`;

interface HeadbandProps {
const StyledSkipLink = styled(SkipLink)<ComponentProps<typeof SkipLink> & { isMobile?: boolean }>`
background-color: ${({ theme }) => theme.greys.white};
transform: translateY(-50%);
transition: top 0.2s cubic-bezier(0.5, 1, 0, 1);
&:not(:focus) {
clip: unset;
height: auto;
top: -50%;
width: auto;
}
&:focus {
top: 50%;
}
`;

interface ApplicationMenuProps {
/** Set the app name to get the proper logos */
appName?: LogoName;
/** Right-side content */
Expand All @@ -51,6 +72,7 @@ interface HeadbandProps {
logoHref?: string;
/** What will be displayed inside the mobile drawer */
mobileDrawerContent?: ReactNode;
skipLinkHref?: string;

customLogo?: ReactNode;
}
Expand All @@ -61,14 +83,19 @@ export function ApplicationMenu({
className,
logoHref = '/',
mobileDrawerContent,
skipLinkHref,
customLogo,
}: HeadbandProps): ReactElement {
}: ApplicationMenuProps): ReactElement {
const { device, isMobile } = useDeviceContext();

return (
<Header className={className} device={device}>
{skipLinkHref && (
<StyledSkipLink data-testid="skip-link" href={skipLinkHref} />
)}

<LogoWrapper to={logoHref} aria-label="Home">
{ customLogo ?? <Logo name={appName} mobile={isMobile} /> }
{customLogo ?? <Logo name={appName} mobile={isMobile} />}
</LogoWrapper>

<Content mobileDrawerContent={mobileDrawerContent}>
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/components/skip-link/skip-link.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { SkipLink } from './skip-link';

describe('SkipLink', () => {
test('Matches Snapshot (Desktop)', () => {
const tree = renderWithProviders(<SkipLink href="test">Test</SkipLink>, 'desktop');
const tree = renderWithProviders(<SkipLink href="test" />, 'desktop');

expect(tree).toMatchSnapshot();
});

test('Matches Snapshot (Mobile)', () => {
const tree = renderWithProviders(<SkipLink href="test">Test</SkipLink>, 'mobile');
const tree = renderWithProviders(<SkipLink href="test" />, 'mobile');

expect(tree).toMatchSnapshot();
});
Expand Down
54 changes: 30 additions & 24 deletions packages/react/src/components/skip-link/skip-link.test.tsx.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 14 additions & 12 deletions packages/react/src/components/skip-link/skip-link.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,39 @@
import React, { ReactElement, ReactNode } from 'react';
import React, { ReactElement } from 'react';
import styled from 'styled-components';
import { useTranslation } from '../../i18n/use-translation';
import { AbstractButton } from '../buttons/abstract-button';
import { useDeviceContext } from '../device-context-provider/device-context-provider';

const StyledLink = styled(AbstractButton).attrs({ as: 'a' })<{ href: string }>`
color: ${({ theme }) => theme.main['primary-1.1']};
font-size: ${({ isMobile }) => (isMobile ? 1 : 0.875)}rem;
font-weight: var(--font-normal);
letter-spacing: 0.015rem;
overflow: hidden;
position: absolute;
text-transform: unset;
white-space: nowrap;
&:not(:focus) {
clip: rect(1px, 1px, 1px, 1px);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
&:focus {
background-color: ${({ theme }) => theme.greys.white};
color: ${({ theme }) => theme.main['primary-1.1']};
font-size: ${({ isMobile }) => (isMobile ? 1 : 0.875)}rem;
font-weight: var(--font-normal);
letter-spacing: 0.015rem;
position: absolute;
text-transform: unset;
}
`;

interface SkipLinkProps {
children: ReactNode;
className?: string;
href: string;
}

export function SkipLink({ children, className, href }: SkipLinkProps): ReactElement {
export function SkipLink({ className, href }: SkipLinkProps): ReactElement {
const { isMobile } = useDeviceContext();
const { t } = useTranslation('skip-link');

return <StyledLink className={className} href={href} isMobile={isMobile}>{children}</StyledLink>;
return <StyledLink className={className} href={href} isMobile={isMobile}>{t('label')}</StyledLink>;
}
22 changes: 14 additions & 8 deletions packages/react/src/i18n/translations.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export const Translations = {
en: {
avatar: {
ariaLabel: '{{username}} avatar',
},
datepicker: {
calendarButtonLabel: 'Choose date',
calendarButtonSelectedLabel: 'Choose date. The selected date is',
Expand Down Expand Up @@ -41,6 +44,9 @@ export const Translations = {
inputAriaLabel: 'Select an option',
validationErrorMessage: 'You must select an option',
},
'skip-link': {
label: 'Skip to main content',
},
'stepper-input': {
validationErrorMessage: 'Invalid number',
},
Expand All @@ -54,7 +60,7 @@ export const Translations = {
'text-area': {
validationErrorMessage: 'This text area is invalid',
maxLengthValidationErrorMessage:
'The number of characters exceeds the maximum allowed. You must shorten the description.',
'The number of characters exceeds the maximum allowed. You must shorten the description.',
characters: '{{current}}/{{max}} characters',
},
'text-input': {
Expand All @@ -63,11 +69,11 @@ export const Translations = {
'user-profile': {
ariaLabel: 'User menu',
},
avatar: {
ariaLabel: '{{username}} avatar',
},
},
fr: {
avatar: {
ariaLabel: 'Avatar de {{username}}',
},
datepicker: {
calendarButtonLabel: 'Choisissez une date',
calendarButtonSelectedLabel: 'Choisissez une date. La date sélectionnée est',
Expand Down Expand Up @@ -109,6 +115,9 @@ export const Translations = {
inputAriaLabel: 'Choisissez une option',
validationErrorMessage: 'Vous devez choisir une option',
},
'skip-link': {
label: 'Passer au contenu principal',
},
'stepper-input': {
validationErrorMessage: 'Ce nombre n\'est pas valide',
},
Expand All @@ -122,7 +131,7 @@ export const Translations = {
'text-area': {
validationErrorMessage: 'Cette zone texte n\'est pas valide',
maxLengthValidationErrorMessage:
'Le nombre de caractères dépasse le maximum autorisé. Vous devez raccourcir la description.',
'Le nombre de caractères dépasse le maximum autorisé. Vous devez raccourcir la description.',
characters: '{{current}}/{{max}} caractères',
},
'text-input': {
Expand All @@ -131,8 +140,5 @@ export const Translations = {
'user-profile': {
ariaLabel: 'Menu utilisateur',
},
avatar: {
ariaLabel: 'Avatar de {{username}}',
},
},
};
Loading

0 comments on commit 1b55de6

Please sign in to comment.