@newskit-render/shared-components

v4.37.1

Published

Newskit Render Shared Components

Downloads

2,749

Readme

@newskit-render/shared-components

sharedTheme

Based on the Newskit Light Theme, the Shared Theme can be used as a base for every Newskit Solutions application. It overrides the brand color, adds custom presets and/or componentDefaults to some of the components in shared-components. You can extend it in the same way it extends Newskit Light theme.

Back Button

The Back Button is a Next.js's next/link wrapper for a Newskit Button or link. It enables native Next.js routing.

  • Props:
interface BackButtonProps {
  backButton?: TBackButton
  backButtonOverrides?: BackButtonOverrides
}

interface TBackButton {
  text: string
  href: string
  'aria-label': string
}

export interface BackButtonOverrides {
  stylePreset?: MQ<string>
  typographyPreset?: MQ<string>
  paddingInline?: MQ<string>
  paddingBlock?: MQ<string>
  marginBlockEnd?: MQ<string>
  iconSize?: MQ<string>
  size?: 'small' | 'medium' | 'large'
  asLink?: boolean
}

ButtonGroup

The group is a component that combines a standard group of Primary and Secondary button. The group can also contain a flag, marking a secure operation. The ButtonGroup can have only one button, (either Primery or Secondary), or both buttons. Each button is a separate component with the following props:

type ButtonProps = Omit<NewkitButtonProps, 'children' | 'size'> & {
  ariaLabel?: string
  text?: string
  href?: string
  onClick?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
  size?: ButtonSize
}

Each button is styled independetly and has its own componentDefaults in the theme. You can override those by passing your own size and overrides props to each button via the context.

The ButtonGroup also has it's own stylePreset, that is used as a componentDefault in the theme. The ButtonGroup component takes the following:

  • Props:
type ButtonGroupProps = {
  loading?: boolean
  secureFlag?: boolean
  secureFlagOverrides?: SecureFlagProps
  primaryButton?: ButtonProps
  secondaryButton?: ButtonProps
  stylePreset?: MQ<string>
  keepFixed?: boolean
  breakPoint?: BreakpointKeys
}

The stylePreset is used to override the default stylePreset of the ButtonGroup. The primaryButton and secondaryButton are used to pass context (props) to the <PrimaryButton /> and <SecondaryButton />: The keepFixed property is used to tell the group if it should be kept at a fixed position at the bottom of the screen on mobile / smaller screens. The secureFlag property is used to tell the group if it should display the flag, that indicates secure operations. The SecureFlag is it's own component, exported from shared-components. go to SecureFlag

Calendar

This component is based on the Airbnb's react-dates calendar picker, enhanced with the ablity to be customized by using newskit design tokens. The end user can select a range of dates. The components accepts the following arguments:

  • overrides?: CalendarStylesProps | optional. If not passed, the Calendar will use the default values. This prop is used to override the style of the calendar and has the following shape:
interface CalendarStylesProps {
  calendar: {
    typographyPreset: string // sets the global typography preset for the component
    borders: {
      stylePreset: string //defines the border style for the calendar; this border surrounds both/all months
    }
    transitionContainer: {
      borders: {
        stylePreset: string //this container comes from 'react-dates' and the same style preset as above is needed here
      }
    }
    day: { // defines the styles for the individual days and the different states they can be in.
      color: string
      backgroundColor: string
      typographyPreset: string
      hover: {
        backgroundColor: string
      }
      selected: {
        color: string
        backgroundColor: string
        active: {
          hover: {
            color: string
            backgroundColor: string
          }
        }
      }
      blocked: {
        color: string
        outOfRange: {
          color: string
        }
      }
      span: { //when we have selected dates and reopen the calendar, these styles apply for the days
        color: string
        backgroundColor: string
        selected: {
          active: {
            hover: {
              color: string
              backgroundColor: string
            }
          }
        }
        hover: {
          color: string
          backgroundColor: string
          active: {
            backgroundColor: string
          }
        }
      }
    }
    week: {
      headers: { //styles for the names of the days of the week
        typographyPreset: string
      }
    }
    month: {
      headers: { // style the names of the months and the arrows and buttons that serve as navigation between months
        typographyPreset: string
        color: string
      }
      navigation: {
        default: {
          stylePreset: string
        }
        vertical: {
          buttons: {
            next: string
          }
        }
        icons: {
          stylePreset: string
          svg: {
            left: {
              fill: string
            }
            vertical: {
              width: string
              height: string
              fill: string
            }
          }
        }
      }
    }
    inputs: { // sets the syles for the input fields that are used for the start and end dates
      typographyPreset: string
      colors: {
        color: string
        fill: string
        backgroundColor: string
      }
      spacing: {
        marginLeft: string
        marginRight: string
        padding: string
      }
      sizing: {
        width: string
      }
      borders: {
        stylePreset: string
      }
    }
  }
}
  • startDate: Date | null | mandatory. Passed by a parent component or a hook, in order to be accessible for further use.
  • endDate: Date | null | mandatory. Passed by a parent component or a hook, in order to be accessible for further use.
  • setDates: (start: null | Date, end: null | Date) => void | mandatory. Passed by a parent component or a hook, in order to set the two dates above. This is most likely to be a setState() hook, but any function that manages state and returns void is valid.

