diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-05-17 18:37:38 +0200 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-05-17 18:37:38 +0200 | 
| commit | 7d9f874364ec6e255e62eb3027011bfed267904c (patch) | |
| tree | 711e3ce9fba99abdff89016f15b9bc763d61f03f | |
| parent | 9a186b357b32325cf77fdce39a6c6c9aff76d8a5 (diff) | |
chore: adjust articles styles
* change animation on article card hover
* change comments section alignment
| -rw-r--r-- | src/components/atoms/buttons/buttons.module.scss | 2 | ||||
| -rw-r--r-- | src/components/atoms/headings/heading.module.scss | 16 | ||||
| -rw-r--r-- | src/components/atoms/headings/heading.stories.tsx | 26 | ||||
| -rw-r--r-- | src/components/atoms/headings/heading.tsx | 58 | ||||
| -rw-r--r-- | src/components/molecules/buttons/heading-button.module.scss | 15 | ||||
| -rw-r--r-- | src/components/molecules/buttons/heading-button.stories.tsx | 73 | ||||
| -rw-r--r-- | src/components/organisms/forms/comment-form.tsx | 14 | ||||
| -rw-r--r-- | src/components/organisms/layout/summary.module.scss | 7 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.module.scss | 9 | ||||
| -rw-r--r-- | src/components/templates/page/page-layout.tsx | 85 | ||||
| -rw-r--r-- | src/pages/article/[slug].tsx | 18 | ||||
| -rw-r--r-- | src/styles/pages/article.module.scss | 1 | 
12 files changed, 211 insertions, 113 deletions
| diff --git a/src/components/atoms/buttons/buttons.module.scss b/src/components/atoms/buttons/buttons.module.scss index 36c66b6..2444bb1 100644 --- a/src/components/atoms/buttons/buttons.module.scss +++ b/src/components/atoms/buttons/buttons.module.scss @@ -16,7 +16,7 @@    }    &--rectangle { -    padding: var(--spacing-2xs) var(--spacing-md); +    padding: var(--spacing-2xs) var(--spacing-sm);    }    &--square, diff --git a/src/components/atoms/headings/heading.module.scss b/src/components/atoms/headings/heading.module.scss index 8620f6f..a420bc1 100644 --- a/src/components/atoms/headings/heading.module.scss +++ b/src/components/atoms/headings/heading.module.scss @@ -6,11 +6,23 @@    letter-spacing: 0.01ex;    &--regular { -    margin: 0; +    margin-bottom: 0; +    margin-top: 0; +  } + +  &--left { +    text-align: left; +  } + +  &--center { +    width: fit-content; +    margin-left: auto; +    margin-right: auto;    }    &--margin { -    margin: 0 0 var(--spacing-sm); +    margin-top: 0; +    margin-bottom: var(--spacing-sm);      & + & {        margin-top: var(--spacing-md); diff --git a/src/components/atoms/headings/heading.stories.tsx b/src/components/atoms/headings/heading.stories.tsx index da5a718..0e3885d 100644 --- a/src/components/atoms/headings/heading.stories.tsx +++ b/src/components/atoms/headings/heading.stories.tsx @@ -8,10 +8,26 @@ export default {    title: 'Atoms/Typography/Headings',    component: Heading,    args: { +    alignment: 'left',      isFake: false,      withMargin: true,    },    argTypes: { +    alignment: { +      control: { +        type: 'select', +      }, +      description: 'The title alignment.', +      options: ['center', 'left'], +      table: { +        category: 'Options', +        defaultValue: { summary: 'left' }, +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      className: {        control: {          type: 'text', @@ -32,6 +48,16 @@ export default {          required: true,        },      }, +    id: { +      control: { +        type: 'text', +      }, +      description: 'An unique id.', +      type: { +        name: 'string', +        required: false, +      }, +    },      isFake: {        control: {          type: 'boolean', diff --git a/src/components/atoms/headings/heading.tsx b/src/components/atoms/headings/heading.tsx index c5bf4ca..e385249 100644 --- a/src/components/atoms/headings/heading.tsx +++ b/src/components/atoms/headings/heading.tsx @@ -1,10 +1,20 @@ -import { FC, ReactNode } from 'react'; +import { +  createElement, +  ForwardedRef, +  forwardRef, +  ForwardRefRenderFunction, +  ReactNode, +} from 'react';  import styles from './heading.module.scss';  export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;  export type HeadingProps = {    /** +   * The title alignment. Default: left; +   */ +  alignment?: 'center' | 'left'; +  /**     * The heading body.     */    children: ReactNode; @@ -30,31 +40,55 @@ export type HeadingProps = {    withMargin?: boolean;  }; +type TitleTagProps = Pick<HeadingProps, 'children' | 'className' | 'id'> & { +  tagName: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'; +}; + +const TitleTag = forwardRef< +  HTMLHeadingElement | HTMLParagraphElement, +  TitleTagProps +>( +  ( +    { children, tagName, ...props }, +    ref: ForwardedRef<HTMLHeadingElement | HTMLParagraphElement> +  ) => { +    return createElement(tagName, { ...props, ref }, children); +  } +); +TitleTag.displayName = 'TitleTag'; +  /**   * Heading component.   *   * Render an HTML heading element or a paragraph with heading styles.   */ -const Heading: FC<HeadingProps> = ({ -  children, -  className, -  id, -  isFake = false, -  level, -  withMargin = true, -}) => { -  const TitleTag = isFake ? `p` : (`h${level}` as keyof JSX.IntrinsicElements); +const Heading: ForwardRefRenderFunction<HTMLDivElement, HeadingProps> = ( +  { +    alignment = 'left', +    children, +    className, +    id, +    isFake = false, +    level, +    withMargin = true, +  }, +  ref: ForwardedRef<HTMLHeadingElement | HTMLParagraphElement> +) => { +  const tagName = isFake ? 'p' : (`h${level}` as TitleTagProps['tagName']);    const levelClass = `heading--${level}`; +  const alignmentClass = `heading--${alignment}`;    const marginClass = withMargin ? 'heading--margin' : 'heading--regular';    return (      <TitleTag -      className={`${styles.heading} ${styles[levelClass]} ${styles[marginClass]} ${className}`} +      tagName={tagName} +      className={`${styles.heading} ${styles[levelClass]} ${styles[alignmentClass]} ${styles[marginClass]} ${className}`}        id={id} +      ref={ref}      >        {children}      </TitleTag>    );  }; -export default Heading; +export default forwardRef(Heading); diff --git a/src/components/molecules/buttons/heading-button.module.scss b/src/components/molecules/buttons/heading-button.module.scss index 9c278e4..3c69221 100644 --- a/src/components/molecules/buttons/heading-button.module.scss +++ b/src/components/molecules/buttons/heading-button.module.scss @@ -20,6 +20,14 @@    border-bottom: fun.convert-px(2) solid var(--color-primary-dark);    cursor: pointer; +  .heading { +    padding: var(--spacing-2xs) 0; +    background: none; +    font-size: var(--font-size-xl); +    font-weight: 500; +    text-align: left; +  } +    &:hover,    &:focus {      .icon { @@ -34,10 +42,3 @@      }    }  } - -.heading { -  padding: var(--spacing-2xs) 0; -  background: none; -  font-size: var(--font-size-xl); -  text-align: left; -} diff --git a/src/components/molecules/buttons/heading-button.stories.tsx b/src/components/molecules/buttons/heading-button.stories.tsx index b0e1465..d1b5ed4 100644 --- a/src/components/molecules/buttons/heading-button.stories.tsx +++ b/src/components/molecules/buttons/heading-button.stories.tsx @@ -1,6 +1,5 @@  import { ComponentMeta, ComponentStory } from '@storybook/react';  import { useState } from 'react'; -import { IntlProvider } from 'react-intl';  import HeadingButtonComponent from './heading-button';  /** @@ -10,6 +9,19 @@ export default {    title: 'Molecules/Buttons/HeadingButton',    component: HeadingButtonComponent,    argTypes: { +    className: { +      control: { +        type: 'text', +      }, +      description: 'Set additional classnames to the button.', +      table: { +        category: 'Styles', +      }, +      type: { +        name: 'string', +        required: false, +      }, +    },      expanded: {        control: {          type: null, @@ -53,13 +65,6 @@ export default {        },      },    }, -  decorators: [ -    (Story) => ( -      <IntlProvider locale="en"> -        <Story /> -      </IntlProvider> -    ), -  ],  } as ComponentMeta<typeof HeadingButtonComponent>;  const Template: ComponentStory<typeof HeadingButtonComponent> = ({ @@ -79,55 +84,21 @@ const Template: ComponentStory<typeof HeadingButtonComponent> = ({  };  /** - * Heading Button Stories - Level 1 + * Heading Button Stories - Expanded   */ -export const Level1 = Template.bind({}); -Level1.args = { -  level: 1, -  title: 'Your title', -}; - -/** - * Heading Button Stories - Level 2 - */ -export const Level2 = Template.bind({}); -Level2.args = { +export const Expanded = Template.bind({}); +Expanded.args = { +  expanded: true,    level: 2,    title: 'Your title',  };  /** - * Heading Button Stories - Level 3 + * Heading Button Stories - Collapsed   */ -export const Level3 = Template.bind({}); -Level3.args = { -  level: 3, -  title: 'Your title', -}; - -/** - * Heading Button Stories - Level 4 - */ -export const Level4 = Template.bind({}); -Level4.args = { -  level: 4, -  title: 'Your title', -}; - -/** - * Heading Button Stories - Level 5 - */ -export const Level5 = Template.bind({}); -Level5.args = { -  level: 5, -  title: 'Your title', -}; - -/** - * Heading Button Stories - Level 6 - */ -export const Level6 = Template.bind({}); -Level6.args = { -  level: 6, +export const Collapsed = Template.bind({}); +Collapsed.args = { +  expanded: false, +  level: 2,    title: 'Your title',  }; diff --git a/src/components/organisms/forms/comment-form.tsx b/src/components/organisms/forms/comment-form.tsx index 5ff4ea4..b2c725f 100644 --- a/src/components/organisms/forms/comment-form.tsx +++ b/src/components/organisms/forms/comment-form.tsx @@ -1,6 +1,9 @@  import Button from '@components/atoms/buttons/button';  import Form, { type FormProps } from '@components/atoms/forms/form'; -import Heading, { type HeadingLevel } from '@components/atoms/headings/heading'; +import Heading, { +  type HeadingProps, +  type HeadingLevel, +} from '@components/atoms/headings/heading';  import Spinner from '@components/atoms/loaders/spinner';  import LabelledField from '@components/molecules/forms/labelled-field';  import { FC, ReactNode, useState } from 'react'; @@ -34,7 +37,11 @@ export type CommentFormProps = Pick<FormProps, 'className'> & {     */    title?: string;    /** -   * The title level. +   * The form title alignment. Default: left. +   */ +  titleAlignment?: HeadingProps['alignment']; +  /** +   * The title level. Default: 2.     */    titleLevel?: HeadingLevel;  }; @@ -44,6 +51,7 @@ const CommentForm: FC<CommentFormProps> = ({    parentId,    saveComment,    title, +  titleAlignment,    titleLevel = 2,    ...props  }) => { @@ -117,7 +125,7 @@ const CommentForm: FC<CommentFormProps> = ({        {...props}      >        {title && ( -        <Heading id={formId} level={titleLevel}> +        <Heading id={formId} level={titleLevel} alignment={titleAlignment}>            {title}          </Heading>        )} diff --git a/src/components/organisms/layout/summary.module.scss b/src/components/organisms/layout/summary.module.scss index 7e86dd2..62dfc0e 100644 --- a/src/components/organisms/layout/summary.module.scss +++ b/src/components/organisms/layout/summary.module.scss @@ -29,8 +29,11 @@    &:hover {      .icon { -      transform: scaleX(1.4); -      transform-origin: left; +      --icon-size: #{fun.convert-px(35)}; + +      :global { +        animation: pulse 1.5s ease-in-out 0.2s infinite; +      }      }    }  } diff --git a/src/components/templates/page/page-layout.module.scss b/src/components/templates/page/page-layout.module.scss index 83e5a80..c6b4e8d 100644 --- a/src/components/templates/page/page-layout.module.scss +++ b/src/components/templates/page/page-layout.module.scss @@ -86,7 +86,14 @@    &__section {      grid-column: 2; -    margin: var(--spacing-md) 0 0; + +    &:first-child { +      margin: var(--spacing-md) 0 0; +    } +  } + +  &__no-comments { +    text-align: center;    }    &__form { diff --git a/src/components/templates/page/page-layout.tsx b/src/components/templates/page/page-layout.tsx index f3f3ea8..2ff2084 100644 --- a/src/components/templates/page/page-layout.tsx +++ b/src/components/templates/page/page-layout.tsx @@ -1,6 +1,7 @@  import Heading from '@components/atoms/headings/heading';  import Notice, { type NoticeKind } from '@components/atoms/layout/notice';  import Sidebar from '@components/atoms/layout/sidebar'; +import { MetaData } from '@components/molecules/layout/meta';  import PageFooter, {    type PageFooterProps,  } from '@components/molecules/layout/page-footer'; @@ -118,10 +119,10 @@ const PageLayout: FC<PageLayoutProps> = ({    const isMounted = useIsMounted(bodyRef);    const hasComments = Array.isArray(comments) && comments.length > 0; -  const hasCommentsSection = hasComments || allowComments; -  const articleModifier = hasCommentsSection -    ? 'article--has-comments' -    : 'article--no-comments'; +  const articleModifier = +    hasComments || allowComments +      ? 'article--has-comments' +      : 'article--no-comments';    const [status, setStatus] = useState<NoticeKind>('info');    const [statusMessage, setStatusMessage] = useState<string>(''); @@ -175,6 +176,15 @@ const PageLayout: FC<PageLayoutProps> = ({      }    }; +  /** +   * Check if meta properties are defined. +   * +   * @param {MetaData} meta - The metadata. +   */ +  const hasMeta = (meta: MetaData) => { +    return Object.values(meta).every((value) => value === null); +  }; +    return (      <Layout        breadcrumbSchema={breadcrumbSchema} @@ -218,7 +228,9 @@ const PageLayout: FC<PageLayoutProps> = ({            {children}          </div>        )} -      <PageFooter meta={footerMeta} className={styles.footer} /> +      {footerMeta && hasMeta(footerMeta) && ( +        <PageFooter meta={footerMeta} className={styles.footer} /> +      )}        <Sidebar          className={`${styles.sidebar} ${styles['sidebar--last']}`}          aria-label={intl.formatMessage({ @@ -229,45 +241,54 @@ const PageLayout: FC<PageLayoutProps> = ({        >          {widgets}        </Sidebar> -      {hasCommentsSection && ( -        <div className={styles.comments}> -          {hasComments && ( -            <section className={styles.comments__section}> -              <Heading level={2}>{commentsTitle}</Heading> +      {allowComments && ( +        <div className={styles.comments} id="comments"> +          <section className={styles.comments__section}> +            <Heading level={2} alignment="center"> +              {commentsTitle} +            </Heading> +            {hasComments ? (                <CommentsList                  comments={comments}                  depth={1}                  Notice={ -                  isReplyRef.current === true ? ( +                  isReplyRef.current === true && (                      <Notice                        kind={status}                        message={statusMessage}                        className={styles.notice}                      /> -                  ) : undefined +                  )                  }                  saveComment={saveComment}                /> -            </section> -          )} -          {allowComments && ( -            <section className={styles.comments__section}> -              <CommentForm -                className={styles.comments__form} -                saveComment={saveComment} -                title={commentFormTitle} -                Notice={ -                  isReplyRef.current === false ? ( -                    <Notice -                      kind={status} -                      message={statusMessage} -                      className={styles.notice} -                    /> -                  ) : undefined -                } -              /> -            </section> -          )} +            ) : ( +              <p className={styles['comments__no-comments']}> +                {intl.formatMessage({ +                  defaultMessage: 'No comments.', +                  id: 'sBwfCy', +                  description: 'PageLayout: no comments text', +                })} +              </p> +            )} +          </section> +          <section className={styles.comments__section}> +            <CommentForm +              className={styles.comments__form} +              saveComment={saveComment} +              title={commentFormTitle} +              titleAlignment="center" +              Notice={ +                isReplyRef.current === false && ( +                  <Notice +                    kind={status} +                    message={statusMessage} +                    className={styles.notice} +                  /> +                ) +              } +            /> +          </section>          </div>        )}      </Layout> diff --git a/src/pages/article/[slug].tsx b/src/pages/article/[slug].tsx index 995e3a9..a0fb7fc 100644 --- a/src/pages/article/[slug].tsx +++ b/src/pages/article/[slug].tsx @@ -15,7 +15,10 @@ import { type Article, type Comment } from '@ts/types/app';  import { loadTranslation, type Messages } from '@utils/helpers/i18n';  import useAddPrismClassAttr from '@utils/hooks/use-add-prism-class-attr';  import useBreadcrumb from '@utils/hooks/use-breadcrumb'; -import usePrismPlugins, { PrismPlugin } from '@utils/hooks/use-prism-plugins'; +import usePrismPlugins, { +  type PrismPlugin, +} from '@utils/hooks/use-prism-plugins'; +import useReadingTime from '@utils/hooks/use-reading-time';  import useSettings from '@utils/hooks/use-settings';  import { GetStaticPaths, GetStaticProps, NextPage } from 'next';  import Head from 'next/head'; @@ -38,7 +41,16 @@ type ArticlePageProps = {   */  const ArticlePage: NextPage<ArticlePageProps> = ({ comments, post }) => {    const { content, id, intro, meta, slug, title } = post; -  const { author, commentsCount, cover, dates, seo, thematics, topics } = meta; +  const { +    author, +    commentsCount, +    cover, +    dates, +    seo, +    thematics, +    topics, +    wordsCount, +  } = meta;    const { data } = useSWR(() => id, getPostComments, {      fallbackData: comments,    }); @@ -46,11 +58,13 @@ const ArticlePage: NextPage<ArticlePageProps> = ({ comments, post }) => {      title,      url: `/article/${slug}`,    }); +  const readingTime = useReadingTime(wordsCount || 0, true);    const headerMeta: PageLayoutProps['headerMeta'] = {      author: author?.name,      publication: { date: dates.publication },      update: dates.update ? { date: dates.update } : undefined, +    readingTime,      thematics:        thematics &&        thematics.map((thematic) => ( diff --git a/src/styles/pages/article.module.scss b/src/styles/pages/article.module.scss index a42c633..04acad9 100644 --- a/src/styles/pages/article.module.scss +++ b/src/styles/pages/article.module.scss @@ -31,6 +31,7 @@    @include mix.media("screen") {      @include mix.dimensions("md") {        width: min-content; +      gap: var(--spacing-2xs);      }    }  } | 
