Skip to content

Commit

Permalink
refactor(studio-root): seperated repeated html on ContactPage to Reac…
Browse files Browse the repository at this point in the history
…t Component (#14169)
  • Loading branch information
framitdavid authored Nov 27, 2024
1 parent fa38b1b commit d67600e
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 94 deletions.
1 change: 1 addition & 0 deletions .github/workflows/lint-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
resource-adm
resource-registry
settings
studio-root
subform
testing
text
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.section {
background-color: var(--colors-grey-100);
border-radius: 4px;
display: flex;
gap: var(--fds-spacing-6);
padding: var(--fds-spacing-8);
max-width: 735px;
}

.iconContainer {
background-color: var(--primary-color-700);
border-radius: 100%;
color: var(--colors-white);
display: flex;
align-items: center;
justify-content: center;
height: 60px;
width: 60px;
box-sizing: border-box;
}

.icon {
font-size: var(--fds-sizing-7);
}

.textContainer {
flex: 1;
}

@media (min-width: 768px) {
.section {
padding: var(--fds-spacing-12);
}
}

@media (min-width: 960px) {
.section {
padding-right: var(--fds-spacing-22);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { ContactSection, type ContactSectionProps } from './ContactSection';
import { render, screen } from '@testing-library/react';
import { SlackIcon } from '@studio/icons';

const defaultContactSectionProps: ContactSectionProps = {
title: '',
description: '',
link: {
name: '',
href: '',
},
Icon: SlackIcon,
};

describe('ContactSection', () => {
it('should render with provided props', () => {
const props: ContactSectionProps = {
...defaultContactSectionProps,
title: 'Get in touch',
description: 'We are helpfull',
Icon: SlackIcon,
link: {
name: 'Get in touch',
href: 'mailto:[email protected]',
},
additionalContent: 'additional content',
};
renderContactSection(props);

const icon = screen.getByTitle(props.title);
expect(icon).toBeInTheDocument();

const heading = screen.getByRole('heading', { name: props.title, level: 2 });
expect(heading).toBeInTheDocument();

const description = screen.getByText(props.description);
expect(description).toBeInTheDocument();

const additionalContent = screen.getByText(props.additionalContent as string);
expect(additionalContent).toBeInTheDocument();

const link = screen.getByRole('link', { name: props.link.name });
expect(link).toBeInTheDocument();
expect(link).toHaveAttribute('href', props.link.href);
});
});

function renderContactSection(props: ContactSectionProps = defaultContactSectionProps): void {
render(<ContactSection {...props} />);
}
38 changes: 38 additions & 0 deletions frontend/studio-root/components/ContactSection/ContactSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { type ComponentType, type ReactElement, type ReactNode } from 'react';
import { StudioHeading, StudioLink, StudioParagraph } from '@studio/components';
import { type IconProps } from '@studio/icons';
import classes from './ContactSection.module.css';

export type ContactSectionProps = {
title: string;
description: string;
link: {
name: string;
href: string;
};
Icon: ComponentType<IconProps>;
additionalContent?: ReactNode;
};
export const ContactSection = ({
title,
description,
link,
Icon,
additionalContent,
}: ContactSectionProps): ReactElement => {
return (
<section className={classes.section}>
<div className={classes.iconContainer}>
<Icon className={classes.icon} title={title} aria-hidden />
</div>
<div className={classes.textContainer}>
<StudioHeading level={2} size='xs' spacing>
{title}
</StudioHeading>
<StudioParagraph spacing>{description}</StudioParagraph>
{additionalContent && <span>{additionalContent}</span>}
<StudioLink href={link.href}>{link.name}</StudioLink>
</div>
</section>
);
};
1 change: 1 addition & 0 deletions frontend/studio-root/components/ContactSection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ContactSection, type ContactSectionProps } from './ContactSection';
40 changes: 0 additions & 40 deletions frontend/studio-root/pages/Contact/ContactPage.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
flex-direction: column;
font-family: var(--studio-font-family);
position: relative;

margin: var(--fds-spacing-10) auto;
max-width: 1230px;
}
Expand All @@ -22,47 +21,8 @@
margin: var(--fds-spacing-12) var(--fds-spacing-8);
}

.section {
background-color: var(--colors-grey-100);
border-radius: 4px;
display: flex;
gap: var(--fds-spacing-6);
padding: var(--fds-spacing-8);
max-width: 735px;
}

.iconContainer {
background-color: var(--primary-color-700);
border-radius: 100%;
color: var(--colors-white);
display: flex;
align-items: center;
justify-content: center;
height: 60px;
width: 60px;
box-sizing: border-box;
}