ContentListView

This component is a highly flexible wrapper around the Newskit's StructuredList component. The content in each list item looks like a table structure, can have multiple cells, can contain buttons, links. The component is used in my-account package to display the sections in Personal details, Subscription and billing and Newsletters and alerts pages, as well as the Holiday stops page.

The ContentListView component takes the following

  • Props:
interface ContentListViewProps {
  children: React.ReactNode
  introductionProps?: IntroductionProps & {
    extendTitleOverrides: TitleOverrides
    extendDescriptionOverrides: DescriptionOverrides
  }
  link?: boolean
  ariaLabel?: string
  marginBlockEnd?: MQ<string>
  stylePreset?: MQ<string>
  showDividerByBreakpoint?: boolean
}

ContentListView uses some components internally and exports 3 additional components: ListItem, DateItemButtons and DateItemStatus.

filterListItems

This function filters a list of items based on specified criteria. It accepts two arguments:

  • ListItems: Array with list items
  • data: The data which will be used for filtering

List items contains filter object that contains two attributes:

  • dataPath - The path to the field in the data object used for filtering the items.
  • showOnTrue - Set to true if you want the item to be included in the filtered list when the dataPath returns true. Set to false if you want the item to be excluded from the filtered list.

The function will return an item if filter is not provided, or the provided data path does not exist in the data object.

ListItem

This is the regular list item. It is used in my-account main pages and takes the following

  • Props:
interface ListItemComponentProps {
  item: ListItemType
  listCellProps?: ListCellProps
  listItemProps: ListItemProps
}

DateItemButtons

This is used when the list item must contain a date and a button. It is used in the Holiday Stops page, when displaying the list of upcoming holiday stops. It takes the following

  • Props:
interface DateItemComponentProps {
 item: DateItemType
 listItemProps: ListItemProps
 listCellProps?: Partial<ListCellProps> & {
   statusOverrides?: {
     typographyPreset?: MQ<string>
     stylePreset?: MQ<string>
   }
 }
 redirectUrl?: string
 onCancel?: () => void
 children?: React.FunctionComponent
}

DateItemStatus

This is used in the holiday stops list, when displaying the current or past holiday stops - the list item displays a date but instad of an edit button, it only displays the status of the holiday stop. The component takes the type of props as the DateItemButtons.

The difference is made by the item's status, which is part of the DateItemType itself and has the following shape:

interface DateItemType {
 id: string
 startDate: string
 endDate: string
 status: string | null
}

CorePackageContent

This component is used to display the core content of a 'subscription package'. It is meant to display the title of the package, an image, a list of benefits for that package, description, payment details. price, promotions etc. It takes the following

  • Props:
