Design Tokens for Mesh have just been released!
Skip to content

Product Card

A product card displays information about a product including title, price, description, and benefits.

Installation

bash
npm install @nib-components/product-card

Note: You will also need to install the peerDependencies @nib/icons and @nib-components/theme.

Usage

There is no default export for the product card package. Instead, there are a collection of components for you to compose together to fit your specific needs.

jsx
import {ProductCardWrapper, ProductCardHeader, ProductCardBody, HighlightMessage, HospitalTiersBadgeModal, MinHeight, InclusionList, ResponsiveInclusionList} from '@nib-components/product-card';

Note: If you are using this component within a React Server Component (like the NextJS 13 App Router), it is suggested to import ProductCardHeaderName, ProductCardHeaderPrice, ProductCardHeaderPaymentFrequency,ProductCardHeaderSmallText, ProductCardHeaderStrikeThroughPrice, HospitalTiersBadgeModalButton and HospitalTiersBadgeModalModal individually.

Interactive demo

jsx

Note: GreyBox is not a product card component, it is purely for demonstration purposes.

Props

HighlightMessage

PropTypeDefaultDescription
children (required)nodeThe message to display above the highlighted product card. All children will be rendered inside our <Copy> component.

ProductCardWrapper

All props passed to <ProductCardWrapper/> will be applied to the underlying <div> as attributes.

PropTypeDefaultDescription
type (required)stringThe type of product the card is displaying. Must be one of hospital, extras or combined.
tierstringThe tier of the hospital product. Not to be used on extras. Must be one of 'basic', 'basic-plus', 'bronze', 'bronze-plus', 'silver', 'silver-plus' or 'gold'.
spacenumber or objectSpacing value to be passed to internal stack component. A size from our spacing scale. Can be made responsive by passing an object of breakpoints. Value(s) must be one of 1...10.
childrennodeThe contents of the card.

ProductCardHeader

jsx

Note: GreyBox is not a product card component, it is purely for demonstration purposes.

All props passed to <ProductCardHeader/> will be applied to the underlying <div> as attributes.

PropTypeDefaultDescription
type (required)stringThe type of product the card is displaying. Must be one of hospital, extras or combined.
tierstringThe tier of the hospital product. Not to be used on extras. Must be one of 'basic', 'basic-plus', 'bronze', 'bronze-plus', 'silver', 'silver-plus' or 'gold'.
spacenumber or object6Spacing value to be passed to internal stack component. A size from our spacing scale. Can be made responsive by passing an object of breakpoints. Value(s) must be one of 1...10.
heightnumberA min-height for the prduct card header, in pixels.
isLoadingbooleanfalseRenders the Loader component if true.
childrennodeThe contents of the card.

The ProductCardHeader has a number of sub-components.

jsx
<ProductCardHeader.Name>Basic Hospital</ProductCardHeader.Name>
<ProductCardHeader.Price hasDisclaimer>45.00</ProductCardHeader.Price>
<ProductCardHeader.PaymentFrequency>Weekly</ProductCardHeader.PaymentFrequency>
<ProductCardHeader.SmallText></ProductCardHeader.SmallText>
<ProductCardHeader.StrikeThroughPrice>900.00</ProductCardHeader.StrikeThroughPrice>

All subcomponents expect children.

ProductCardHeader.Name

The product name is rendered as an <h3> by default. It is important that pages have semantic heading hierarchy for screen reader users.

To alter this heading level to suit your page structure you can use the styled-components polymorphic as prop:

jsx
<ProductCardHeader.Name as="h4">Basic Hospital</ProductCardHeader.Name>
<ProductCardHeader.Name as="div">Basic Hospital</ProductCardHeader.Name> //If you cannot know for sure the heading hierarchy of the page a div is safest

ProductCardHeader.Price

PropTypeDefaultDescription
hasDisclaimerbooleanfalseWhether the price should have an asterisk appended to the end.
titleComponentstringThe underlying component of the ProductCardHeader Price. Must be one of h1, h2, h3, h4, h5, h6, div, label, span, header.

ProductCardBody

All props passed to <ProductCardBody/> will be applied to the underlying <div> as attributes.

PropTypeDefaultDescription
spacenumber or object6Spacing value to be passed to internal stack component. A size from our spacing scale. Can be made responsive by passing an object of breakpoints. Value(s) must be one of 1...10.
isUnavailablebooleanfalseRenders an overlay over the product card body when unavailable.
childrennodeThe contents of the card.

HospitalTiersBadgeModal

A button that opens a modal to explain the hospital tiers, highlighting the passed in tier. -plus tiers are omitted from the modal for brevity unless the selected tier is one of "basic-plus", "bronze-plus" or "silver-plus".

Should not be included in Extras product cards.

jsx

The hospital tier heading is rendered as an <h4> within a <button> by default. It is important that pages have semantic heading hierarchy for screen reader users.

To alter this heading level to suit your page structure you can use the styled-components polymorphic as prop:

jsx
<HospitalTiersBadgeModal tier="silver-plus" as="h5">
<HospitalTiersBadgeModal tier="silver-plus" as="div"> //If you cannot know for sure the heading hierarchy of the page a div is safest
PropTypeDefaultDescription
tier (required)stringThe tier of the hospital product. Not to be used on extras. Must be one of 'basic', 'basic-plus', 'bronze', 'bronze-plus', 'silver', 'silver-plus' or 'gold'.

The HospitalTiersBadgeModal has a number of sub-components, which are not to be used in conjunction with parent component but to allow different implementations:

jsx
<HospitalTiersBadgeModal.Button onClick={handleOpenModal} as="h5">Basic Hospital</HospitalTiersBadgeModal.Button>
<HospitalTiersBadgeModal.Modal visible={isVisible} onClose={handleCloseModal} tier={tier} mbpUrl={mbpUrl} />

