From d7bcd93efcd4f1ae20678d0efa6777cfadc09a4e Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 10 Nov 2023 12:16:59 +0100 Subject: refactor(components): replace Overview with ProjectOverview component * `cover` prop is now expecting a ReactElement (NextImage) * `meta` prop is now limited to a specific set of meta items * add a `name` prop to add an accessible name to the figure element --- .../project-overview/project-overview.tsx | 193 +++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/components/organisms/project-overview/project-overview.tsx (limited to 'src/components/organisms/project-overview/project-overview.tsx') diff --git a/src/components/organisms/project-overview/project-overview.tsx b/src/components/organisms/project-overview/project-overview.tsx new file mode 100644 index 0000000..2b8be0e --- /dev/null +++ b/src/components/organisms/project-overview/project-overview.tsx @@ -0,0 +1,193 @@ +import { + type ForwardRefRenderFunction, + type HTMLAttributes, + forwardRef, + useCallback, + type ReactElement, +} from 'react'; +import { useIntl } from 'react-intl'; +import type { Maybe, ValueOf } from '../../../types'; +import { + Time, + type SocialWebsite, + Link, + SocialLink, + Figure, +} from '../../atoms'; +import { MetaList, type MetaItemData } from '../../molecules'; +import styles from './project-overview.module.scss'; + +export type Repository = { + id: Extract; + label: string; + url: string; +}; + +export type ProjectPopularity = { + count: number; + url?: string; +}; + +export type ProjectMeta = { + creationDate: string; + lastUpdateDate: string; + license: string; + popularity: ProjectPopularity; + repositories: Repository[]; + technologies: string[]; +}; + +const validMeta = [ + 'creationDate', + 'lastUpdateDate', + 'license', + 'popularity', + 'repositories', + 'technologies', +] satisfies (keyof ProjectMeta)[]; + +const isValidMetaKey = (key: string): key is keyof ProjectMeta => + (validMeta as string[]).includes(key); + +export type ProjectOverviewProps = Omit< + HTMLAttributes, + 'children' +> & { + /** + * The project cover. + */ + cover?: ReactElement; + /** + * The project meta. + */ + meta: Partial; + /** + * The project name. + */ + name: string; +}; + +const ProjectOverviewWithRef: ForwardRefRenderFunction< + HTMLDivElement, + ProjectOverviewProps +> = ({ className = '', cover, meta, name, ...props }, ref) => { + const wrapperClass = `${styles.wrapper} ${className}`; + const intl = useIntl(); + const coverLabel = intl.formatMessage( + { + defaultMessage: 'Illustration of {projectName}', + description: 'ProjectOverview: cover accessible name', + id: '701ggm', + }, + { projectName: name } + ); + const metaLabels = { + creationDate: intl.formatMessage({ + defaultMessage: 'Created on:', + description: 'ProjectOverview: creation date label', + id: 'c0Oecl', + }), + lastUpdateDate: intl.formatMessage({ + defaultMessage: 'Updated on:', + description: 'ProjectOverview: update date label', + id: 'JbT+fA', + }), + license: intl.formatMessage({ + defaultMessage: 'License:', + description: 'ProjectOverview: license label', + id: 'QtdnFV', + }), + popularity: intl.formatMessage({ + defaultMessage: 'Popularity:', + description: 'ProjectOverview: popularity label', + id: 'cIAOyy', + }), + repositories: intl.formatMessage({ + defaultMessage: 'Repositories:', + description: 'ProjectOverview: repositories label', + id: '3bKzk0', + }), + technologies: intl.formatMessage({ + defaultMessage: 'Technologies:', + description: 'ProjectOverview: technologies label', + id: 'OWkqXt', + }), + } satisfies Record; + + const getMetaValue = useCallback( + (key: keyof ProjectMeta, value: ValueOf) => { + if (typeof value === 'string') { + return key === 'license' ? value :