interface CorePackageContentProps {
 titleBar?: TitleBarProps
 image?:
   | (ImageProps & {
       marginBlockEnd?: MQ<string>
     })
   | false
 benifitList?: Omit<UnorderedListProps, 'children'> & {
   marginBlockEnd?: MQ<string>
   icon?: NewsKitIcon
 }
 benifitItems?: string[]
 divider?: {
   marginBlockEnd?: MQ<string>
 }
 promo?: string
 promoOverrides?: Omit<TextBlockProps, 'children'> & {
   marginBlockEnd?: MQ<string>
 }
 paymentKey?: string
 paymentValue?: string
 paymentOverrides?: Omit<TextBlockProps, 'children'> & {
   marginBlockEnd?: MQ<string>
 }
 description?: string
 descriptionOverrides?: Omit<TextBlockProps, 'children'> & {
   marginBlockEnd?: MQ<string>
 }
 billingDetails?: string
 billingDetailsOverrides?: Omit<TextBlockProps, 'children'> & {
   marginBlockEnd?: MQ<string>
 }
 backgroundColor?: string
 padding?: string
}

ExpandSection

This component is used when we need to partially hide a part of some text, that is too long to display at once. It contains a button that allows the contend to be expanded and collaped as needed. It's initial state, the text of the buttons, and the styling of the content can be controlled using the following

  • Props:
interface ExpandSectionProps {
 data: {
   textList: string[]
   moreInfo: {
     text: string
     textList: string[]
   }
 }
 expanded?: boolean
 expandButton?: { expand: string; collapse: string }
 unorderedListOverrides?: {
   content?: {
     stylePreset?: MQ<string>
     typographyPreset?: MQ<string>
   }
   marginBlockEnd?: MQ<string>
 }
 textOverrides?: {
   typographyPreset?: MQ<string>
   stylePreset?: MQ<string>
   marginBlockEnd?: MQ<string>
 }
 buttonOverrides?: {
   stylePreset?: MQ<string>
   paddingInline?: MQ<string>
   paddingBlock?: MQ<string>
   minHeight?: MQ<string>
   minWidth?: MQ<string>
   width?: MQ<string>
 }
}

FormComponent

This component is a wrapper around the Newskit's Form component and is used to display the forms that edit Name, Display Name and email in my-account. It takes the following

  • Props:
interface FormProps extends FormWrapperProps {
 validation?: Record<string, any>
 validationSchemaKey?: string
 fields: EditFieldType[]
 disable?: boolean
 handleError?: (
   type: string,
   errorMessage?: ErrorMessage,
   genericErrorMessage?: GenericErrorMessage,
   message?: string
 ) => void
 handleLoading?: (
   type: string,
   errorMessage?: ErrorMessage,
   genericErrorMessage?: GenericErrorMessage
 ) => void
 handleSuccess?: () => void
 baseUrl?: string
 errorMessages?: ErrorMessage
 genericErrorMessage?: GenericErrorMessage
 onSubmit?: (
   e: {
     [x: string]: any
   },
   validationSchemaKey: string | unknown,
   router?: any,
   handleError?: ((overrideMessage?: string) => void) | undefined,
   handleSuccess?: (() => void) | undefined,
   redirectUrl?: string | undefined
 ) => Promise<Response>
}

The form is based on react-hook-form and uses yup validation.

Header

This component is used to form the upper part of the page, below the navigation header. It contains the title of the page's cpntent, and other elements that might be used as the page's heading.

  • Props:
type HeaderProps = IntroductionProps & {
 backButton?: TBackButton
 backButtonOverrides?: BackButtonOverrides
 image?: EnhancedImageProps | false
 marginBlockEnd?: MQ<string>
 showDivider?: boolean
 fullWidthTitle?: boolean
 imageCell?: GridLayoutItemProps
 introductionCell?: GridLayoutItemProps
 backButtonCell?: GridLayoutItemProps
}

Image

The image component is a wrapper around next/image.

  • Props:
type ImageProps = {
 aspectRatio?: string | number
 href?: string
 width?: number
} & NextImageProps

Introduction

This component is used to display the headline and description of the page or modal. It is used in both Header and Modal components.

  • Props:
interface IntroductionProps {
 title?: string
 titleOverrides?: TitleOverrides
 description?: string | string[]
 descriptionOverrides?: DescriptionOverrides
 overline?: string
 overlineOverrides?: OverlineOverrides
 center?: boolean
 innerHTML?: string
 makeBoldTextRegEx?: string
 makeBoldTextRegExFlags?: string
}

Link

The eventContext interface is not exported

  • Props:
