summaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/atoms/forms/form.tsx3
-rw-r--r--src/components/atoms/links/link.module.scss23
-rw-r--r--src/components/atoms/lists/description-list-item.module.scss2
-rw-r--r--src/components/molecules/layout/meta.tsx59
-rw-r--r--src/components/molecules/layout/page-footer.tsx4
-rw-r--r--src/components/molecules/layout/page-header.module.scss6
-rw-r--r--src/components/molecules/layout/page-header.tsx11
-rw-r--r--src/components/organisms/forms/comment-form.tsx12
-rw-r--r--src/components/organisms/widgets/sharing.stories.tsx21
-rw-r--r--src/components/organisms/widgets/sharing.tsx7
-rw-r--r--src/components/templates/page/page-layout.module.scss6
-rw-r--r--src/components/templates/page/page-layout.stories.tsx2
-rw-r--r--src/components/templates/page/page-layout.tsx17
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={