Skip to content

Commit

Permalink
⚡ [#76] Lazy load appointment routes/components
Browse files Browse the repository at this point in the history
Most forms won't need this code, so we can lazy load it instead.
  • Loading branch information
sergei-maertens committed Jan 28, 2025
1 parent 7b4ce71 commit c9abd72
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ describe('Create appointment session expiration', () => {
});

// select a product
const dropdowns = screen.getAllByRole('combobox');
const dropdowns = await screen.findAllByRole('combobox');

Check failure on line 95 in src/components/appointments/CreateAppointment/CreateAppointment.spec.jsx

View workflow job for this annotation

GitHub Actions / Run Javascript tests

src/components/appointments/CreateAppointment/CreateAppointment.spec.jsx > Create appointment session expiration > resets the session storage/local state

TestingLibraryElementError: Unable to find role="combobox" Ignored nodes: comments, script, style <body> <div> <h2> Unexpected Application Error! </h2> <h3 style="font-style: italic;" > {"name":"Error","message":"Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?","stack":"Error: Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?\n at TransformPluginContext._formatError (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47574:41)\n at TransformPluginContext.error (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47569:16)\n at normalizeUrl (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:45818:23)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:45937:39\n at async Promise.all (index 0)\n at async TransformPluginContext.transform (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:45864:7)\n at async EnvironmentPluginContainer.transform (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47400:18)\n at async loadAndTransform (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:41198:27)\n at async ViteNodeServer._transformRequest (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite-node/dist/server.mjs:538:16)\n at async ViteNodeServer._fetchModule (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite-node/dist/server.mjs:505:17)\n at async Proxy.fetch (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vitest/dist/chunks/resolveConfig.9CnFfuqj.js:6630:22)\n at async EventEmitter.onMessage (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vitest/dist/chunks/index.TH3f4LSA.js:91:20)","__vitest_rollup_error__":{"plugin":"vite:import-analysis","id":"/home/runner/work/open-forms-sdk/open-forms-sdk/src/components/appointments/CreateAppointment/LandingPage.jsx","loc":{"file":"/home/runner/work/open-forms-sdk/open-forms-sdk/src/components/appointments/CreateAppointment/LandingPage.jsx","line":1,"column":18},"frame":"101| }\n102| cov_1imwgj2o89();\n103| import { Navigate, useSearchParams } from \"react-router-dom\";\n | ^\n104| import { APPOINTMENT_STEP_PATHS } from \"./steps\";\n105| import { jsx as _jsx } from \"react/jsx-runtime\";"}} </h3> <p> 💿 Hey developer 👋 </p> <p> You can provide a way better UX than this when your app throws errors by providing your own <code style="padding: 2px 4px; background-color: rgba(200, 200, 200, 0.5);" > ErrorBoundary </code> or <code style="padding: 2px 4px; background-color: rgba(200, 200, 200, 0.5);" > errorElement </code> prop on your route. </p> </div> </body> Ignored nodes: comments, script, style <body> <div> <h2> Unexpected Application Error! </h2> <h3 style="font-style: italic;" > {"name":"Error","message":"Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?","stack":"Error: Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?\n at TransformPluginContext._formatError (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47574:41)\n at TransformPlugi
expect(dropdowns).toHaveLength(1);
await user.click(dropdowns[0]);
await user.keyboard('[ArrowDown]');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {buildForm} from 'api-mocks';
import {mockSubmissionPost, mockSubmissionProcessingStatusGet} from 'api-mocks/submissions';
import {loadCalendarLocale} from 'components/forms/DateField/DatePickerCalendar';
import {FUTURE_FLAGS, PROVIDER_FUTURE_FLAGS} from 'routes';

Check warning on line 9 in src/components/appointments/CreateAppointment/CreateAppointment.stories.jsx

View workflow job for this annotation

GitHub Actions / Create storybook build

'/home/runner/work/open-forms-sdk/open-forms-sdk/src/routes/index.jsx' imported multiple times

Check warning on line 9 in src/components/appointments/CreateAppointment/CreateAppointment.stories.jsx

View workflow job for this annotation

GitHub Actions / Lint code (ESLint)

'/home/runner/work/open-forms-sdk/open-forms-sdk/src/routes/index.jsx' imported multiple times
import {createAppointmentRoutes} from 'routes/appointments';
import routes from 'routes';

Check warning on line 10 in src/components/appointments/CreateAppointment/CreateAppointment.stories.jsx

View workflow job for this annotation

GitHub Actions / Create storybook build

'/home/runner/work/open-forms-sdk/open-forms-sdk/src/routes/index.jsx' imported multiple times

Check warning on line 10 in src/components/appointments/CreateAppointment/CreateAppointment.stories.jsx

View workflow job for this annotation

GitHub Actions / Lint code (ESLint)

'/home/runner/work/open-forms-sdk/open-forms-sdk/src/routes/index.jsx' imported multiple times
import {ConfigDecorator, LayoutDecorator} from 'story-utils/decorators';

import {
Expand Down Expand Up @@ -47,21 +47,8 @@ export default {
};

const Wrapper = ({form}) => {
const routes = [
{
path: '/appointments',
children: [
{
path: '*',
element: <CreateAppointment />,
children: createAppointmentRoutes,
},
],
},
];
const router = createMemoryRouter(routes, {
initialEntries: ['/appointments/'],
initialIndex: 0,
initialEntries: ['/afspraak-maken/'],
future: FUTURE_FLAGS,
});
return (
Expand Down
17 changes: 17 additions & 0 deletions src/components/appointments/CreateAppointment/LandingPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Navigate, useSearchParams} from 'react-router-dom';

Check failure on line 1 in src/components/appointments/CreateAppointment/LandingPage.jsx

View workflow job for this annotation

GitHub Actions / Create 'production' build

Unable to resolve path to module 'react-router-dom'

Check failure on line 1 in src/components/appointments/CreateAppointment/LandingPage.jsx

View workflow job for this annotation

GitHub Actions / Create storybook build

Unable to resolve path to module 'react-router-dom'

Check failure on line 1 in src/components/appointments/CreateAppointment/LandingPage.jsx

View workflow job for this annotation

GitHub Actions / Lint code (ESLint)

Unable to resolve path to module 'react-router-dom'

import {APPOINTMENT_STEP_PATHS} from './steps';

// TODO: replace with loader that redirects at the route level
export const LandingPage = () => {
const [params] = useSearchParams();
return (
<Navigate
replace
to={{
pathname: APPOINTMENT_STEP_PATHS[0],
search: `?${params}`,
}}
/>
);
};
20 changes: 0 additions & 20 deletions src/components/appointments/CreateAppointment/steps.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import {defineMessage} from 'react-intl';
import {Navigate, useSearchParams} from 'react-router';

import {ChooseProductStep, ContactDetailsStep, LocationAndTimeStep} from '../steps';

export const APPOINTMENT_STEPS = [
{
path: 'producten',
element: <ChooseProductStep navigateTo="../kalender" />,
name: defineMessage({
description: "Appointments navbar title for 'products' step",
defaultMessage: 'Product',
}),
},
{
path: 'kalender',
element: <LocationAndTimeStep navigateTo="../contactgegevens" />,
name: defineMessage({
description: "Appointments navbar title for 'location and time' step",
defaultMessage: 'Location and time',
}),
},
{
path: 'contactgegevens',
element: <ContactDetailsStep navigateTo="../overzicht" />,
name: defineMessage({
description: "Appointments navbar title for 'contact details' step",
defaultMessage: 'Contact details',
Expand All @@ -31,17 +25,3 @@ export const APPOINTMENT_STEPS = [
];

export const APPOINTMENT_STEP_PATHS = APPOINTMENT_STEPS.map(s => s.path);

// TODO: replace with loader that redirects at the route level
export const LandingPage = () => {
const [params] = useSearchParams();
return (
<Navigate
replace
to={{
pathname: APPOINTMENT_STEP_PATHS[0],
search: `?${params}`,
}}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ describe('Cancelling an appointment', () => {
it('renders the correct page for a cancel route', async () => {
render(<Wrapper />);

const textbox = screen.getByRole('textbox', {name: 'Your email address'});
const textbox = await screen.findByRole('textbox', {name: 'Your email address'});

Check failure on line 46 in src/components/appointments/cancel/CancelAppointment.integration.spec.jsx

View workflow job for this annotation

GitHub Actions / Run Javascript tests

src/components/appointments/cancel/CancelAppointment.integration.spec.jsx > Cancelling an appointment > renders the correct page for a cancel route

TestingLibraryElementError: Unable to find role="textbox" and name "Your email address" Ignored nodes: comments, script, style <body> <div> <h2> Unexpected Application Error! </h2> <h3 style="font-style: italic;" > {"name":"Error","message":"Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?","stack":"Error: Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?\n at TransformPluginContext._formatError (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47574:41)\n at TransformPluginContext.error (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47569:16)\n at normalizeUrl (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:45818:23)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:45937:39\n at async Promise.all (index 0)\n at async TransformPluginContext.transform (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:45864:7)\n at async EnvironmentPluginContainer.transform (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:47400:18)\n at async loadAndTransform (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:41198:27)\n at async ViteNodeServer._transformRequest (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite-node/dist/server.mjs:538:16)\n at async ViteNodeServer._fetchModule (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite-node/dist/server.mjs:505:17)\n at async Proxy.fetch (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vitest/dist/chunks/resolveConfig.9CnFfuqj.js:6630:22)\n at async EventEmitter.onMessage (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vitest/dist/chunks/index.TH3f4LSA.js:91:20)","__vitest_rollup_error__":{"plugin":"vite:import-analysis","id":"/home/runner/work/open-forms-sdk/open-forms-sdk/src/components/appointments/CreateAppointment/LandingPage.jsx","loc":{"file":"/home/runner/work/open-forms-sdk/open-forms-sdk/src/components/appointments/CreateAppointment/LandingPage.jsx","line":1,"column":18},"frame":"101| }\n102| cov_1imwgj2o89();\n103| import { Navigate, useSearchParams } from \"react-router-dom\";\n | ^\n104| import { APPOINTMENT_STEP_PATHS } from \"./steps\";\n105| import { jsx as _jsx } from \"react/jsx-runtime\";"}} </h3> <p> 💿 Hey developer 👋 </p> <p> You can provide a way better UX than this when your app throws errors by providing your own <code style="padding: 2px 4px; background-color: rgba(200, 200, 200, 0.5);" > ErrorBoundary </code> or <code style="padding: 2px 4px; background-color: rgba(200, 200, 200, 0.5);" > errorElement </code> prop on your route. </p> </div> </body> Ignored nodes: comments, script, style <body> <div> <h2> Unexpected Application Error! </h2> <h3 style="font-style: italic;" > {"name":"Error","message":"Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?","stack":"Error: Failed to resolve import \"react-router-dom\" from \"src/components/appointments/CreateAppointment/LandingPage.jsx\". Does the file exist?\n at TransformPluginContext._formatError (file:///home/runner/work/open-forms-sdk/open-forms-sdk/node_modules/vite/dist/node/chunks/dep-M1IYMR16.js:475
expect(textbox).toBeVisible();
expect(textbox.name).toBe('email');
expect(screen.getByRole('button')).toBeVisible();
Expand Down
22 changes: 21 additions & 1 deletion src/components/appointments/index.jsx
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
export {default as CreateAppointment} from './CreateAppointment';
/**
* This module acts as the (lazy loaded) entry point for the appointments chunk.
*/
import CreateAppointment from './CreateAppointment';
import Confirmation from './CreateAppointment/Confirmation';
import {LandingPage} from './CreateAppointment/LandingPage';
import Summary from './CreateAppointment/Summary';
import {CancelAppointment, CancelAppointmentSuccess} from './cancel';
import {ChooseProductStep, ContactDetailsStep, LocationAndTimeStep} from './steps';

export {
CreateAppointment,
Confirmation,
LandingPage,
Summary,
CancelAppointment,
CancelAppointmentSuccess,
ChooseProductStep,
ContactDetailsStep,
LocationAndTimeStep,
};
38 changes: 9 additions & 29 deletions src/routes/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import {Cosign} from 'components/CoSign';
import ErrorBoundary from 'components/Errors/ErrorBoundary';
import Form from 'components/Form';
import SessionExpired from 'components/Sessions/SessionExpired';
import {CreateAppointment} from 'components/appointments';

import {createAppointmentRoutes, manageAppointmentRoutes} from './appointments';
import appointmentRoutes from './appointments';
import cosignRoutes from './cosign';
import formRoutes from './form';

Expand All @@ -24,33 +23,9 @@ const routes = [
element: <App />,
children: [
{
path: 'afspraak-annuleren',
children: [
{
path: '*',
children: manageAppointmentRoutes,
},
],
},
{
path: 'afspraak-maken',
children: [
{
path: '*',
element: <CreateAppointment />,
children: createAppointmentRoutes,
},
],
},
{
path: 'cosign',
children: [
{
path: '*',
element: <Cosign />,
children: cosignRoutes,
},
],
path: 'cosign/*',
element: <Cosign />,
children: cosignRoutes,
},
{
path: 'sessie-verlopen',
Expand All @@ -60,6 +35,11 @@ const routes = [
</ErrorBoundary>
),
},
// appointments splat
{
path: '*',
children: appointmentRoutes,
},
// All the rest goes to the formio-based form flow
{
path: '*',
Expand Down
88 changes: 73 additions & 15 deletions src/routes/appointments.jsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,99 @@
import ErrorBoundary from 'components/Errors/ErrorBoundary';
import Confirmation from 'components/appointments/CreateAppointment/Confirmation';
import Summary from 'components/appointments/CreateAppointment/Summary';
import {APPOINTMENT_STEPS, LandingPage} from 'components/appointments/CreateAppointment/steps';
import {CancelAppointment, CancelAppointmentSuccess} from 'components/appointments/cancel';

/**
* Route subtree for appointment forms.
*/
const createAppointmentRoutes = [
{
path: '',
element: <LandingPage />,
lazy: async () => {
const {LandingPage} = await import('components/appointments');
return {element: <LandingPage />};
},
},
{
path: 'producten',
lazy: async () => {
const {ChooseProductStep} = await import('components/appointments');
return {element: <ChooseProductStep navigateTo="../kalender" />};
},
},
{
path: 'kalender',
lazy: async () => {
const {LocationAndTimeStep} = await import('components/appointments');
return {element: <LocationAndTimeStep navigateTo="../contactgegevens" />};
},
},
{
path: 'contactgegevens',
lazy: async () => {
const {ContactDetailsStep} = await import('components/appointments');
return {element: <ContactDetailsStep navigateTo="../overzicht" />};
},
},
...APPOINTMENT_STEPS.map(({path, element}) => ({path, element})),
{
path: 'overzicht',
element: <Summary />,
lazy: async () => {
const {Summary} = await import('components/appointments');
return {element: <Summary />};
},
},
{
path: 'bevestiging',
element: <Confirmation />,
lazy: async () => {
const {Confirmation} = await import('components/appointments');
return {element: <Confirmation />};
},
},
];

const manageAppointmentRoutes = [
{
path: '',
element: (
<ErrorBoundary>
<CancelAppointment />
</ErrorBoundary>
),
lazy: async () => {
const {CancelAppointment} = await import('components/appointments');
return {
element: (
<ErrorBoundary>
<CancelAppointment />
</ErrorBoundary>
),
};
},
},
{
path: 'succes',
element: <CancelAppointmentSuccess />,
lazy: async () => {
const {CancelAppointmentSuccess} = await import('components/appointments');
return {element: <CancelAppointmentSuccess />};
},
},
];

const appointmentRoutes = [
{
path: 'afspraak-annuleren',
children: [
{
path: '*',
children: manageAppointmentRoutes,
},
],
},
{
path: 'afspraak-maken',
children: [
{
path: '*',
children: createAppointmentRoutes,
lazy: async () => {
const {CreateAppointment} = await import('components/appointments');
return {element: <CreateAppointment />};
},
},
],
},
];

export {createAppointmentRoutes, manageAppointmentRoutes};
export default appointmentRoutes;

0 comments on commit c9abd72

Please sign in to comment.