interface EventContext {
  event_navigation_action: string
  event_navigation_name: string
  event_navigation_browsing_method: EventTrigger
  event_navigation_label?: string
}

interface LinkProps
 extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
 href: string
 type: 'standalone' | 'inline'
 eventContext?: EventContext
 external?: boolean
 overrides?: {
   stylePreset?: MQ<string>
   typographyPreset?: MQ<string>
   spaceInline?: MQ<string>
   externalIcon?: {
     size?: string
   }
 }
}

Modal

This component is a wrapper around the Newskit's Modal. The modal content can contain other components from shared-components: Image, Introduction, CorePackageContent and ButtonGroup

  • Props:
interface ModalProps
 extends Omit<MKModalProps, 'children'>,
   ModalContentProps {
 closePosition?: 'left' | 'right' | 'none'
 onOpen?: () => void
}

interface ModalContentProps {
  paddingInline?: MQ<string>
  paddingBlock?: MQ<string>
  image?: ImageProps & {
    marginBlockEnd?: MQ<string>
  }
  introduction?: IntroductionProps
  inlineMessage?: Omit<InlineMessageProps, 'children'> & {
    message?: string
    marginBlockEnd?: MQ<string>
  }
  corePackageContent?: CorePackageContentProps
  buttonGroup?: ButtonGroupProps & {
    paddingInline?: MQ<string>
    paddingBlock?: MQ<string>
  }
  centeredButton?: ButtonProps & {
    paddingInline?: MQ<string>
    paddingBlock?: MQ<string>
  }
  modalLink?: ModalLinkProps
}

interface ModalLinkProps {
  links?: Array<LinkProps & { text: string }> | (LinkProps & { text: string })
  introduction?: IntroductionProps
}

NavigationFooter

  • Props:
interface FooterProps {
 footer: FooterContext
 footerOverrides: FooterContextOverrides
 margin?: {
   xsMargin?: string
   smMargin?: string
   mdMargin?: string
   lgMargin?: string
 }
 gutter?: {
   xsColumnGutter?: string
   smColumnGutter?: string
   mdColumnGutter?: string
   lgColumnGutter?: string
 }
}

interface FooterContext {
  helpChat?: React.ComponentType<{
    chatHelpOverrides?: Record<string, unknown>
  }>

  menuItemArray: {
    text: string
    href: string
    id: string | number
  }[]
  legalText: string
}

interface FooterContextOverrides {
 ariaLabel?: string
 menuItemOverrides?: {
   stylePreset?: MQ<string>
   typographyPreset?: MQ<string>
   paddingInline?: MQ<string>
   paddingBlock?: MQ<string>

 }
 menuOverrides?: {
   marginBlockEnd?: MQ<string>
   backgroundColor?: string
   borderColorTop?: string
   borderColorBottom?: string
   padding?: {
     xs: string
     md: string
   }
 }
 legalTextOverrides?: {
   marginBlockEnd?: MQ<string>
   stylePreset?: MQ<string>
   typographyPreset?: MQ<string>
   padding?: {
     xs: string
     md: string
   }
 }
 chatHelpOverrides?: Record<string, unknown>
}

NavigationPrimary

This component is a wrapper around a <header> tag and contains the primary navigation, the logo, and can also display additional item, depending on the route. For example /account route displays the word Account next to the logo and the navigation lead to either the main page or logout. The / route displays links to index pages and only displays Account link and icon if the visitor is loggged in. The component takes the following

  • Props:
interface NavigationPrimaryProps extends NavigationPrimaryThemeProps {
 children?: React.ReactNode
 nav?: NavigationPrimaryInterface[] | false
 title?: string
 titleHref?: string
 titleOverrides?: {
    stylePreset?: MQ<string>
    typographyPreset?: MQ<string>
 }
 logoSrc?: string
 logoWidth?: string
 logoHeight?: string
 loggedInUser?: boolean
 gridOverrides?: {
   width?: MQ<string>
   minWidth?: MQ<string>
   maxWidth?: MQ<string>
   height?: MQ<string>
   minHeight?: MQ<string>
   maxHeight?: MQ<string>
 } & {
   marginInlineStart?: MQ<string>
   marginInlineEnd?: MQ<string>
   marginInline?: MQ<string>
   marginBlockStart?: MQ<string>
   marginBlockEnd?: MQ<string>
   marginBlock?: MQ<string>
 }
}

