Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FE] Typography component #78

Merged
6 changes: 5 additions & 1 deletion frontend/src/app/(app)/documentation/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Typography } from '@app/components/common/Typography';

export default function Documentation() {
return (
<div>
<h1 className="mb-10 text-lg font-semibold leading-6 text-navy-900">Documentation</h1>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
Documentation
</Typography>
</div>
);
}
9 changes: 7 additions & 2 deletions frontend/src/app/(app)/library/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { LadderCard } from '@app/components/common/LadderCard';
import { getLadders } from '@app/api/ladder';
import { Typography } from '@app/components/common/Typography';

export default async function LibraryPage() {
const data = await getLadders();

return (
<div>
<h1 className="mb-10 text-lg font-semibold leading-6 text-navy-900">CPF Library</h1>
<p className="mb-6 tracking-wide text-navy-600">Select a career path to view the details.</p>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
CPF Library
</Typography>
<Typography className="mb-6 text-navy-600" variant="body-m/regular">
Select a career path to view the details.
</Typography>
<div className="grid grid-cols-3 gap-6">
{data.map((ladder) => (
<LadderCard key={ladder.ladderSlug} {...ladder} />
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/app/(app)/my-space/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Typography } from '@app/components/common/Typography';

export default function MySpace() {
return (
<div>
<h1 className="mb-10 text-lg font-semibold leading-6 text-navy-900">My Space</h1>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
My Space
</Typography>
</div>
);
}
7 changes: 4 additions & 3 deletions frontend/src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { redirect } from 'next/navigation';
import { routes } from '@app/constants';

export default function Home() {
return (
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">We shall see</div>
);
redirect(routes.mySpace.index);
}
6 changes: 5 additions & 1 deletion frontend/src/app/(app)/people/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { Typography } from '@app/components/common/Typography';

export default function People() {
return (
<div>
<h1 className="mb-10 text-lg font-semibold leading-6 text-navy-900">People</h1>
<Typography className="mb-10" variant="body-l/semibold" as="h1">
People
</Typography>
</div>
);
}
45 changes: 24 additions & 21 deletions frontend/src/components/common/AccordionCard/AccordionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PropsWithChildren, useEffect, useState } from 'react';
import { ChevronRightIcon } from '@app/static/icons/ChevronRightIcon';
import { generateClassNames } from '@app/utils';
import { AccordionCardProps } from './AccordionCard.interface';
import { Typography } from '@app/components/common/Typography';

export const AccordionCard = ({
title,
Expand All @@ -20,27 +21,29 @@ export const AccordionCard = ({

return (
<div className={className}>
<h2>
<button
type="button"
className={generateClassNames(
'flex w-full items-center justify-between rounded-2xl border border-navy-200 p-6',
{
'hover:bg-navy-100': !isOpen && children,
'hover:bg-transparent rounded-b-none border-b-0 pb-0': isOpen,
'cursor-auto': !children,
},
)}
onClick={() => setOpen(!isOpen)}
>
<span className={`${small ? 'text-base' : 'text-xl'} font-semibold text-navy-900`}>{title}</span>
{children ? (
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-navy-50">
<ChevronRightIcon className={generateClassNames('rotate-90 text-navy-500', { '-rotate-90': isOpen })} />
</div>
) : undefined}
</button>
</h2>
<button
type="button"
className={generateClassNames(
'flex w-full items-center justify-between rounded-2xl border border-navy-200 p-6',
{
'hover:bg-navy-100': !isOpen && children,
'hover:bg-transparent rounded-b-none border-b-0 pb-0': isOpen,
'cursor-auto': !children,
},
)}
onClick={() => setOpen(!isOpen)}
>
{small ? (
<Typography variant="body-m/semibold">{title}</Typography>
) : (
<Typography variant="head-s/semibold">{title}</Typography>
)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe something like that:

Suggested change
{small ? (
<Typography variant="body-m/semibold">{title}</Typography>
) : (
<Typography variant="head-s/semibold">{title}</Typography>
)}
<Typography variant={small ? "body-m/semibold" : "head-s/semibold"}>{title}</Typography>

{children ? (
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-navy-50">
<ChevronRightIcon className={generateClassNames('rotate-90 text-navy-500', { '-rotate-90': isOpen })} />
</div>
) : undefined}
</button>
{children ? (
<div className={generateClassNames('rounded-b-2xl border border-t-0 border-navy-200 p-6', { hidden: !isOpen })}>
{children}
Expand Down
29 changes: 15 additions & 14 deletions frontend/src/components/common/AccordionList/AccordionListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ import { PropsWithChildren, useState } from 'react';
import { ChevronRightIcon } from '@app/static/icons/ChevronRightIcon';
import { generateClassNames } from '@app/utils';
import { AccordionListItemProps } from './AccordionList.interface';
import { Typography } from '@app/components/common/Typography';

export const AccordionListItem = ({ title, children }: PropsWithChildren<AccordionListItemProps>) => {
const [isOpen, setOpen] = useState(false);

return (
<div className="border-b border-b-navy-200 px-2 py-4">
<h3>
<button
type="button"
className={generateClassNames('flex w-full items-center justify-between', { 'cursor-auto': !children })}
onClick={() => setOpen(!isOpen)}
>
<span className="text-left text-base font-medium text-navy-600">{title}</span>
<div className="flex min-h-10 min-w-10 items-center justify-center">
{children ? (
<ChevronRightIcon className={generateClassNames('rotate-90 text-navy-500', { '-rotate-90': isOpen })} />
) : undefined}
</div>
</button>
</h3>
<button
type="button"
className={generateClassNames('flex w-full items-center justify-between', { 'cursor-auto': !children })}
onClick={() => setOpen(!isOpen)}
>
<Typography variant="body-m/medium" className="text-left text-navy-600">
{title}
</Typography>
<div className="flex min-h-10 min-w-10 items-center justify-center">
{children ? (
<ChevronRightIcon className={generateClassNames('rotate-90 text-navy-500', { '-rotate-90': isOpen })} />
) : undefined}
</div>
</button>
{children ? (
<div className={generateClassNames('mt-4 text-sm font-normal text-navy-600', { hidden: !isOpen })}>
{children}
Expand Down
9 changes: 7 additions & 2 deletions frontend/src/components/common/BucketCard/BucketCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Link from 'next/link';
import { BucketCardProps } from './BucketCard.interface';
import { ChevronRightIcon } from '@app/static/icons/ChevronRightIcon';
import { routes } from '@app/constants';
import { Typography } from '@app/components/common/Typography';

export const BucketCard = ({ bucket, ladderSlug }: BucketCardProps) => {
const { bucketSlug, bucketName, description } = bucket;
Expand All @@ -12,12 +13,16 @@ export const BucketCard = ({ bucket, ladderSlug }: BucketCardProps) => {
className="flex cursor-pointer flex-col gap-2 rounded-2xl border border-navy-200 bg-white p-6 hover:bg-navy-100"
>
<div className="flex justify-between">
<h2 className="text-xl font-semibold text-navy-900">{bucketName}</h2>
<Typography variant="head-s/semibold" as="h2">
{bucketName}
</Typography>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-navy-50">
<ChevronRightIcon className="text-navy-500" />
</div>
</div>
<p className="text-base text-navy-600">{description}</p>
<Typography variant="body-m/regular" className="text-navy-600">
{description}
</Typography>
</Link>
);
};
5 changes: 4 additions & 1 deletion frontend/src/components/common/LadderCard/LadderCard.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Link from 'next/link';
import { LadderCardInterface } from './LadderCard.interface';
import { routes } from '@app/constants';
import { Typography } from '@app/components/common/Typography';

export const LadderCard = ({ ladderName, ladderSlug }: LadderCardInterface) => (
<Link
href={`${routes.library.index}/${ladderSlug}`}
className="flex h-44 cursor-pointer items-center justify-center rounded-2xl border border-navy-200 bg-white hover:bg-navy-100"
>
<h2 className="text-l text-navy-900">{ladderName}</h2>
<Typography as="h2" variant="body-l/semibold">
{ladderName}
</Typography>
</Link>
);
46 changes: 46 additions & 0 deletions frontend/src/components/common/Typography/Typography.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export interface TypographyProps {
variant?: TypographyVariants;
as?: TextElement;
className?: string;
}

export type TypographyVariants =
Copy link
Collaborator Author

@wiktoriasalamon wiktoriasalamon Jul 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was considering splitting variant and font weight to different parameters, but I decided to leave it this way to stay consistent with typography styles from Figma (I only shortened 'Heading' to 'head'). I think it would be easier to use while implementing new views:
For example:
image
Jane Edge has a style named "Headline M/Meddium", so for the Typography component we should use the head-m/medium variant

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I've split button styles into two separate props to reflect Figma. Your approach makes sense to me 👌

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good ideas, let's go with that

| 'hint/caps-medium'
| 'hint/regular'
| 'hint/medium'
| 'hint/semibold'
| 'hint/bold'
| 'body-s/regular'
| 'body-s/medium'
| 'body-s/semibold'
| 'body-s/bold'
| 'body-m/regular'
| 'body-m/medium'
| 'body-m/semibold'
| 'body-m/bold'
| 'body-l/regular'
| 'body-l/medium'
| 'body-l/semibold'
| 'body-l/bold'
| 'head-s/regular'
| 'head-s/medium'
| 'head-s/semibold'
| 'head-s/bold'
| 'head-m/regular'
| 'head-m/medium'
| 'head-m/semibold'
| 'head-m/bold'
| 'head-l/regular'
| 'head-l/medium'
| 'head-l/semibold'
| 'head-l/bold'
| 'head-xl/regular'
| 'head-xl/medium'
| 'head-xl/semibold'
| 'head-xl/bold'
| 'head-2xl/regular'
| 'head-2xl/medium'
| 'head-2xl/semibold'
| 'head-2xl/bold';

export type TextElement = 'p' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
72 changes: 72 additions & 0 deletions frontend/src/components/common/Typography/Typography.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React, { PropsWithChildren } from 'react';
import { TypographyProps, TypographyVariants } from '@app/components/common/Typography/Typography.interface';
import { generateClassNames } from '@app/utils';

const variantsStyles: {
[key in TypographyVariants]: string;
} = {
'hint/caps-medium': 'text-xs tracking-widest uppercase font-medium',
'hint/regular': 'text-xs tracking-wider font-normal',
'hint/medium': 'text-xs font-medium',
'hint/semibold': 'text-xs font-semibold',
'hint/bold': 'text-xs font-bold',
'body-s/regular': 'text-sm tracking-wide font-normal',
'body-s/medium': 'text-sm tracking-wider font-medium',
'body-s/semibold': 'text-sm tracking-wider font-semibold',
'body-s/bold': 'text-sm tracking-wide font-bold',
'body-m/regular': 'text-base tracking-wider font-normal',
'body-m/medium': 'text-base font-medium',
'body-m/semibold': 'text-base font-semibold',
'body-m/bold': 'text-base font-bold',
'body-l/regular': 'text-lg font-normal',
'body-l/medium': 'text-lg font-medium',
'body-l/semibold': 'text-lg font-semibold',
'body-l/bold': 'text-xl font-bold',
'head-s/regular': 'text-xl font-normal',
'head-s/medium': 'text-xl font-medium',
'head-s/semibold': 'text-xl font-semibold',
'head-s/bold': 'text-xl font-bold',
'head-m/regular': 'text-2xl font-normal',
'head-m/medium': 'text-2xl font-medium',
'head-m/semibold': 'text-2xl font-semibold',
'head-m/bold': 'text-2xl font-bold',
'head-l/regular': 'text-3xl font-normal',
'head-l/medium': 'text-3xl font-medium',
'head-l/semibold': 'text-3xl font-semibold',
'head-l/bold': 'text-3xl font-bold',
'head-xl/regular': 'text-4xl font-normal',
'head-xl/medium': 'text-4xl font-medium',
'head-xl/semibold': 'text-4xl font-semibold',
'head-xl/bold': 'text-4xl font-bold',
'head-2xl/regular': 'text-5xl font-normal',
'head-2xl/medium': 'text-5xl font-medium',
'head-2xl/semibold': 'text-5xl font-semibold',
'head-2xl/bold': 'text-5xl font-bold',
};

export const Typography = ({
variant = 'body-m/regular',
as,
className,
children,
}: PropsWithChildren<TypographyProps>) => {
const classnames = generateClassNames('text-navy-900', variantsStyles[variant], className ?? '');

switch (as) {
case 'h1':
return <h1 className={classnames}>{children}</h1>;
case 'h2':
return <h2 className={classnames}>{children}</h2>;
case 'h3':
return <h3 className={classnames}>{children}</h3>;
case 'h4':
return <h4 className={classnames}>{children}</h4>;
case 'h5':
return <h5 className={classnames}>{children}</h5>;
case 'h6':
return <p className={classnames}>{children}</p>;
case 'p':
default:
return <p className={classnames}>{children}</p>;
}
};
1 change: 1 addition & 0 deletions frontend/src/components/common/Typography/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Typography } from './Typography';
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { Modal } from '@app/components/common/Modal';
import { AdvancementLevelProps } from './AdvancemetLevel.interface';
import { useAdvancementLevel } from '@app/components/modules/AdvancementLevel/AdvancementLevel.hooks';
import { Markdown } from '@app/components/common/Markdown';
import { Typography } from '@app/components/common/Typography';
import { Button } from '@app/components/common/Button';

export const AdvancementLevel: React.FC<AdvancementLevelProps> = ({ showVerticalLine, data }) => {
const { hideModal, openModal, toggleAccordionOpen, modalOpen, accordionOpen } = useAdvancementLevel();
Expand All @@ -14,7 +16,7 @@ export const AdvancementLevel: React.FC<AdvancementLevelProps> = ({ showVertical
const shouldBeExpandedByDefault = Object.keys(data.categories).length === 1;

return (
<div className="flex flex-row">
<div className="flex flex-row gap-2">
<div className="relative flex flex-col items-center">
<button
className="mb-2 mt-4 flex h-6 w-6 items-center justify-center rounded-full bg-blue-800 hover:opacity-50"
Expand All @@ -24,23 +26,24 @@ export const AdvancementLevel: React.FC<AdvancementLevelProps> = ({ showVertical
</button>
{showVerticalLine && <div className="absolute left-3 top-12 h-[calc(100%-40px)] w-[1.5px] bg-blue-800" />}
</div>
<div className="mb-4 ml-2 flex w-full flex-col gap-4">
<div className="mb-4 ml-4 flex w-full flex-col gap-4">
<button
className={`flex w-full cursor-pointer flex-col gap-4 rounded-lg p-4 ${!accordionOpen && 'hover:bg-navy-50'}`}
className={`flex w-full cursor-pointer flex-col gap-4 rounded-lg ${!accordionOpen && 'hover:bg-navy-50'}`}
onClick={toggleAccordionOpen}
>
<h3 className="text-lg">Advancement level {advancementLevel}</h3>
<p className="text-base tracking-wide text-navy-600">{description}</p>
<Typography as="h3" variant="body-l/semibold">
Advancement level {advancementLevel}
</Typography>
<Typography variant="body-m/regular" className="text-navy-600">
{description}
</Typography>
</button>
{accordionOpen && (
<>
{projects.length > 0 && (
<button
className="w-fit text-sm font-semibold text-blue-800 hover:text-blue-900 hover:underline hover:underline-offset-4"
onClick={openModal}
>
<Button variant="link" onClick={openModal} className="w-fit text-sm">
An example way to pass level
</button>
</Button>
)}
{Object.entries(categories).map(([category, skills]) => (
<AccordionCard
Expand Down
Loading