.icon {
font-size: 2.3rem;
}

.textContainer {
flex: 1;
}

@media (min-width: 768px) {
.content {
margin: var(--fds-spacing-18);
}

.section {
padding: var(--fds-spacing-12);
}
}

@media (min-width: 960px) {
.section {
padding-right: var(--fds-spacing-22);
}
}
104 changes: 50 additions & 54 deletions frontend/studio-root/pages/Contact/ContactPage.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,77 @@
import React from 'react';
import classes from './ContactPage.module.css';
import { Heading, Link, Paragraph } from '@digdir/designsystemet-react';
import { Trans, useTranslation } from 'react-i18next';
import { EnvelopeClosedIcon, SlackIcon, GitHubIcon } from '@studio/icons';
import classNames from 'classnames';
import { GetInTouchWith } from 'app-shared/getInTouch';
import {
EmailContactProvider,
GitHubIssueContactProvider,
SlackContactProvider,
} from 'app-shared/getInTouch/providers';
import { StudioPageImageBackgroundContainer } from '@studio/components';
import {
StudioPageImageBackgroundContainer,
StudioHeading,
StudioParagraph,
} from '@studio/components';
import { ContactSection, type ContactSectionProps } from '../../components/ContactSection';

export const ContactPage = (): React.ReactElement => {
const { t } = useTranslation();
const contactByEmail = new GetInTouchWith(new EmailContactProvider());
const contactBySlack = new GetInTouchWith(new SlackContactProvider());
const contactByGitHubIssue = new GetInTouchWith(new GitHubIssueContactProvider());

const contactSections: Array<ContactSectionProps> = [
{
title: t('contact.email.heading'),
description: t('contact.email.content'),
link: {
name: t('general.service_desk.email'),
href: contactByEmail.url('serviceDesk'),
},
Icon: EnvelopeClosedIcon,
},
{
title: t('contact.slack.heading'),
description: t('contact.slack.content'),
additionalContent: (
<StudioParagraph spacing asChild>
<ul>
<Trans i18nKey='contact.slack.content_list'>
<li />
</Trans>
</ul>
</StudioParagraph>
),
link: {
name: t('contact.slack.link'),
href: contactBySlack.url('product-altinn-studio'),
},
Icon: SlackIcon,
},
{
title: t('contact.github_issue.heading'),
description: t('contact.github_issue.content'),
link: {
name: t('contact.github_issue.link_label'),
href: contactByGitHubIssue.url('choose'),
},
Icon: GitHubIcon,
},
];

return (
<StudioPageImageBackgroundContainer image='/designer/img/page-background.svg'>
<div className={classes.container}>
<div className={classes.content}>
<div>
<Heading size='medium' spacing>
<StudioHeading size='medium' spacing>
{t('general.contact')}
</Heading>
</StudioHeading>
</div>
<section className={classes.section}>
<div className={classes.iconContainer}>
<EnvelopeClosedIcon className={classes.icon} />
</div>
<div className={classes.textContainer}>
<Heading level={2} size='xsmall' spacing>
{t('contact.email.heading')}
</Heading>
<Paragraph spacing>{t('contact.email.content')}</Paragraph>
<Link href={contactByEmail.url('serviceDesk')}>
{t('general.service_desk.email')}
</Link>
</div>
</section>
<section className={classes.section}>
<div className={classes.iconContainer}>
<SlackIcon className={classes.icon} />
</div>
<div className={classes.textContainer}>
<Heading level={2} size='xsmall' spacing>
{t('contact.slack.heading')}
</Heading>
<Paragraph spacing>{t('contact.slack.content')}</Paragraph>
<Paragraph spacing asChild>
<ul>
<Trans i18nKey='contact.slack.content_list'>
<li />
</Trans>
</ul>
</Paragraph>
<Link href={contactBySlack.url('product-altinn-studio')}>
{t('contact.slack.link')}
</Link>
</div>
</section>
<section className={classes.section}>
<div className={classNames(classes.iconContainer)}>
<GitHubIcon className={classes.icon} />
</div>
<div className={classes.textContainer}>
<Heading level={2} size='xsmall' spacing>
{t('contact.github_issue.heading')}
</Heading>
<Paragraph spacing>{t('contact.github_issue.content')}</Paragraph>
<Link href={contactByGitHubIssue.url('choose')}>
{t('contact.github_issue.link_label')}
</Link>
</div>
</section>
{contactSections.map((contactSection) => (
<ContactSection {...contactSection} key={contactSection.title} />
))}
</div>
</div>
</StudioPageImageBackgroundContainer>
Expand Down

0 comments on commit d67600e

Please sign in to comment.