diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/atoms/forms/form.tsx | 3 | ||||
| -rw-r--r-- | src/components/atoms/links/link.module.scss | 23 | ||||
| -rw-r--r-- | src/components/atoms/lists/description-list-item.module.scss | 2 | ||||
| -rw-r--r-- | src/components/molecules/layout/meta.tsx | 59 | ||||
| -rw-r--r-- | src/components/molecules/layout/page-footer.tsx | 4 | ||||
| -rw-r--r-- | src/components/molecules/layout/page-header.module.scss | 6 | ||||
| -rw-r--r-- | src/components/molecules/layout/page-header.tsx | 11 | ||||
| -rw-r--r-- | src/components/organisms/forms/comment-form.tsx | 12 | ||||
| -rw-r--r-- | src/components/organisms/widgets/sharing.stories.tsx | 21 | ||||
| -rw-r--r-- | src/components/organisms/widgets/sharing.tsx | 7 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.module.scss | 6 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.stories.tsx | 2 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.tsx | 17 |
13 files changed, 105 insertions, 68 deletions
diff --git a/src/components/atoms/forms/form.tsx b/src/components/atoms/forms/form.tsx index ef8dce4..b819aea 100644 --- a/src/components/atoms/forms/form.tsx +++ b/src/components/atoms/forms/form.tsx @@ -35,7 +35,6 @@ export type FormProps = { */ const Form: FC<FormProps> = ({ children, - className = '', grouped = true, onSubmit, ...props @@ -68,7 +67,7 @@ const Form: FC<FormProps> = ({ }; return ( - <form onSubmit={handleSubmit} className={className} {...props}> + <form onSubmit={handleSubmit} {...props}> {getFormItems()} </form> ); diff --git a/src/components/atoms/links/link.module.scss b/src/components/atoms/links/link.module.scss index 5c97bd2..1b89727 100644 --- a/src/components/atoms/links/link.module.scss +++ b/src/components/atoms/links/link.module.scss @@ -2,29 +2,6 @@ @use "@styles/abstracts/variables" as var; .link { - background: linear-gradient(to top, var(--color-primary) 50%, transparent 50%) - 0 0 / 100% 201% no-repeat; - color: var(--color-primary); - text-decoration-thickness: 0.15em; - text-underline-offset: 20%; - transition: all 0.3s linear 0s, text-decoration 0.18s ease-in-out 0s; - - &:hover { - color: var(--color-primary-light); - text-decoration-thickness: 0.25em; - } - - &:focus { - background-position: 0 100%; - color: var(--color-fg-inverted); - } - - &:active { - background-position: 0 0; - color: var(--color-primary-dark); - text-decoration-thickness: 18%; - } - &[hreflang] { &::after { display: inline-block; diff --git a/src/components/atoms/lists/description-list-item.module.scss b/src/components/atoms/lists/description-list-item.module.scss index 60cad57..aba90ce 100644 --- a/src/components/atoms/lists/description-list-item.module.scss +++ b/src/components/atoms/lists/description-list-item.module.scss @@ -27,6 +27,8 @@ } &--inline-values { + row-gap: var(--spacing-2xs); + .term { flex: 1 1 100%; } diff --git a/src/components/molecules/layout/meta.tsx b/src/components/molecules/layout/meta.tsx index 1f6219a..74bd4ff 100644 --- a/src/components/molecules/layout/meta.tsx +++ b/src/components/molecules/layout/meta.tsx @@ -92,13 +92,17 @@ export type MetaData = { */ topics?: string[] | JSX.Element[]; /** - * A total. + * A total number of posts. */ - total?: string; + total?: number; /** * The update date. */ update?: MetaDate; + /** + * An url. + */ + website?: string; }; export type MetaKey = keyof MetaData; @@ -145,80 +149,86 @@ const Meta: FC<MetaProps> = ({ case 'author': return intl.formatMessage({ defaultMessage: 'Written by:', - id: 'OI0N37', description: 'Meta: author label', + id: 'OI0N37', }); case 'comments': return intl.formatMessage({ defaultMessage: 'Comments:', - id: 'jTVIh8', description: 'Meta: comments label', + id: 'jTVIh8', }); case 'creation': return intl.formatMessage({ defaultMessage: 'Created on:', - id: 'b4fdYE', description: 'Meta: creation date label', + id: 'b4fdYE', }); case 'license': return intl.formatMessage({ defaultMessage: 'License:', - id: 'AuGklx', description: 'Meta: license label', + id: 'AuGklx', }); case 'popularity': return intl.formatMessage({ defaultMessage: 'Popularity:', - id: 'pWTj2W', description: 'Meta: popularity label', + id: 'pWTj2W', }); case 'publication': return intl.formatMessage({ defaultMessage: 'Published on:', - id: 'QGi5uD', description: 'Meta: publication date label', + id: 'QGi5uD', }); case 'readingTime': return intl.formatMessage({ defaultMessage: 'Reading time:', - id: 'EbFvsM', description: 'Meta: reading time label', + id: 'EbFvsM', }); case 'repositories': return intl.formatMessage({ defaultMessage: 'Repositories:', - id: 'DssFG1', description: 'Meta: repositories label', + id: 'DssFG1', }); case 'technologies': return intl.formatMessage({ defaultMessage: 'Technologies:', - id: 'ADQmDF', description: 'Meta: technologies label', + id: 'ADQmDF', }); case 'thematics': return intl.formatMessage({ defaultMessage: 'Thematics:', - id: 'bz53Us', description: 'Meta: thematics label', + id: 'bz53Us', }); case 'topics': return intl.formatMessage({ defaultMessage: 'Topics:', - id: 'gJNaBD', description: 'Meta: topics label', + id: 'gJNaBD', }); case 'total': return intl.formatMessage({ defaultMessage: 'Total:', - id: '92zgdp', description: 'Meta: total label', + id: '92zgdp', }); case 'update': return intl.formatMessage({ defaultMessage: 'Updated on:', - id: 'tLC7bh', description: 'Meta: update date label', + id: 'tLC7bh', + }); + case 'website': + return intl.formatMessage({ + defaultMessage: 'Official website:', + description: 'Meta: official website label', + id: 'GRyyfy', }); default: return ''; @@ -279,8 +289,8 @@ const Meta: FC<MetaProps> = ({ { defaultMessage: '{commentsCount, plural, =0 {No comments} one {# comment} other {# comments}}<a11y> about {title}</a11y>', - id: '02rgLO', description: 'Meta: comments count', + id: '02rgLO', }, { a11y: (chunks: ReactNode) => ( @@ -316,6 +326,23 @@ const Meta: FC<MetaProps> = ({ case 'publication': case 'update': return getDate(value as MetaDate); + case 'total': + return intl.formatMessage( + { + defaultMessage: + '{postsCount, plural, =0 {No articles} one {# article} other {# articles}}', + description: 'BlogPage: posts count meta', + id: 'OF5cPz', + }, + { postsCount: value as number } + ); + case 'website': + const url = value as string; + return ( + <Link href={url} external={true}> + {url} + </Link> + ); default: return value as string | ReactNode | ReactNode[]; } diff --git a/src/components/molecules/layout/page-footer.tsx b/src/components/molecules/layout/page-footer.tsx index e998b1e..97e449f 100644 --- a/src/components/molecules/layout/page-footer.tsx +++ b/src/components/molecules/layout/page-footer.tsx @@ -19,7 +19,9 @@ export type PageFooterProps = { */ const PageFooter: FC<PageFooterProps> = ({ meta, ...props }) => { return ( - <footer {...props}>{meta && <Meta data={meta} layout="column" />}</footer> + <footer {...props}> + {meta && <Meta data={meta} withSeparator={false} />} + </footer> ); }; diff --git a/src/components/molecules/layout/page-header.module.scss b/src/components/molecules/layout/page-header.module.scss index 4c7df5f..232023a 100644 --- a/src/components/molecules/layout/page-header.module.scss +++ b/src/components/molecules/layout/page-header.module.scss @@ -56,3 +56,9 @@ .meta { font-size: var(--font-size-sm); } + +.intro { + > *:last-child { + margin-bottom: 0; + } +} diff --git a/src/components/molecules/layout/page-header.tsx b/src/components/molecules/layout/page-header.tsx index 9abe9af..6759c7f 100644 --- a/src/components/molecules/layout/page-header.tsx +++ b/src/components/molecules/layout/page-header.tsx @@ -1,5 +1,5 @@ import Heading from '@components/atoms/headings/heading'; -import { FC } from 'react'; +import { FC, ReactNode } from 'react'; import Meta, { type MetaData } from './meta'; import styles from './page-header.module.scss'; @@ -19,7 +19,7 @@ export type PageHeaderProps = { /** * The page title. */ - title: string; + title: ReactNode; }; /** @@ -35,9 +35,12 @@ const PageHeader: FC<PageHeaderProps> = ({ }) => { const getIntro = () => { return typeof intro === 'string' ? ( - <div dangerouslySetInnerHTML={{ __html: intro }} /> + <div + className={styles.intro} + dangerouslySetInnerHTML={{ __html: intro }} + /> ) : ( - <div>{intro}</div> + <div className={styles.intro}>{intro}</div> ); }; diff --git a/src/components/organisms/forms/comment-form.tsx b/src/components/organisms/forms/comment-form.tsx index 9e0abdf..5ff4ea4 100644 --- a/src/components/organisms/forms/comment-form.tsx +++ b/src/components/organisms/forms/comment-form.tsx @@ -1,5 +1,5 @@ import Button from '@components/atoms/buttons/button'; -import Form from '@components/atoms/forms/form'; +import Form, { type FormProps } from '@components/atoms/forms/form'; import Heading, { type HeadingLevel } from '@components/atoms/headings/heading'; import Spinner from '@components/atoms/loaders/spinner'; import LabelledField from '@components/molecules/forms/labelled-field'; @@ -15,11 +15,7 @@ export type CommentFormData = { website?: string; }; -export type CommentFormProps = { - /** - * Set additional classnames to the form wrapper. - */ - className?: string; +export type CommentFormProps = Pick<FormProps, 'className'> & { /** * Pass a component to print a success/error message. */ @@ -44,12 +40,12 @@ export type CommentFormProps = { }; const CommentForm: FC<CommentFormProps> = ({ - className = '', Notice, parentId, saveComment, title, titleLevel = 2, + ...props }) => { const intl = useIntl(); const [name, setName] = useState<string>(''); @@ -116,9 +112,9 @@ const CommentForm: FC<CommentFormProps> = ({ return ( <Form onSubmit={submitHandler} - className={className} aria-label={formAriaLabel} aria-labelledby={formLabelledBy} + {...props} > {title && ( <Heading id={formId} level={titleLevel}> diff --git a/src/components/organisms/widgets/sharing.stories.tsx b/src/components/organisms/widgets/sharing.stories.tsx index 47213b6..59b86d3 100644 --- a/src/components/organisms/widgets/sharing.stories.tsx +++ b/src/components/organisms/widgets/sharing.stories.tsx @@ -1,5 +1,4 @@ import { ComponentMeta, ComponentStory } from '@storybook/react'; -import { IntlProvider } from 'react-intl'; import SharingWidget from './sharing'; /** @@ -9,6 +8,19 @@ export default { title: 'Organisms/Widgets', component: SharingWidget, argTypes: { + className: { + control: { + type: 'text', + }, + description: 'Set additional classnames to the sharing links list.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, data: { description: 'The page data.', type: { @@ -58,13 +70,6 @@ export default { }, }, }, - decorators: [ - (Story) => ( - <IntlProvider locale="en"> - <Story /> - </IntlProvider> - ), - ], } as ComponentMeta<typeof SharingWidget>; const Template: ComponentStory<typeof SharingWidget> = (args) => ( diff --git a/src/components/organisms/widgets/sharing.tsx b/src/components/organisms/widgets/sharing.tsx index 85dadb0..c63f5db 100644 --- a/src/components/organisms/widgets/sharing.tsx +++ b/src/components/organisms/widgets/sharing.tsx @@ -23,6 +23,10 @@ export type SharingData = { export type SharingProps = { /** + * Set additional classnames to the sharing links list. + */ + className?: string; + /** * The page data to share. */ data: SharingData; @@ -46,6 +50,7 @@ export type SharingProps = { * Render a list of sharing links inside a widget. */ const Sharing: FC<SharingProps> = ({ + className = '', data, media, expanded = true, @@ -201,7 +206,7 @@ const Sharing: FC<SharingProps> = ({ return ( <Widget expanded={expanded} level={level} title={widgetTitle} {...props}> - <ul className={styles.list}>{getItems()}</ul> + <ul className={`${styles.list} ${className}`}>{getItems()}</ul> </Widget> ); }; diff --git a/src/components/templates/page/page-layout.module.scss b/src/components/templates/page/page-layout.module.scss index 7602492..83e5a80 100644 --- a/src/components/templates/page/page-layout.module.scss +++ b/src/components/templates/page/page-layout.module.scss @@ -72,6 +72,7 @@ .footer { grid-column: 2; + margin: var(--spacing-sm) 0 var(--spacing-2xs); } .comments { @@ -87,4 +88,9 @@ grid-column: 2; margin: var(--spacing-md) 0 0; } + + &__form { + max-width: 40ch; + margin: auto; + } } diff --git a/src/components/templates/page/page-layout.stories.tsx b/src/components/templates/page/page-layout.stories.tsx index 002b951..8af5f98 100644 --- a/src/components/templates/page/page-layout.stories.tsx +++ b/src/components/templates/page/page-layout.stories.tsx @@ -473,7 +473,7 @@ export const Blog = Template.bind({}); Blog.args = { breadcrumb: postsListBreadcrumb, title: 'Blog', - headerMeta: { total: `${posts.length} posts` }, + headerMeta: { total: posts.length }, children: ( <> <PostsList posts={posts} byYear={true} total={posts.length} /> diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx index bc90f4c..f3f3ea8 100644 --- a/src/components/templates/page/page-layout.tsx +++ b/src/components/templates/page/page-layout.tsx @@ -20,7 +20,7 @@ import TableOfContents from '@components/organisms/widgets/table-of-contents'; import { type SendCommentVars } from '@services/graphql/api'; import { sendComment } from '@services/graphql/comments'; import useIsMounted from '@utils/hooks/use-is-mounted'; -import { FC, ReactNode, useRef, useState } from 'react'; +import { FC, HTMLAttributes, ReactNode, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; import Layout, { type LayoutProps } from '../layout/layout'; import styles from './page-layout.module.scss'; @@ -33,6 +33,11 @@ export type PageLayoutProps = Pick< * True if the page accepts new comments. Default: false. */ allowComments?: boolean; + bodyAttributes?: HTMLAttributes<HTMLDivElement>; + /** + * Set additional classnames to the body wrapper. + */ + bodyClassName?: string; /** * The breadcrumb items. */ @@ -83,6 +88,8 @@ export type PageLayoutProps = Pick< const PageLayout: FC<PageLayoutProps> = ({ children, allowComments = false, + bodyAttributes, + bodyClassName = '', breadcrumb, breadcrumbSchema, comments, @@ -91,8 +98,8 @@ const PageLayout: FC<PageLayoutProps> = ({ id, intro, isHome = false, - widgets, title, + widgets, withToC = false, }) => { const intl = useIntl(); @@ -202,11 +209,12 @@ const PageLayout: FC<PageLayoutProps> = ({ {typeof children === 'string' ? ( <div ref={bodyRef} - className={styles.body} + className={`${styles.body} ${bodyClassName}`} dangerouslySetInnerHTML={{ __html: children }} + {...bodyAttributes} /> ) : ( - <div ref={bodyRef} className={styles.body}> + <div ref={bodyRef} className={`${styles.body} ${bodyClassName}`}> {children} </div> )} @@ -245,6 +253,7 @@ const PageLayout: FC<PageLayoutProps> = ({ {allowComments && ( <section className={styles.comments__section}> <CommentForm + className={styles.comments__form} saveComment={saveComment} title={commentFormTitle} Notice={ |
