@newskit-render/my-account
v7.89.0
Published
Newskit Render
Downloads
2,893
Readme
@newskit-render/my-account
Contributing Guidelines
Contribution Guidelines can be found in CONTRIBUTING.md
Getting Started
There are two ways to start with @newskit-render/my-account
:
1. If you want to build a project from scratch, that will have everything from Newskit Solutions, you need to run create-render-app
(See Newskit-Solutions Getting started).
2. If you already have a next application and you want to include my-account package you just need to run yarn add @newskit-render/my-account
or npm install @newskit-render/my-account
based on which package manager you use. Next you need to create an account folder inside your pages folder, and you can add your account pages there:
import { PersonalDetails, getProviderProps } from '@newskit-render/my-account'
export default PersonalDetails
export const getServerSideProps = async (context) =>
getProviderProps({ ...context, provider: 'PersonalDetails' })
Authentication
If you have already included my account into your application, you will be able to see account pages, but they won't be populated with any data. That's because my-account needs the following environment variables in order to connect with CPS (formerly MAIN).
MAIN_GRAPHQL_URL="" // This is going to be deprecated and removed soon. Please use ACCOUNT_PROVIDER_GRAPHQL_URL
ACCOUNT_PROVIDER_GRAPHQL_URL=""
ACCOUNT_PROVIDER_INTERNAL_AUTH0_DOMAIN=""
ACCOUNT_PROVIDER_INTERNAL_CLIENT_ID=""
ACCOUNT_PROVIDER_INTERNAL_CONNECTION=""
ACCOUNT_PROVIDER_GRAPHQL_URL: This is the main graphql endpoint - you can ask someone from your team, or a member from CPS to provide it for you. ACCOUNT_PROVIDER_INTERNAL_AUTH0_DOMAIN, ACCOUNT_PROVIDER_INTERNAL_CLIENT_ID, and ACCOUNT_PROVIDER_INTERNAL_CONNECTION are needed for the changing password and email functionality and like ACCOUNT_PROVIDER_GRAPHQL_URL can be provided by CPS.
If you are interested in my-account - CPS integration and you want some in-depth info about it, you can have a look at our space in confluence
Step-up MFA
To enable step-up MFA both @newskit-render/api
and @newskit-render/my-account
should have following environment variables:
ENABLE_STEPUP_MFA="true"
STEPUP_MFA_GO_TO_URI=""
ENABLE_STEPUP_MFA: disables MFA if set to anything other then 'true' or excluded STEPUP_MFA_GO_TO_URI: used to decide where to redirect user on succesfull MFA calls.
Tracking user interactions - Tealium config
We rely on Tealium in combination with Newskit to track user interaction. In the my-account package we track user interactions ( such as clicking ) in two ways, depending on what component we want to track:
When using Newskit components that are by nature interactive ( buttons, switches, links, etc. ) we pass an eventContext
object, with the following shape:
eventContext: {
key[string]: string
}
which contains the Tealium key-value pairs needed for analytics (e.g. event_navigation_name: "sidebar option selection"
)
And an
eventOriginator: string
used to pass information on which interactive element was used to trigger the event ( e.g. eventOriginator: "primary button"
).
Wrapper components, exported form shared-components, such as the ButtonGroup, use the eventOriginator
and eventContext
internally.
For example, when using the ButtonGroup component, exported from shared-components, we can pass the Teallium configuration for the buttons as such:
props: {
buttonGroupProps: {
primaryButton: {
text: 'Save',
eventContext: {
event_navigation_action: 'navigation',
event_navigation_name: 'button:save',
event_navigation_browsing_method: EventTrigger.Click,
},
ariaLabel: 'Save and go back to ...',
},
secondaryButton: {
eventContext: {
event_navigation_action: 'navigation',
event_navigation_name: 'button:cancel',
event_navigation_browsing_method: EventTrigger.Click,
},
ariaLabel: 'Cancel and go back to ...',
href: '[baseURL]/my-url',
},
},
...moreContext,
},
The ButtonGroup will add the event originator automatically.
For the components that are NOT naturally interactive ( like the StructuredListItem ), but we have added interactivity to them, the Tealium tracking can be added with the function createClickEvent
, which is exported from shared-components package.
It takes the following params:
createClickEvent(
originator: string,
text: string,
label?: string,
articleParentName?: string,
pageName?: string,
event_navigation_action = 'navigation'
)
And returns a Tealium configuration object for a click event.
When using this function, we rely on the PUBLISHER
environment variable to keep the tracking title agnostic. In the future, we will move the Teallium configuration for these cases to the contexts.
Page loading events
In many cases we want to track visitor’s landing on a page. To do this, we use the usePageViewTracking hook, exported from shared-components
. The hook accepts an object as an argument:
interface TealiumEventContext {
originator: string
context: Record<string, string>
}
And is triggered only if such an object is passed to it via the page’s context.
The context’s key for page view tracking is tealiumContext
. For example:
const personalDetailsContext = {
…otherContextValues,
tealiumContext: {
originator: 'page view',
context: {
page_site_name: ‘you ‘site name,
page_section: 'my account',
page_type: ‘personal ‘details,
page_restrictions: 'restricted',
page_name: 'personal details',
‘some_other_key’: ‘some other value’
},
},
}
For contributors: the rules for keeping the package title agnostic are the same.
Theming
The my-account package appearance can be controlled by a custom theme. The package uses NewsKit's theming system to enable customisation. See Newskit theming
To override the My Account base theme you need to pass a new theme as a prop of a page component:
import React from 'react'
import { PersonalDetails, getProviderProps } from '@newskit-render/my-account'
import { sharedTheme } from '@newskit-render/shared-components'
import { createTheme } from 'newskit'
const customTheme = createTheme({
name: 'new-theme',
baseTheme: sharedTheme,
overrides: {
stylePresets: {
ADD YOUR CUSTOM PRESETS
}
}
})
const AccountPersonalDetails = (props) => (
<PersonalDetails {...props} customTheme={customTheme} />
)
export default AccountPersonalDetails
export const getServerSideProps = async (context) =>
getProviderProps({ ...context, provider: 'PersonalDetails' })
Use the sharedTheme
as the base to any new theme you create. You can see this theme here
Currently the app created with create-render-app
has a dropdown theme selector that you can see by pressing '`' key. This is also implemented in Account. When you add a custom theme you should remove the AppContext
and createThemeDropdownObject
logic.
Page types
Currently provides a number of default pages, however they are based on 3 page types
- Display
- Edit
- Add
The current Display pages (pages/account/
) are Personal Details page (PersonalDetails
component), Subscription and Billing page (SubscriptionAndBilling
component), Newsletters & Alerts page (NewslettersAndAlerts
component) and Newsletters page (Newsletters
component). They differ from Edit pages by using sections data type to display information (see Page overrides and Properties of ContextOptions for information on sections).
The DynamicPage
component
The DynamicPage
component was introduced to ensure the rendering of all dynamic pages is done in an uniform way. It takes a mapping object, a page name, a react component and its props. DynamicPage
checks if the page exists in the mapping object and eiter renders the passed Component with its props, or the NotFound
page.
It is not madatory to use this new way of rendering the dynamic pages, and no need to change anything in your code if you are using an old version of Render.
All Edit pages are currently accessed from pages/account/edit/[field].tsx
(EditField
component, passed to DynamicPage
). They differ from Display pages by using forms data type to display forms you can edit user data with (see Page overrides and Properties of ContextOptions for information on forms).
All Add pages are currenlty accessed from pages/account/add/[field].tsx
(AddField
component, passed to DynamicPage
)`
Page overrides
The My Account package has data driven pages using React context. You can override defaults starting on a page basis.
The following examples will override aspects of the Personal Details page:
1. Override the page's header description and list items title styles:
import React from 'react'
import { PersonalDetails, getProviderProps, ContextOptions, personalDetailsContext } from '@newskit-render/my-account'
const overrideContext: ContextOptions = {
...personalDetailsContext,
header: {
...personalDetailsContext.header,
description:
'This will override the default header details',
},
sectionsOverrides: {
list: {
titleOverrides: {
marginBlockEnd: {
xs: 'space040',
md: 'space050',
},
typographyPreset: 'utilityHeading020',
as: 'h3'
},
},
},
}
const Page = (props) => <PersonalDetails context={overrideContext} {...props} />
export default Page
export const getServerSideProps = async (context) =>
getProviderProps({ ...context, provider: 'PersonalDetails' })
2. Override the sections on the page
const overrideContext: ContextOptions = {
...personalDetailsContext,
sections: [
{
type: 'digital',
componentType: 'list',
props: {
introductionProps: {
title: 'Section added'
},
items: [
{
label: 'Backup Email',
type: 'email',
default: '[You can add a backup email]',
href: '[baseURL]/edit/backup-email',
ariaLabel: 'Add bacup email',
}
]
}
}
]
}
3. Add onto the existing sections:
const overrideContext: ContextOptions = {
...personalDetailsContext,
sections: [
...personalDetailsContext.sections,
{
type: 'print',
componentType: 'list',
props: {
introductionProps: {
title: 'Another section added'
},
items: [
{
label: 'Landline phone number',
default: [
'Please enter your landline number',
'Invalid landline number',
],
href: '[baseURL]/edit/landline',
type: 'landline',
ariaLabel: 'Edit your number',
},
]
}
}
]
}
The following examples will override aspects of Edit pages pages/account/edit/[field].tsx
.
- Let's change 3 forms - Password, Name and Address:
import React from 'react'
import { EditField, getProviderProps, ContextOptions, editFieldContext } from '@newskit-render/my-account'
import { IconFilledInfo, IconFilledSearch } from 'newskit'
import validation from '../../../validation'
const overrideContext: ContextOptions = {
...editFieldContext,
forms: {
...editFieldContext.forms,
password: {
header: {
title: 'Change your Password',
image: {
src: '/assets/personal-details-header.svg',
alt: '',
},
showDivider: true,
},
props: {
text: [
'For extra security we will send you a <b>change password link</b> to the email saved in your account.',
'The link can only be used once and expires in 20 minutes.',
],
textOverriders: {
typographyPreset: {
xs: 'utilityBody020',
sm: 'utilityBody030',
},
stylePreset: 'inkBase',
marginBlockEnd: {
xs: 'space060',
sm: 'space070',
},
},
buttonGroupProps: {
primaryText: 'Get Link',
},
},
},
name: {
props: {
inputText: {
cells: {
xs: 12,
md: 6,
},
},
infoPanel: {
icon: <IconFilledInfo overrides={{ size: 'iconSize030' }} />,
children:
'We will only use your name to comunicate with you directly. When making comments on our websites and apps, your ‘display name’ is used to protect your identity.',
infoPanelCells: {
xs: 12,
md: 8
},
overrides: {
marginBlockEnd: 'space060',
},
},
},
},
address: {
header: {
description: 'You can edit your address here.',
},
props: {
inputText: {
marginBlockEnd: 'space040',
},
fullAddressInput: {
label: 'Search for Address',
assistiveText: 'Type any part of address or postcode to search',
cells: {
xs: 12,
},
marginBlockEnd: {
xs: 'space080',
md: 'space090',
},
icon: (
<IconFilledSearch
overrides={{
size: 'iconSize030',
}}
/>
),
},
line1Input: {
label: 'Address field 1',
cells: {
xs: 6,
},
},
line2Input: {
label: 'Address field 2',
cells: {
xs: 6,
},
},
line3Input: {
label: 'Town/City',
cells: {
xs: 6,
},
},
line4Input: {
label: 'County',
cells: {
xs: 6,
},
},
line5Input: {
label: 'Postcode',
cells: {
xs: 6,
},
},
countryInput: {
labelProps: {
text: 'Country',
overrides: {
typographyPreset: 'utilityLabel020',
marginBlockEnd: 'space030',
},
},
cells: {
xs: 6,
},
panelProps: {
maxHeight: '130px',
zIndex: 'no-layer',
},
optionsProps: {
defaultValue: 'GBR',
},
},
},
}
}
}
// If you are using an older version, you can keep this code
const AccountEditField = (props) => (
<EditField {...props} validation={validation} context={overrideContext} />
)
export default AccountEditField
// If you are using the new DynamicPage component, the code will look like this:
const AccountEditField = (props) => (
<DynamicPage
objectMap={editComponentMap}
pageName={props.data.type}
Component={EditField}
componentProps={props}
validation={validation}
context={overrideContext}
/>
)
export const getServerSideProps = async (context) =>
getProviderProps({ ...context, provider: 'EditField' })
Section types
The section displays information based on the type of subscription and the type of component, used for display:
type: 'print' | 'digital' // the type of subscription
componentType: 'list' // the component, used for displaying the data. Currently we only support `list`
Each item in this list
also has a type. Currently the item types are:
Item types
- 'name'
- 'dob'
- 'displayName'
- 'email'
- 'password'
- 'mobile'
- 'landline'
- 'address'
- 'subscription'
- 'price'
- 'customerNumber'
- 'subDate'
- 'benefits'
- 'payment'
- 'billDate'
- 'amountDue'
- 'newsletters'
- 'commentingNotifications'
- 'contactPreferences'
For more details, see below
Properties of ContextOptions
1. data
- Do not touch - this will be overriden in my-account.
2. userData
- Do not touch - this will be overriden in my-account.
3. head
- currently lets you override the title tag of the page:
head: {
pageTitle: string,
siteTitle: string | false
}
4. navigationPrimary
- Global property that changes the nav links in the top-banner area - an Array of nav objects or false to have no nav:
navigationPrimary: {
nav?: {
text: string
link: string
icon: React.ReactElement<NewsKitIconProps>
ariaLabel?: string
},
title?: string,
logoSrc?: string, // path to logo.svg
logoWidth?: string, // width of logo
titlePosition?: string, // uses css top property to ajust title position
}
5. sideNav
- Global property that changes sidebar navigation - an Array of nav objects:
sideNav: [
{
text: string,
href: string,
id: string,
}
]
6. sideNavSelected : string
- The Id of the selected sideNav
7. sideNavOverrides
- The styling ovderrides, applied to the sideNav:
sideNavOverrides: {
menuItemOverrides: {
stylePreset: MQ<string>,
typographyPreset: MQ<string>,
paddingInline?: MQ<string>
paddingBlock?: MQ<string>
},
backgroundColor: string,
}
8. pastDueBanner
- Banner displayed when your subscription is passed due. If not provided, the following default will be used:
pastDueBanner: {
firstNotice: {
title: "We haven't been able to take payment",
text:
'You may need to update your payment details to keep your subscription.',
button: 'Update payment details',
},
secondNotice: {
title: 'Act now to keep your subscription',
text:
'We’ve tried several times, but haven’t been able to take payment. Please update your payment details to keep your subscription.',
button: 'Update payment details',
},
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',
},
},
cancelled: {
title: 'Your subscription has been cancelled.',
phoneNumber: 'XXXX-XXX-XXXX',
text:
'You’ll no longer have access to subscription benefits. To re-activate call ##PHONE_NUMBER##.',
dismissDays: 7,
},
toBeCancelled: {
title: 'Your subscription will end soon.',
phoneNumber: 'XXXX-XXX-XXXX',
text:
'You have cancelled your subscription and will lose access to all benefits on ##DATE##. To re-activate your subscription call ##PHONE_NUMBER##.',
},
toBeCancelledWithRefund: {
title: 'Your subscription will end soon.',
phoneNumber: '0800 xxxx xxxxx',
text:
'We have successfully cancelled your subscription and will be processing your refund shortly. If you have any question please call ##PHONE_NUMBER##.',
dismissDays: 7,
},
treshold: {
firstNotice: 26,
secondNotice: 30,
},
}
Banner messages indicate different states of the subscription:
- firstNotice: - Banner when the subscription past due is below first threshold
- secondNotice: - Banner when the subscription past due is above than first threshold, but below that second threshold
- terminated: Banner when the subscription past due is above the second threshold
- toBeCancelled: Banner shown when user has cancelled their subscription but before end of billing cycle
- cancelled: Banner shown when user has cancelled their subscription and is after end of billing cycle Banner thresholds are are numbers, representing a number of days since the last payment. They are used to calculate when to show the first and the second banner message.
A link to all the banner's defaults and typ[es can be found here
The pastDueBanner uses some custom stylePreset
PastDueFirstNotice PastDueLastNotice UpdatePaymentButton
see here
9. header - Changes Page header
header: {
title: string,
titleOverrides: TitleOverrides,
fullWidthTitle: boolean,
description: string,
descriptionOverrides: DescriptionOverrides,
backButton: {
href: string
'aria-label': string
text: string
}
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, // (will render as LinkStandalone not Button)
},
image: ImageProps,
marginBlockEnd: MQ<string>,
showDivider: boolean,
imageCell?: GridLayoutItemProps, //GridLayoutItemProps see NewsKit (can be used to change order of items)
introductionCell?: GridLayoutItemProps,
backButtonCell?: GridLayoutItemProps,
}
10. sections
- an Array of section items:
sections: {
type: SectionType,
componentType: string, // currently only 'list'
props: ContentContainerProps
}
For all the sub-types, see the explanation here
The ContentListItem uses some custom stylePresets
baseInteractivePrimary030 contentListView
see here
11. sectionsOverrides
- currently only for lists:
sectionsOverrides?: {
list: {
titleOverrides?: TitleOverrides
descriptionOverrides?: DescriptionOverrides
}
}
For all the sub-types, see the explanation here
12. forms
- we use forms in the edit pages. Contexts differ between forms. Currently we support the following options for forms and their corresponding contexts:
form: {
default?: FormContext,
password?: PasswordContext,
name?: FormContext,
'display-name'?: FormContext,
email?: FormContext,
landline?: FormContext,
mobile?: FormContext,
payment?: FormContext,
address?: AddressFormContext,
'commenting-notifications'?: CommentingNotificationsContext,
'delivery-instructions'?: DeliveryInstructionsContext,
'holiday-stops'?: HolidayStopContext,
}
For all the context types, see below
13. cancellation
- subscription cancellation:
cancellation: {
reason: Partial<CancellationContext>
confirm: Partial<CancellationContext>
}
For all the context types, see below
14. footer
- Global property
footer:FooterContext = {
menuItemArray: [
{
text: string,
href: string,
id: string | number,
}
],
legalText: string
}
For more about the FooterContext type, see below
15. footerOverides: - Global property.
Used to override the styling of the footer menu items, chat help (if any) and the legal text.
footerOverrides: FooterContextOverrides
For more details, see below
16. baseUrl: string
- change if you are not going to stick to the default routing set up in the core
package. Currently set as /account
but will need to be changes if you change pages/account
file structure in core
.
17. buttonGroupProps
- (override all here or individually by form)
buttonGroupProps {
loading?: boolean
secureFlag?: boolean
secureFlagOverrides?: SecureFlagProps
primaryButton?: ButtonProps
secondaryButton?: ButtonProps
stylePreset?: MQ<string>
keepFixed?: boolean
breakPoint?: BreakpointKeys
}
For more details about the ButtonGroupProps type, see below
The ButtonGroup uses custom stylePresets
buttonGroupXs buttonGroupSm
see here
18. zuoraCustomErrorMessages?:
see Payment section
19. noSubscription?:
SubscriptionStatusProps (see below)
20. previousSubscription?:
SubscriptionStatusProps
interface SubscriptionStatusProps {
header?: HeaderProps
buttonGroupProps?: Partial<ButtonGroupProps>
}
For more on the SubscriptionStatusProps, see below
21. outsideDeliveryAddressModal
Modal to be shown when subscription type is either Print or Digi-Print and postcode is outside the delivery area. Currently it looks like this:
outsideDeliveryAddressModal?: {
closePosition:string
open:boolean
overrides:
content?:
paddingInline?: MQ<string>
paddingBlock?: MQ<string>
panel:
width:MQ<string>
height:MQ<string>
introductionProps: IntroductionProps
title: string
titleOverrides: TitleOverrides
description: string
descriptionOverrides: DescriptionOverrides
modalLink :
links : Array<{
href : string
type : string
text : string
}>
introductionProps: IntroductionProps
title: string
titleOverrides: TitleOverrides
description: string
descriptionOverrides: DescriptionOverrides
buttonGroupProps: ButtonGroupProps
redirectUrl : string
}
It is a part of EditFieldContextOptions, which you can see below
Change redirectUrl
if you are not sticking to the default routing set up in the Core package. Currently set as /account/subscription-and-billing
but will need to be changes if you change pages/account/subscription-and-billing
file structure in the Core package.
22. infoBanner
- Banner for informative messages.
This banner is visible only if the following data is provided in the Context Options:
infoBanner {
bannerName: string
preset?: string
title?: string
text?: string
disableClose?: boolean
}
When the close button is clicked, a new object with the key as the banner name and the value as "dismissed" will be added to the local storage. The banner will remain invisible as long as this local storage value is present.
Address Form - Loqate
The Address form uses Loqate to add address lookup functionality. For it to work you need to add LOQACCOUNT_KEY to your CircleCi context.
To get the LOQACCOUNT_KEY please speak to the Solutions team.
Update Preference centre link
You can update the link to the Preference centre using the context. Example:
const overrideContext: ContextOptions = NewslettersAndAlertsContext;
(overrideContext.sections[0].props as ContentListViewProps).items[2].href = 'https://mypreferences.the-tls.co.uk/login'
const Page = (props) => <NewslettersAndAlerts context={overrideContext} {...props} />
export default Page
Reset password expiration
In the Password reset page, you can customize the lifetime of the password reset link. The time will be shown in the inline message to the users and will also indicate how long until the inline message is no longer shown.
To set a time, add the following row to your .env.local. Value is in seconds.
PASSWORD_URL_LIFETIME=900
If this is not set, the default value will be 900 seconds (15 minutes).
Past Due Banner
The Past Due Banner has been moved into @newskit-render/shared-components
The Past Due Banner is an exported Component, which can be rendered anywhere. Just import the Component and use it in a React application:
import { PastDueBannerExternal } from '@newskit-render/shared-components'
<PastDueBannerExternal
pastDueBanner={pastDueBanner}
user={user}
wrapper={BannerContainer}
/>
Props: {
className?: string
pastDueBanner?: PastDueBannerType
user: UserData
wrapper?: React.ComponentType
theme?: UncompiledTheme
}
The pastDueBanner prop must be in the following format:
interface PastDueBannerType {
firstNotice: Notice
secondNotice: Notice
terminated: Notice
toBeCancelled: Notice
toBeCancelledWithRefund: Notice
cancelled: Notice
treshold: PastDueBannerTreshold
}
For Notice
and PastDueBannerTreshold
, see below
If past due banner context is not provied, the component will use it's default context!
For the user prop we are expecting information from MAIN API user: Example:
{
User {
paymentFailure {
active
startDate
}
subscriptions {
serviceCancellationDate
}
}
}
please see MAIN's voyager
Payment
There are two payment providers - Stripe and Zuora. When you load Provider component, it will pick the provider based on the payment url. E.g. /payment/credit-card
. The currently supported payment methods are creit-card
and direct-debit
, both provided by Zuora.
If you are already using my-account package and you have used the /payment
as your only payment route, you don't need to make any changes to your code. This is still supported, and will work as before.
Example /payment/index.tsx
:
It requires an environment variable called ZUORA_PAGE_ID
import {
Payment,
getProviderProps,
PaymentProvider,
} from '@newskit-render/my-account'
export default Payment
export const getServerSideProps = async (context) => {
return getProviderProps({
...context,
provider: 'Payment',
paymentProvider: PaymentProvider.Zuora, // this is no longer needed, but it's safe to let it stay there.
})
}
If you have more than one payment method, e.g. both Credit card and Direct debit, you can use them this way:
Example /payment/[paymentMethod].tsx
with dynamic routing to the different payment methods:
Each method needs its own environment variable that contains the Zuora page id. The env varibales names are:
- ZUORA_DIRECT_DEBIT_PAGE_ID
- ZUORA_CREDIT_CARD_PAGE_ID
import {
getProviderProps,
Payment,
paymentMethodsMap,
DynamicPage,
} from '@newskit-render/my-account'
const PaymentPage = (props) => {
return (
<DynamicPage
objectMap={paymentMethodsMap}
pageName={props.paymentType}
Component={Payment}
componentProps={props}
/>
)
}
export default PaymentPage
export const getServerSideProps = async (context) => {
return getProviderProps(
{
...context,
provider: 'Payment',
},
{ featureFlags } /* cra-effected */
)
}
The payment provider will be chosen based on the payment method in the url. Make sure, that you use credit-card
or direct-debit
.
The ZUORA_PAGE_ID is also supported and can be used for a third Zuora payment method.
- Stripe - requires a stripe key. The key can be passed by the env variable
STRIPE_KEY
, which should be loaded in env.local. If the variable is not passed, Stripe will be loaded with test account. Stripe key can be retrieved from here. More info on Stripe integration can be found here - Zuora - Data for Zoura forms will come from CPS ( formerly MAIN ) and you should speak to the CPS team about setting up a form. You will need two more environment variable, in addition to the ones containing the page ids, for Zuora to work:
TITLE - CPS can give you this but it will need to be passed into your kubernetes using the Helm value-{env}.yml files:
TITLE: 'thesunuk'
ZUORA_RSA_SIGNATURE_URI - Please check (https://knowledgecenter.zuora.com/Billing/Billing_and_Payments/LA_Hosted_Payment_Pages/B_Payment_Pages_2.0/F_Generate_the_Digital_Signature_for_Payment_Pages_2.0)
Zoura's validation error messages can be overriden in the context:
zuoraCustomErrorMessages?: {
creditCardHolderName: string
creditCardNumber:
one: string
two: string
three: string
cardSecurityCode:
one: string
four: string
creditCardExpiration: string
creditCardPostalCode: string
creditCardCountry: string
unknownError: string
}
These correspond to Zoura's retured error code: 001: Required field not completed - detault for single property or one 002: Invalid card number - two 003: Invalid card type - three 004: Invalid CVV number - four
unknownError: when the error received from Zuora does not have any code or other identifier. Give this field a meaningful value, as it will be shown to the user in some cases. Back to
Book a Holiday Stop
import { HolidayStop } from '@newskit-render/my-account'
import newrelic from 'newrelic'
import React, { useContext } from 'react'
import { AppContext } from '../../../context/app-context'
import { createThemeDropdownObject } from '../../../helpers/createThemeDropdownObject'
const AccountHolidayStop = (props) => {
const { theme, setTheme } = useContext(AppContext)
const themeDropdownObject = createThemeDropdownObject(setTheme)
return (
<HolidayStop
{...props}
customTheme={theme}
themeDropdownObject={themeDropdownObject}
/>
)
}
export default AccountHolidayStop
The Book a Holiday Stop page has the following context which can be overridden. On the page different text is displayed for print and digital customers, this is under print
and digital
keys.
export const holidayStopContext: HolidayStopContextOptions = {
head: {
pageTitle: 'Book a holiday stop',
},
header: {
title: 'Book a holiday stop',
titleOverrides: {
marginBlockEnd: {
xs: 'space050',
sm: 'space060',
},
},
backButton: {
text: 'Back',
href: SUBSCRIPTION_BASE_URL,
'aria-label': 'back',
},
showDivider: true,
backButtonOverrides: {
marginBlockEnd: { xs: 'space050', sm: 'space060', md: 'space070' },
stylePreset: 'buttonOutlinedSecondary',
typographyPreset: 'utilityLabel010',
iconSize: 'iconSize010',
},
marginBlockEnd: {
xs: 'space050',
sm: 'space060',
md: 'space080',
},
},
subscriptionTypes: {
print: {
textList: [
`You can pause your print deliveries at any time by adding a Holiday Stop to your account. You can benefit from pausing deliveries and receiving a refund over a total 5 weeks worth of deliveries within a 12 month period.`,
`The latest you can submit a holiday request for the next day is 16:00 GMT.`,
],
moreInfo: {
text: `You may wish to still pause your deliveries even if you've exceeded your credited allowance of Holiday Stops. Doing so will pause deliveries on these days, however your account will not be credited.`,
textList: [
`Any days where delivery is paused will be credited back to your account and taken off your next bill (allowance and dates depending).`,
`The credit due to you will be applied to an invoice no more than 8 weeks after the holiday start date.`,
],
},
},
digital: {
textList: [
'You can add a holiday stop to your account at any time. You can benefit from pausing your vouchers and receiving a credit over a total of 5 weeks worth of Holiday Stops within a 12 month period.',
'The latest you can submit a holiday request for the next day is 16:00 GMT.',
],
moreInfo: {
text:
'Any arrangements you have for direct delivery by a local retailer will need to be cancelled by you.',
textList: [
'Any days where vouchers are paused will be credited back to your account and taken off your next bill.',
'The credit due to you will be applied to an invoice no more than 8 weeks after the holiday start date.',
],
},
},
'digi-print': {
textList: [
'You can add a holiday stop to your account at any time. You can benefit from pausing your vouchers and receiving a credit over a total of 5 weeks worth of Holiday Stops within a 12 month period.',
'The latest you can submit a holiday request for the next day is 16:00 GMT.',
],
moreInfo: {
text:
'Any arrangements you have for direct delivery by a local retailer will need to be cancelled by you.',
textList: [
'Any days where vouchers are paused will be credited back to your account and taken off your next bill.',
'The credit due to you will be applied to an invoice no more than 8 weeks after the holiday start date.',
],
},
},
},
textOverrides: {
typographyPreset: {
xs: 'utilityBody020',
sm: 'utilityBody030',
},
stylePreset: 'inkBase',
marginBlockEnd: {
xs: 'space040',
md: 'space050',
},
},
unorderedListOverrides: {
content: {
stylePreset: 'inkBase',
typographyPreset: {
xs: 'utilityBody020',
sm: 'utilityBody030',
},
},
marginBlockEnd: {
xs: 'space040',
md: 'space050',
},
},
button: {
label: 'Add a Holiday Stop',
marginBlockStart: { xs: 'space070', md: 'space080' },
marginBlockEnd: { xs: 'space070', md: 'space080' },
overrides: {
stylePreset: 'buttonSolidPrimary',
width: { xs: '100%', sm: 'inherit' },
},
},
}
Newsletters
import { Newsletters } from '@newskit-render/my-account'
import newrelic from 'newrelic'
import React, { useContext } from 'react'
import { AppContext } from '../../../context/app-context'
import { createThemeDropdownObject } from '../../../helpers/createThemeDropdownObject'
const UserNewslettersPage = (props) => {
const { theme, setTheme } = useContext(AppContext)
const themeDropdownObject = createThemeDropdownObject(setTheme)
return (
<Newsletters
{...props}
customTheme={theme}
themeDropdownObject={themeDropdownObject}
/>
)
}
export default UserNewslettersPage
The newsletters page is designed to display user newsletters in a grid view. Each card in the grid contains an image, newsletter title, description, frequency, and a subscribe/unsubscribe switch button.
If a user is subscribed, they will see all the available newsletters, including featured newsletters, if any. But if a user is registered, they will only see those newsletters whose includesRA
property is set to true
. The includesRA
property is used to determine if a newsletter is available for registered users. It is set in the CMP-Microservices-Config repository in /newsletterservice/newsletter.properties.
This page has the following context which can be overridden:
const newslettersContext: NewslettersContextOptions = {
head: {
pageTitle: title,
},
header: {
title,
titleOverrides: {
marginBlockEnd: {
xs: 'space050',
sm: 'space060',
},
},
description:
'Exclusive content and a curated selection of our top stories straight to your inbox.',
descriptionOverrides: {
marginBlockEnd: {
xs: 'space000',
sm: 'space000',
md: 'space000',
},
},
backButton: {
text: 'Back',
href: NEWSLETTERS_BASE_URL,
'aria-label': 'back',
},
backButtonOverrides: {
marginBlockEnd: {
xs: 'space060',
sm: 'space060',
md: 'space060',
},
stylePreset: 'buttonOutlinedSecondary',
typographyPreset: 'utilityLabel010',
iconSize: 'iconSize010',
},
showDivider: false,
marginBlockEnd: {
xs: 'space060',
sm: 'space060',
md: 'space080',
lg: 'space060',
xl: 'space060',
},
},
sideNavSelected: 'newsletters-and-alerts',
footer,
cardOverrides: {
stylePreset: 'newslettersCard',
maxWidth: {
xs: 'none',
sm: 'none',
md: 'none',
lg: 'none',
xl: '250px',
},
maxHeight: {
xs: 'none',
sm: 'none',
md: 'none',
lg: 'none',
xl: '250px',
},
minWidth: {
xs: 'none',
sm: 'none',
md: 'none',
lg: 'none',
xl: '250px',
},
minHeight: {
xs: 'none',
sm: 'none',
md: 'none',
lg: 'none',
xl: '250px',
},
paddingInline: {
xs: '14px',
md: '14px',
},
paddingBlock: {
xs: '14px',
md: '14px',
},
},
cardContentOverrides: {
rowGap: 'space050',
rows: {
xs: 'auto',
sm: '1fr 0.5fr 0.3fr',
md: '1fr 0.5fr 0.3fr',
lg: '1fr 0.5fr 0.3fr',
xl: '1fr 0.7fr 0.3fr',
},
},
frequencyOverrides: {
stylePreset: 'inkSubtle',
typographyPreset: 'utilitySubheading010',
},
headlineOverrides: {
content: {
typographyPreset: {
xs: 'editorialHeadline020',
sm: 'editorialHeadline020',
md: 'editorialHeadline030',
lg: 'editorialHeadline030',
xl: 'editorialHeadline030',
},
heading: {
stylePreset: 'inkBrand010',
},
paddingInlineEnd: 'space020',
},
},
textOverrides: {
stylePreset: 'inkBrand010',
typographyPreset: {
xs: 'utilityBody010',
sm: 'utilityBody010',
md: 'utilityBody020',
lg: 'utilityBody020',
xl: 'utilityBody020',
},
},
gridLayoutOverrides: {
columns: {
xs: '1fr 1fr',
sm: '1fr 1fr',
md: '1fr 1fr',
lg: '1fr 1fr',
xl: '1fr 1fr 1fr 1fr',
},
columnGap: '15px',
autoFlow: 'row',
rowGap: '15px',
},
switchButtonProps: {
label: 'Subscribe',
size: 'small',
overrides: {
onIcon: DoneIcon,
offIcon: CloseIcon,
input: {
stylePreset: 'newslettersSwitchButton',
},
},
},
newsletterImages: {
default: '/placeholder.png',
},
genericErrorMessage: {
pending: 'Updating your ${field} newsletter subscription preferences',
error:
"Sorry, we're unable to save your ${field} newsletter subscription preferences right now. Please try again or come back later.",
success: 'Your ${field} newsletter subscription has been updated.',
},
layoutMainContentOverrides: {
fullWidth: true,
width: {
xs: '100%',
md: '100%',
},
paddingInline: {
xs: 'space045',
sm: 'space060',
md: 'space090',
lg: 'space080',
xl: 'space080',
},
paddingBlockStart: {
xs: 'space060',
sm: 'space080',
md: 'space080',
lg: 'space090',
xl: 'space080',
},
},
}
Redirect unauthenticated users to custom URL
If you require a page to be inaccessible for unauthenticated users, you can utilize the following environment variable - UNAUTHENTICATED_USERS_REDIRECT_URL:https://redirect-url.com
.
If the GET_USER_PAYMENT_INFO
query return the following error as a response - Context creation failed: You must provide acs cookie or bearer token with the valid user cpn
the getProviderProps
function is going to redirect the user to the specified URL.
Redirect users to custom URL if the payment type is not "CreditCard" or "BankTransfer"
If you require a page to be inaccessible for users that have payment type different then CreditCard
or BankTransfer
, you can utilize an additional property within the getProviderProps
options: notEligibleForPaymentPagesRedirectUrl
.
The getServerSideProps
function will redirect directly to the specified URL if the payment type is not CreditCard
or BankTransfer
.
import { getProviderProps } from '@newskit-render/my-account'
export const getServerSideProps = async (context) => {
return getProviderProps(
{ ...context, provider: 'Payment', // User payment type redirects are working with all providers },
{ notEligibleForPaymentPagesRedirectUrl: 'https://redirect-destination.url' }
)
}
ContentPass configuration
If your organization uses ContentPass to manage the cookies, placed in the browser, you need to add a link to ContetnPass dashboard to the footer.
ContentPass places a special cookie - the _cpauthhint
cookie if the user has a ContentPass subscription. Regardless of the value of the cookie, we use its presence to display a special link in the footer, which leads to the ContetnPass dashboard. The link's configuration is the same as a regular link in the footer, but the id should be content_pass
. For example:
footer: {
legalText: 'Your copyright legal text',
linksArray: [
{
text: 'Some regular link',
href: '#',
id: 1,
},
{
text: 'Generic Ad pass',
href: 'https://link-to-content-pass',
id: 'content_pass',
},
],
},
if you need to display more information, you can use the <ContentPassInfo>
component. It is displayed at the bottom of the <Subscription and billing render>
if there is a ContentPass cookie and a configuration for the component has been added to the Subscription and Billing context.
Example configuration:
...subscriptionAndBillingContext,
contentPass: {
contentPassPanelOverrides: {
stylePreset: 'contentPassInfoPanel',
paddingBlock: 'space060',
paddingInline: 'space040',
},
headlineOverrides: {
text: 'Your headline',
typographyPreset: 'editorialHeadline030',
},
textOverrides: {
text: 'Manage your ContentPass subscription.',
paddingBlock: 'spacing050',
},
linkOverrides: {
text: 'Go to your ContentPass account',
href: 'https://link-to-content-pass',
},
},
Type descriptions
ContentContainerProps go back to sections
Forms' contexts go back to forms
CancellationReasonProps. back to cancellation
ConfirmCancellationProps back to cancellation
EditFieldContextOptions go back to outsideDeliveryAddressModal
The full descriptions of all other types mentioned can be found here as they are exported from there.
Go back to: Override Page's Header Override Page's sections Add on to the existing sections PastDueBanner how to use Payment how to use Properties of Context Options
- navigationPrimary context option
- sideNav context option
- pastDueBanner context option
- header context option
- sections context option
- forms context option
- cancellation context option
- footer context option
- baseUrl context option
- buttonGroupProps context option
- zuoraCustomErrorMessages
- noSubscription/previousSubscription context options
- outsideDeliveryAddressModal context option