and uses the following interfaces:

interface NavigationPrimaryInterface {
  text: string
  link: string
  icon: React.ReactElement<NewsKitIconProps> | null
  ariaLabel?: string
}

interface NavigationPrimaryThemeProps {
  customTheme?: UncompiledTheme
}

type Logo =
  | {
      src?: string
      width?: string
      height?: string
      top?: string
    }
  | undefined

PastDueBanner, PastDueBannerExternal and pastDuebannerDefaultContext

PastDueBanner is the component, used to display the notification message when a user's subscription is no longer active or soon to be deactivated. There are several different states of deactivation, each one represented by a slightly different banner. The differences are passed to the component through a context object. In both PastDueBanner and PastDueBannerExternal the context is passed through the pastDueBanner prop. The components's props are as follows:

  • PastDueBanner props:
type PastDueBannerProps = {
 className?: string
 pastDueBanner: PastDueBannerType
 userData: Partial<UserData>
 url: string
}
  • PastDueBannerExternal props:
type PastDueBannerExternalProps = {
  className?: string
  pastDueBanner?: PastDueBannerType
  user: UserData
  wrapper?: React.ComponentType
  theme?: UncompiledTheme
}

If no context is passed to the components, a default context (called pastDueBannerDefaultContext) is used:

  • the context type is as follows:
interface PastDueBannerType {
 firstNotice: Notice
 secondNotice: Notice
 terminated: Notice
 toBeCancelled: Notice
 toBeCancelledWithRefund: Notice
 cancelled: Notice
 treshold: PastDueBannerTreshold
}

type Notice = {
  title: string
  text: string
  dismissDays?: number
  phoneNumber?: string
  button?: string
  link?: {
    linkLocation: string
    linkText: string
  }
}

In this context object parts of the banner's message are placeholders. The placeholders are dynamically replaced with an actual value. To add a hyperlink to the banner's message, pass a link prop to the banner in the context. The link prop has this shape :

link: {

linkLocation: string, // example: 'https://someurl.com'

linkText: string, // example: 'click here'

},

In the text prop, add a ##LINK## placeholder where the hyperlink should be.

The pastDueBannerDefaultContext contains an example of this in its "terminated" banner:

terminated: {

title: 'Your subscription has been terminated',

phoneNumber: 'XXXX-XXX-XXXX',

text:

'We didn’t receive payment for your subscription. To reactivate it, please call ##PHONE_NUMBER##. Or click ##LINK##.',

dismissDays: 7,

link: {

linkLocation: 'http://localhost:3000/account',

linkText: 'here',

},

},

RadioForm

The RadioForm component combines several Newskit components in a single choice form, including validation. The validation is based on react-hook-form. A textarea field can be added to one or more of the radio buttons in the form, in case some extra information needs to be provided with the choice. The form takes the following:

  • Props:
interface RadioFormProps {
 onSubmit: (response: RadioFormResponse) => void
 introductionProps?: Omit<IntroductionProps, 'title' | 'titleOverrides'>
 radioInputList: RadioFormInput[]
 radioInputName?: string
 size?: 'small' | 'medium' | 'large'
 resolver: Resolver
 radioInputListOverrides?: Overrides
 errorOverrides?: Overrides
 inputTextAreaOverrides?: Omit<Overrides, 'marginBlockEnd'> & {
   marginBlockStart?: MQ<string>
 }
 inputAssistiveTextOverrides?: Overrides
 characterCountOverrides?: Overrides
 marginBlockEnd?: MQ<string>
 buttonGroupProps?: ButtonGroupProps
 introductionInlineMessage?: string
 introductionInlineMessageOverrides?: Omit<InlineMessageProps, 'children'> & {
   marginBlockEnd?: MQ<string>
 }
 gridProps?: {
   xsMargin: string
   xsColumnGutter: string
   xsRowGutter: string
 }
}

SecureFlag

SecureFlag is a simple component, used to mark a payment operation as secure. It takes the following:

  • Props:
const props: SecureFlagProps = {
  size,
  overrides,
  iconOverrides,
  icon,
}

