Product Card
A product card displays information about a product including title, price, description, and benefits.
Installation
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.
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
andHospitalTiersBadgeModalModal
individually.
Interactive demo
Note:
GreyBox
is not a product card component, it is purely for demonstration purposes.
Props
HighlightMessage
Prop | Type | Default | Description |
---|---|---|---|
children (required) | node | The 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.
Prop | Type | Default | Description |
---|---|---|---|
type (required) | string | The type of product the card is displaying. Must be one of hospital , extras or combined . | |
tier | string | The 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' . | |
space | number or object | Spacing 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 . | |
children | node | The contents of the card. |
ProductCardHeader
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.
Prop | Type | Default | Description |
---|---|---|---|
type (required) | string | The type of product the card is displaying. Must be one of hospital , extras or combined . | |
tier | string | The 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' . | |
space | number or object | 6 | Spacing 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 . |
height | number | A min-height for the prduct card header, in pixels. | |
isLoading | boolean | false | Renders the Loader component if true. |
children | node | The contents of the card. |
The ProductCardHeader
has a number of sub-components.
<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:
<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
Prop | Type | Default | Description |
---|---|---|---|
hasDisclaimer | boolean | false | Whether the price should have an asterisk appended to the end. |
titleComponent | string | The 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.
Prop | Type | Default | Description |
---|---|---|---|
space | number or object | 6 | Spacing 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 . |
isUnavailable | boolean | false | Renders an overlay over the product card body when unavailable. |
children | node | The 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.
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:
<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
Prop | Type | Default | Description |
---|---|---|---|
tier (required) | string | The 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:
<HospitalTiersBadgeModal.Button onClick={handleOpenModal} as="h5">Basic Hospital</HospitalTiersBadgeModal.Button><HospitalTiersBadgeModal.Modal visible={isVisible} onClose={handleCloseModal} tier={tier} mbpUrl={mbpUrl} />
HospitalTiersBadgeModal.Button
Prop | Type | Default | Description |
---|---|---|---|
onClick (required) | function | An event handler function to open modal. | |
children (required) | string | The button label. | |
as | string or React Component | h4 | Applied to underlying Heading component to change heading level. |
HospitalTiersBadgeModal.Modal
Prop | Type | Default | Description |
---|---|---|---|
visible (required) | boolean | Whether the Modal is being displayed. | |
onClose (required) | function | An event handler function to close modal. | |
tier (required) | string | The 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' . | |
mbpUrl | string | The button label. | |
titleComponent | string | The 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.
Prop | Type | Default | Description |
---|---|---|---|
height (required) | number or object | The 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.
Prop | Type | Default | Description |
---|---|---|---|
breakpoint | string | 'xxl' | Must be one of our standard breakpoints: 'xs' , 'sm' , 'md' , 'lg' , 'xl' , 'xxl' or 'xxxl' . |
space | number or object | Spacing 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.
Prop | Type | Default | Description |
---|---|---|---|
isStatic | boolean | false | To just display the inclusion name and coverType icon, with no onCLick or modal for further detail. |
inclusions (required) | array | Each item in the array should match the below structure. | |
products (required) | array | Each item in the array should match the below structure. | |
productId | number | The id of the product. Required for extras products with dynamic inclusion lists to highlight the correct row in the AnnualLimitsTable . | |
limitsForActiveServiceForEachProduct | array | Each item in the array should match the below structure. | |
onInclusionModalOpen | function | A function to call when the inclusion modal opens. | |
onInclusionModalClose | function | A function to call when the inclusion modal closes. | |
policyBookletLink | string | '/docs/policy-booklet' | Each. |
inclusionListViewMode | string | The mode in which the inclusions are displayed, either 'namedGroups', 'namedGroupsAccordion' or 'none'/undefined for default. | |
onServCompGroupAccordionToggle | function | Callback for for user interaction with accordions under 'namedGroupsAccordion' view mode. | |
expandedServCompGroups | array | Array of service component groups that should be expanded. | |
serviceComponent | object | When supplied, informs the component that ServiceComponent inclusions are being displayed, will adjust the modal accordingly. |
Dynamic inclusion
{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:
{id: PropTypes.number,name: PropTypes.string,coveredTypeCode: PropTypes.oneOf(['Inclusion', 'Exclusion', 'MBP', 'etc.']),}
Products
An array of products that match the following shape:
{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:
{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
.
policyBookletLink
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.