HospitalTiersBadgeModal.Button

PropTypeDefaultDescription
onClick (required)functionAn event handler function to open modal.
children (required)stringThe button label.
asstring or React Componenth4Applied to underlying Heading component to change heading level.

HospitalTiersBadgeModal.Modal

PropTypeDefaultDescription
visible (required)booleanWhether the Modal is being displayed.
onClose (required)functionAn event handler function to close modal.
tier (required)stringThe tier of the hospital product. Not to be used on extras. Must be one of 'basic', 'basic-plus', 'bronze', 'bronze-plus', 'silver', 'silver-plus' or 'gold'.
mbpUrlstringThe button label.
titleComponentstringThe underlying component of the HospitalTiersBadgeModal TiersModal. Must be one of h1, h2, h3, h4, h5, h6, div, label, span, header.

MinHeight

Forcing subsections of product cards to align with different content lengths unfortunately requires setting some fixed min-heights.

PropTypeDefaultDescription
height (required)number or objectThe value in px for the min-height. Useful for faking equal height product cards with varying contents. Can be made responsive by passing an object of breakpoints.

ResponsiveInclusionList

If product cards are stacking on mobile, inclusions list should collapse behind a toggle to save vertical space. This component hides the inclusion list below the xxl breakpoint by default.

PropTypeDefaultDescription
breakpointstring'xxl'Must be one of our standard breakpoints: 'xs', 'sm', 'md', 'lg', 'xl', 'xxl' or 'xxxl'.
spacenumber or objectSpacing value to be passed to internal stack component. A size from our spacing scale. Can be made responsive by passing an object of breakpoints. Value(s) must be one of 1...10.

InclusionList

A list of inclusions. Static or dynamic.

PropTypeDefaultDescription
isStaticbooleanfalseTo just display the inclusion name and coverType icon, with no onCLick or modal for further detail.
inclusions (required)arrayEach item in the array should match the below structure.
products (required)arrayEach item in the array should match the below structure.
productIdnumberThe id of the product. Required for extras products with dynamic inclusion lists to highlight the correct row in the AnnualLimitsTable.
limitsForActiveServiceForEachProductarrayEach item in the array should match the below structure.
onInclusionModalOpenfunctionA function to call when the inclusion modal opens.
onInclusionModalClosefunctionA function to call when the inclusion modal closes.
policyBookletLinkstring'/docs/policy-booklet'Each.
inclusionListViewModestringThe mode in which the inclusions are displayed, either 'namedGroups', 'namedGroupsAccordion' or 'none'/undefined for default.
onServCompGroupAccordionTogglefunctionCallback for for user interaction with accordions under 'namedGroupsAccordion' view mode.
expandedServCompGroupsarrayArray of service component groups that should be expanded.
serviceComponentobjectWhen supplied, informs the component that ServiceComponent inclusions are being displayed, will adjust the modal accordingly.

Dynamic inclusion

js
{
id: PropTypes.number,
name: PropTypes.string,
description: PropTypes.string,
coveredTypeCode: PropTypes.oneOf(['Inclusion', 'Exclusion', 'MBP', 'etc.']),
waitingPeriod: PropTypes.string
}

Static inclusion

If you have a static inclusion list you can probably get away with:

js
{
id: PropTypes.number,
name: PropTypes.string,
coveredTypeCode: PropTypes.oneOf(['Inclusion', 'Exclusion', 'MBP', 'etc.']),
}

Products

An array of products that match the following shape:

js
{
claimsPercentage: PropTypes.number,
description: PropTypes.string,
id: PropTypes.number,
name: PropTypes.string,
productServices: PropTypes.array,
type: PropTypes.string
}

productId

The id of the product. Required for extras products with dynamic inclusion lists to highlight the correct row in the AnnualLimitsTable.

Optional. A number.

limitsForActiveServiceForEachProduct

For a given serviceId, an array of annual limit information for each product that has the service as an inclusion.

To be dynamically passed to InclusionList based upon the service clicked.

Required. An array of inclusions.

Each inclusion should match the following structure:

js
{
annualIncrease: PropTypes.number,
applyToName: PropTypes.string,
limitAmount: PropTypes.number,
limitText: PropTypes.string,
maxLimitPerTimePeriod: PropTypes.number,
productId: PropTypes.number,
productName: PropTypes.string,
serviceId: PropTypes.number,
timePeriod: PropTypes.string
}

onInclusionModalOpen

A function that is called as the inclusion modal opens. Generally to be used with an extras product to load the appropriate annualLimits data for the active inclusion.

You must ensure that the products prop is also passed in to the InclusionList as this function is called with fixed arguments:

onInclusionModalOpen(inclusion.id, products)

This hook is very specific to our usecase and will likely not support any other cases. If you need this function to be more flexible for your usecase, please open an issue or PR.

onInclusionModalClose

A function that is called as the inclusion modal closes. Generally to be used with an extras product to clear the annualLimits data for the active inclusion.

The url of the link to the policy booklet referenced in many of the modals. Not needed if your InclusionList is static. Required if your product card is being whitelabelled.

Optional. A string. Defaults to '/docs/policy-booklet'.

Considerations

Product card groups

Product cards can be used individually or in a group of two or more cards. When displaying a product card group, arrange the products in a continuum from cheapest (first) to most expensive (last).

Product highlight

A product card can contain a product highlight, appearing as a sentence above a product card. A product highlight assigns a card more emphasis within a product card group. Only highlight one card in a product card group and should be used to call out a recommended product or value for money product.

Long benefit lists

If you have a long benefit list, users may not scroll down beyond the list. Ensure no pertinent content appears below the product card, or alternatively apply a toggle to the benefit list so it is hidden until a user requests it.