@serenis/shared-components
v0.29.0
Published
Cross-application shared components for the Serenis design system. Higher-level components built on top of `@serenis/ui` and `@serenis/cdk` that are used across multiple Serenis applications.
Keywords
Readme
@serenis/shared-components
Cross-application shared components for the Serenis design system. Higher-level components built on top of @serenis/ui and @serenis/cdk that are used across multiple Serenis applications.
Install
npm install @serenis/shared-componentsWhat's included
Components:
Nav— Responsive navigation bar with mobile overlay menu. Accepts a list ofMenuItementries, each with up to three columns of links.TableOfContent— Collapsible index of the headings of a long-form article, built from theHeadinglist returned byextractHeadings.LazyVideoWrapper— Click-to-play façade for embedded videos: theiframeis mounted only on user intent, so the third-party player is not requested on page load.Bibliography— The printed sources of an editorial article, with the citation emitted as structured data.Webliography— The online sources of an editorial article, each with an outbound link and the citation emitted as structured data.Share— The share block of an editorial article: copy the link (or open the native share sheet) plus one outbound link per social network.Faqs— The FAQ accordions of an editorial article, with the set emitted asFAQPagestructured data. Real headings only when the title was written editorially.HtmlScript— Renders a CMS-authored HTML snippet, decoding the base64 body carried by thehtmlscriptshort code. The markup is injected as-is, so only trusted editorial content may reach it.JsonLinkedData— Renders JSON-LD structured data (<script type="application/ld+json">) for SEO, with typed schemas viaschema-dts.@contextis added for every item.
Utilities:
extractHeadings— Reads the headings out of an HTML string and returns them asHeading[](depth,slug,text).slugifyChildren— Turns the text content of a React node into a slug, so rendered headings get theidanchorsTableOfContentlinks to.
All components take their user-facing strings as props: the package holds no translations, so the consuming application passes text already localized.
Usage
import { Nav, type MenuItem } from '@serenis/shared-components'
const isExternal = (url: string) => url.startsWith('http')
const Header = ({ menuItems }: { menuItems: MenuItem[] }) => (
<Nav
getUrlAttributes={(url) => (isExternal(url) ? { rel: 'noreferrer', target: '_blank' } : {})}
menuItems={menuItems}
withBannerPromo={false}
/>
)import { extractHeadings, TableOfContent } from '@serenis/shared-components'
const ArticleIndex = ({ html }: { html: string }) => (
<TableOfContent headings={extractHeadings(html)} title="In this article" />
)import { LazyVideoWrapper } from '@serenis/shared-components'
const Video = () => (
<LazyVideoWrapper
data-thumbnail="https://example.com/cover.webp"
playAriaLabel="Play the video"
src="https://player.vimeo.com/video/76979871"
title="How the therapy path works"
/>
)import { Bibliography, Webliography } from '@serenis/shared-components'
const Sources = ({ bibliography, webliography }) => (
<>
<Webliography
getOpenReferenceLabel={(title) => `Open reference: ${title}`}
items={webliography}
title="Online sources"
/>
<Bibliography items={bibliography} title="Printed sources" />
</>
)import { Share } from '@serenis/shared-components'
const ArticleShare = ({ article, url }) => (
<Share
articleTitle={article.title}
body="Share this article with whoever needs it."
copiedLabel="Copied!"
copyLinkLabel="copy link"
getShareOnLabel={(network) => `share on ${network}`}
getUrlAttributes={(url) => (url.startsWith('/') ? {} : { rel: 'noopener nofollow', target: '_blank' })}
title="Did you learn something new?"
url={url}
/>
)import { Faqs } from '@serenis/shared-components'
// A written title is promoted to an h2 and the questions to h3; the generic fallback stays a span.
const ArticleFaqs = ({ article, faqs }) => (
<Faqs
customTitle={article.titleFaq}
defaultTitle="Some questions you might have"
items={faqs}
renderAnswer={(html) => <HtmlToReact htmlString={html} />}
/>
)import { HtmlScript } from '@serenis/shared-components'
// base64body comes from the parsed article content, already encoded as UTF-8.
const EmbeddedSnippet = ({ base64body }: { base64body: string }) => <HtmlScript base64body={base64body} />import { JsonLinkedData } from '@serenis/shared-components'
const Page = () => (
<JsonLinkedData
items={[
{
'@type': 'MedicalOrganization',
name: 'Serenis',
url: 'https://www.serenis.it',
},
]}
/>
)License
PolyForm Noncommercial 1.0.0 — see LICENSE for the full text.