This is a Next.js project bootstrapped with create-next-app
.
First, run the development server:
npm run dev
# or
yarn dev
Open http://localhost:3000 with your browser to see the result.
You can start editing the page by modifying pages/index.js
. The page auto-updates as you edit the file.
We have these available environments (with their corresponding ENVIRONMENT variable values):
- Local - local
- Development - dev
- Staging - staging staging
- Pre-Production - preprod prod
- Production - prod prod
These are set within .env
locally or via charts in kubernetes.
Please note that preprod setup should be as close to prod environment as possible and should ALWAYS share the same feature flags.
If any text needs to be added to the application, please add new files (or update existing ones) to locales
directory.
For a page to access any translation, you will need to update i18n.js
.
When adding a page, it must be wrapped in createPage HOC with a prop of publicPage to be passed. This will setup auth and guards on the page.
yarn test:unit
Our Jest unit tests use React Testing Library and aim to provide coverage of our components by testing the functionality rather than implementation of our application.
Where queries and mutations take place, we use a mocked Apollo Provider and feed in mocks of the request and data returned by the API.
Where we need to test an app-level context which performs a query, such as the AuthenticatedUserContext, we can wrap the component being tested in its provider and a MockedProvider
and pass in mocks for the query:
<MockedProvider mocks=[meMocks.getGetMeMock()]>
<AuthenticatedUserProvider>
<ComponentBeingTested />
</AuthenticatedUserProvider>
</MockedProvider>
The AuthenticatedUserProvider
is then able to perform its query against the mocked provider, allowing us to test app-level context behaviour for a specific component or user journey.
Ensure you have a locally generated schema as it is used for API mocking:
yarn apollo:generate
-
Write your query or mutation with
gql
tag -
Run
yarn apollo:generate
to generate types for your queries and mutations (for now, it will pick upschema.graphql
withinabcd-api
directory as the basis for types) -
Import and use those generated types with
useMutation
anduseQuery
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
We have a simple Storybook config to allow isolated component development. This is only configured for local development currently.
Stories can be added alongside component src code using *.story.tsx
.
To run locally:
yarn storybook
All API requests are being proxied. Proxy is created using express-http-proxy.
Body size limit of 25mb was added. If the app needs to handle any file uploads bigger than that, you will need to update this value. Otherwise, you will receive a 503
error code as the proxy won't be able to handle the request.
If any new public website pages are added, the sitemap should be updated to add those urls.
There are instances of solution cards on the Public Homepage, Public Confirmation page and Public Stories page.
In order to update the data to include recently added solutions, please update the solution-cards
within publicSolutions.json, adding new solutions to the bottom of the array.
The public Homepage and Confirmation page will display the three most recently-added (bottom three) solutions from publicSolutions.json.
The images that are paired with the solution articles are stored in Public. New images added should follow the same naming pattern, pairing the name with the solution's ID, e.g. Solution[28].png.
Private Solutions Pages use dynamic routing (/solutions/[solution]
). To add a new solution, a new member must be added to the Solutions enum.
Private Solution Detail components make use of the DetailContentBlock component, which takes contentItems
as an array of translation content, which it will map over and render using the appropriate component for each item, depending on its type
property. It also takes an optional components
object, which will be passed to a TransText
component, allowing interpolated components within the translation content. See ContentBlockType for a list of supported content types.
You can locate the featureFlags.ts
file within the src/utils directory.
To add a feature flag, please follow the guidance below:
- Declare a variable and the desired environments, for example:
const isDashboardEnabled = (environment: string) =>
isEnvironmentLocal(environment) || isEnvironmentDevelopment(environment);
-
Add a member to the
Flags
enum, for example:IS_DASHBOARD_ENABLED
. -
Add the above to the
FEATURE_FLAG_PREDICATES
variable, for example:[Flags.IS_DASHBOARD_ENABLED]: isDashboardEnabled
. The feature flag will now be available to call and use. -
By importing and calling
getFeatureFlag
, you are now able to declare a variable and use this as a boolean condition to control features based on the environments selected, for example:
const isDashboardEnabled = getFeatureFlag(Flags.IS_DASHBOARD_ENABLED);
- Please add a comment where flags are used which highlight the highest-level Jira ticket number related to the flag. This will ensure it is clear what should be affected by the flag when it is to be removed, for example:
// abcd-1234 Dashboard
Lauch Darkly is a feature flagging software. There are 2 ways to access flags in the application, the first is clientside, inside components. The useFlags()
hook returns all flags for that user.
In Server side rendered pages the launchDarklyServerClient.ts is used to retrieve flags. An example of this is:
const launchDarklyClient = await getClient();
const demoLogo = await launchDarklyClient.variation(
'demo-logo',
{ key: 'global-key' },
false
);
For jest testing launch darkly provide an npm package for react testing here.
The documentation is simple to follow, the main functions to use are mockFlags({yourFlag: value})
and resetLDMocks()
We display modals throughout the app using our Modal component, which uses react-modal. When a modal needs to be triggered across a wider context, there is an app-wide Modal context, which can be called using the useModal hook. For example, the Edit Interests modal can be triggered using the following openModal
call. New modals can be added to the Modal context by updating the ModalProvider and its types.
const { openModal } = useModal();
openModal({
modalType: ModalType.EDIT_INTERESTS,
contentProps: {
translationPrefix: 'common',
},
});
We are tracking the opt-in users using HubSpot and Amplitude in integration with Segment. More on the integration can be found here. The non-production users will be tracked by HubSpot, if they are whitelisted. To whitelist a user add the user's email address to the HUBSPOT_WHITELIST
array in src/constants.ts
.
By default the Sorttable Table component will paginated all data passed to it by the perPage prop, this is defaulted to 15.
To load async data a "size" prop must be passed to the component, this is the total amount of pages there are. Passing this prop will effectively make the table function in a asynchronous way as it is no longer expecting client side pagination.
There are 2 more important props to get async pagination work, they are "onPageChange" and "loading". The onPageChange callback can be used to trigger the loading of more data, and the loading prop indicates what state the table is in.
An example of a asynchronous paginated table can be seen in the Storyboard or by viewing the asyncData component