interface SecureFlagProps extends FlagProps {
 iconOverrides?: {
   paddingInline?: MQ<string>
   paddingBlock?: MQ<string>
   spaceInline?: MQ<string>
   size?: MQ<string>
   stylePreset?: MQ<string>
 }
 icon?: ReactElement<NewsKitIcon>
}

The component has a default styling in the theme, consisting of size, overrides and iconOverrides:

const secureFlagDefaultOverrides = {
  stylePreset: 'secureFlagStylePresets',
  typographyPreset: 'utilityLabel010',
  paddingInline: 'space000',
  paddingBlock: 'space000',
  spaceInline: 'space020',
}

const secureFlagIconDefaultOverrides = {
  paddingInline: 'space000',
  paddingBlock: 'space000',
  spaceInline: 'space020',
  size: 'iconSize020',
}

const secureFlagComponentDefaults: SecureFlagProps = {
  size: 'small',
  overrides: secureFlagDefaultOverrides,
  iconOverrides: secureFlagIconDefaultOverrides,
}

These are going to be used if no values are passed from the context.

Seo

The Seo component is a wrapper around the Next.js native Head component, used to build the <head> section of the document. It is used to gurantee a certain minimal level of SEO and takes the following:

  • Props:
const seoProps: SEOProps = {
 title,
 description,
 url,
 siteHost,
 hrefLang = 'en',
 maxImagePreview = 'large',
 gscId,
 fbTitle,
 fbType,
 fbImageUrl,
 twUsername,
 twTitle,
 twDescription,
 twImageUrl,
 twImageAlt,
}

type SEOProps {
  title: string
  description: string
  url: string
  siteHost: string
  hrefLang?: string
  maxImagePreview?: 'none' | 'standard' | 'large'
  gscId?: string
  fbTitle?: string
  fbType: string
  fbImageUrl: string
  twUsername?: string
  twTitle?: string
  twDescription?: string
  twImageUrl?: string
  twImageAlt?: string
}

SkipToContent

A small component, used for accesibility purposes. Allows the page's visitor to use the Tab bitton to skip to the content of the page, and move around it.

SubscriptionDetails

The component is a small wrapper around the Newskit'a banner.It's maent to display the name and price of a user's subscription, as well as a button, which reveals the full details on click. The component is used in the mobile/smaller screens view in the payment-details page of the checkout package.

  • Props:
interface SubscriptionDetailsProps {
 banner?: {
   buttonText: string
   message: string
   title: string
   onClick?: () => void
 }
 bannerOverrides?: {
   stylePreset: MQ<string>
   paddingInline?: MQ<string>
   paddingBlock?: MQ<string>
   maxWidth: string
   minHeight: MQ<string>
   content: {
     title: {
       stylePreset: MQ<string>
     }
     message: {
       typographyPreset: MQ<string>
       stylePreset: MQ<string>
     }
   }
 }
 containerOverrides?: {
   stylePreset: MQ<string>
   marginBlockEnd: MQ<string>
 }
 margin?: {
   xsMargin?: string
   smMargin?: string
   mdMargin?: string
   lgMargin?: string
 }
 gutter?: {
   xsColumnGutter?: string
   smColumnGutter?: string
   mdColumnGutter?: string
   lgColumnGutter?: string
 }
}

SubscriptionDrawer

This component uses the Newskit's Drawer component and the CorePackageContent to display the full details of a user's subscription on a mobile screen. This is the component that is displayed if the button in SubscriptionDetails is clicked. It takes the following

  • Props:
interface SubscriptionDrawerProps
 extends CorePackageContentProps,
   Omit<DrawerProps, 'children'> {
 size?: string
}

TitleBar

This component displays the title of the CorePackageContent and takes the following

  • Props:
interface TitleBarProps
 extends Omit<NKTitleBarProps, 'children' | 'actionItem'> {
 text: string
 marginBlockEnd?: MQ<string>
 link?: {
   text: string
   href: string
   'aria-label'?: string
 }
 linkOverriders?: {
   stylePreset?: MQ<string>
   typographyPreset?: MQ<string>
   paddingInline?: MQ<string>
   paddingBlock?: MQ<string>
   marginBlockEnd?: MQ<string>
   iconSize?: MQ<string>
 }
}