Skip to content

Commit

Permalink
feat: Add Meta component (#365)
Browse files Browse the repository at this point in the history
* feat: add Meta component

* refactor: change Meta from class to functional component

remove unused "defaultProps" variable

* fix: change types for opengraph tags

Add type for opengraph tags
Add type for Pick fields with specific type

* fix: remove ts-ignore line
  • Loading branch information
Pavel-Tyan authored Jan 29, 2025
1 parent 9ef615c commit 4e91ec1
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
110 changes: 110 additions & 0 deletions src/components/Meta/Meta.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React from 'react';
import {compose} from 'react-recompose';

import {MetaProps, SocialSharingMeta} from '../SocialSharingMeta';
import {DocMeta, DocMetaProps} from '../DocMeta';
import withRouter, {WithRouterProps} from '../../hoc/withRouter';
import withLang, {WithLangProps} from '../../hoc/withLang';
import {sanitizeHtml} from '../../utils/sanitize';

interface SharingProps {
title?: string;
description?: string;
}

export interface MetaComponentProps extends DocMetaProps {
type?: string;
url?: string;
image?: string;
locale?: string;
sharing?: SharingProps;
extra?: MetaProps[];
description?: string;
keywords?: string | string[];
noIndex?: boolean;
canonical?: string;
alternate?: Record<string, string>;
schemaJsonLd?: unknown;
isAwsServer?: boolean;
metadata?: Record<string, string>[];
}

type MetaComponentInnerProps = MetaComponentProps & WithRouterProps & WithLangProps;

type PickByType<T, K> = {
[P in keyof T as T[P] extends K | undefined ? P : never]: T[P];
};

interface OpenGraphTags {
[key: string]: string | undefined;
}

const Meta: React.FC<MetaComponentInnerProps> = (props) => {
const overrideOpenGraphgTags = (): OpenGraphTags => {
const {metadata = [], ...rest} = props;

const result: OpenGraphTags = rest as PickByType<typeof rest, string>;

for (const meta of metadata) {
const {property, content} = meta;
if (property?.startsWith('og')) {
result[property.slice(3, property.length)] = content;
}
}

return result;
};

const {
extra,
router,
description,
sharing = {},
keywords,
noIndex,
canonical,
alternate,
...documentMetaProps
} = props;

const {
type,
url,
locale,
image,
title,
description: ogDescription,
}: OpenGraphTags = overrideOpenGraphgTags();

const sharingTitle = sanitizeHtml(title);
const sharingDescription = sanitizeHtml(ogDescription || sharing.description);
const metaDescription = sanitizeHtml(description);
const metaKeywords = Array.isArray(keywords) ? keywords.join(',') : keywords;
const fullUrl = typeof window === 'undefined' ? router.pathname : window.location.href;

return (
<React.Fragment>
<DocMeta {...documentMetaProps} />
<SocialSharingMeta
type={type}
url={url || fullUrl}
locale={locale}
title={sharingTitle}
description={sharingDescription}
image={image}
extra={extra}
>
{noIndex && <meta name="robots" content="noindex, nofollow" />}
{canonical && <link rel="canonical" href={canonical} />}
{alternate &&
Object.keys(alternate).map((key) => (
<link key={key} rel="alternate" hrefLang={key} href={alternate[key]} />
))}
{description && <meta name="description" content={metaDescription} />}
{keywords && <meta name="keywords" content={metaKeywords} />}
</SocialSharingMeta>
</React.Fragment>
);
};

export default compose<MetaComponentInnerProps, MetaComponentProps>(withRouter, withLang)(Meta);
2 changes: 2 additions & 0 deletions src/components/Meta/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export {default as Meta} from './Meta';
export * from './Meta';

0 comments on commit 4e91ec1

Please sign in to comment.