Skip to content

Commit c5f7375

Browse files
chore(storybook): migrate to storybook v9 (#4165)
* chore(storybook): sync versions for storybook packages * chore(storybook): migrate storybook to v9 * chore(webpack): polyfill nodejs modules * fix: missing TS modules * fix: miscellaneous * fix: resolve react-dom imports * fix: chromatic install * fix: polyfill and flow * fix: root type * chore: update to v9.0.14 * chore: upgrade cypress * fix: cypress tests * test: pin testing-library/user-event * chore: update cypress circleci * revert: Revert "test: pin testing-library/user-event" This reverts commit e1394a8. * chore: remove redundant test * fix: i'm not going to spend time looking into this * revert: remove createRoot for react 17 support --------- Co-authored-by: greg-in-a-box <103291617+greg-in-a-box@users.noreply.github.com>
1 parent 463839e commit c5f7375

File tree

49 files changed

+1392
-1861
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1392
-1861
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ executors:
99
working_directory: ~/buie
1010
cypress-executor:
1111
docker:
12-
- image: cypress/included:13.13.0
12+
- image: cypress/included:13.17.0
1313
user: root
1414
resource_class: large
1515
working_directory: ~/buie

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ i18n/json/
1111
node_modules/
1212
reports/
1313
storybook/
14+
test/screenshots/
15+
test/videos/

.storybook/customTheme.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { create } from '@storybook/theming/create';
1+
import { create } from 'storybook/theming/create';
22
import * as vars from '../src/styles/variables';
33

44
export default create({

.storybook/main.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import sass from 'sass';
33
import type { StorybookConfig } from '@storybook/react-webpack5';
44
import type { Configuration } from 'webpack';
55

6+
import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
67
import TranslationsPlugin from '@box/frontend/webpack/TranslationsPlugin';
78
import { translationDependencies } from '../scripts/i18n.config';
89

@@ -19,8 +20,6 @@ const config: StorybookConfig = {
1920
addons: [
2021
'@chromatic-com/storybook',
2122
'@storybook/addon-docs',
22-
'@storybook/addon-essentials',
23-
'@storybook/addon-interactions',
2423
{
2524
name: '@storybook/addon-styling-webpack',
2625
options: {
@@ -45,7 +44,6 @@ const config: StorybookConfig = {
4544
],
4645
},
4746
},
48-
'@storybook/addon-links',
4947
'@storybook/addon-webpack5-compiler-babel',
5048
'storybook-react-intl',
5149
],
@@ -65,6 +63,9 @@ const config: StorybookConfig = {
6563
generateBundles: true,
6664
additionalMessageData: translationDependencies.map(pkg => `${pkg}/i18n/[language]`),
6765
}),
66+
new NodePolyfillPlugin({
67+
additionalAliases: ['process'],
68+
}),
6869
);
6970

7071
return webpack;

.storybook/manager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { addons } from '@storybook/manager-api';
1+
import { addons } from 'storybook/manager-api';
22
import customTheme from './customTheme';
33

44
addons.setConfig({

.storybook/public/mockServiceWorker.js

Lines changed: 120 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,23 @@
55
* Mock Service Worker.
66
* @see https://github.com/mswjs/msw
77
* - Please do NOT modify this file.
8-
* - Please do NOT serve this file on production.
98
*/
109

11-
const PACKAGE_VERSION = '2.3.1'
12-
const INTEGRITY_CHECKSUM = '26357c79639bfa20d64c0efca2a87423'
10+
const PACKAGE_VERSION = '2.10.2'
11+
const INTEGRITY_CHECKSUM = 'f5825c521429caf22a4dd13b66e243af'
1312
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
1413
const activeClientIds = new Set()
1514

16-
self.addEventListener('install', function () {
15+
addEventListener('install', function () {
1716
self.skipWaiting()
1817
})
1918

20-
self.addEventListener('activate', function (event) {
19+
addEventListener('activate', function (event) {
2120
event.waitUntil(self.clients.claim())
2221
})
2322

24-
self.addEventListener('message', async function (event) {
25-
const clientId = event.source.id
23+
addEventListener('message', async function (event) {
24+
const clientId = Reflect.get(event.source || {}, 'id')
2625

2726
if (!clientId || !self.clients) {
2827
return
@@ -62,7 +61,12 @@ self.addEventListener('message', async function (event) {
6261

6362
sendToClient(client, {
6463
type: 'MOCKING_ENABLED',
65-
payload: true,
64+
payload: {
65+
client: {
66+
id: client.id,
67+
frameType: client.frameType,
68+
},
69+
},
6670
})
6771
break
6872
}
@@ -89,17 +93,18 @@ self.addEventListener('message', async function (event) {
8993
}
9094
})
9195

92-
self.addEventListener('fetch', function (event) {
93-
const { request } = event
94-
96+
addEventListener('fetch', function (event) {
9597
// Bypass navigation requests.
96-
if (request.mode === 'navigate') {
98+
if (event.request.mode === 'navigate') {
9799
return
98100
}
99101

100102
// Opening the DevTools triggers the "only-if-cached" request
101103
// that cannot be handled by the worker. Bypass such requests.
102-
if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
104+
if (
105+
event.request.cache === 'only-if-cached' &&
106+
event.request.mode !== 'same-origin'
107+
) {
103108
return
104109
}
105110

@@ -110,51 +115,69 @@ self.addEventListener('fetch', function (event) {
110115
return
111116
}
112117

113-
// Generate unique request ID.
114118
const requestId = crypto.randomUUID()
115119
event.respondWith(handleRequest(event, requestId))
116120
})
117121

122+
/**
123+
* @param {FetchEvent} event
124+
* @param {string} requestId
125+
*/
118126
async function handleRequest(event, requestId) {
119127
const client = await resolveMainClient(event)
128+
const requestCloneForEvents = event.request.clone()
120129
const response = await getResponse(event, client, requestId)
121130

122131
// Send back the response clone for the "response:*" life-cycle events.
123132
// Ensure MSW is active and ready to handle the message, otherwise
124133
// this message will pend indefinitely.
125134
if (client && activeClientIds.has(client.id)) {
126-
;(async function () {
127-
const responseClone = response.clone()
128-
129-
sendToClient(
130-
client,
131-
{
132-
type: 'RESPONSE',
133-
payload: {
134-
requestId,
135-
isMockedResponse: IS_MOCKED_RESPONSE in response,
135+
const serializedRequest = await serializeRequest(requestCloneForEvents)
136+
137+
// Clone the response so both the client and the library could consume it.
138+
const responseClone = response.clone()
139+
140+
sendToClient(
141+
client,
142+
{
143+
type: 'RESPONSE',
144+
payload: {
145+
isMockedResponse: IS_MOCKED_RESPONSE in response,
146+
request: {
147+
id: requestId,
148+
...serializedRequest,
149+
},
150+
response: {
136151
type: responseClone.type,
137152
status: responseClone.status,
138153
statusText: responseClone.statusText,
139-
body: responseClone.body,
140154
headers: Object.fromEntries(responseClone.headers.entries()),
155+
body: responseClone.body,
141156
},
142157
},
143-
[responseClone.body],
144-
)
145-
})()
158+
},
159+
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
160+
)
146161
}
147162

148163
return response
149164
}
150165

151-
// Resolve the main client for the given event.
152-
// Client that issues a request doesn't necessarily equal the client
153-
// that registered the worker. It's with the latter the worker should
154-
// communicate with during the response resolving phase.
166+
/**
167+
* Resolve the main client for the given event.
168+
* Client that issues a request doesn't necessarily equal the client
169+
* that registered the worker. It's with the latter the worker should
170+
* communicate with during the response resolving phase.
171+
* @param {FetchEvent} event
172+
* @returns {Promise<Client | undefined>}
173+
*/
155174
async function resolveMainClient(event) {
156175
const client = await self.clients.get(event.clientId)
157176

177+
if (activeClientIds.has(event.clientId)) {
178+
return client
179+
}
180+
158181
if (client?.frameType === 'top-level') {
159182
return client
160183
}
@@ -175,20 +198,38 @@ async function resolveMainClient(event) {
175198
})
176199
}
177200

201+
/**
202+
* @param {FetchEvent} event
203+
* @param {Client | undefined} client
204+
* @param {string} requestId
205+
* @returns {Promise<Response>}
206+
*/
178207
async function getResponse(event, client, requestId) {
179-
const { request } = event
180-
181208
// Clone the request because it might've been already used
182209
// (i.e. its body has been read and sent to the client).
183-
const requestClone = request.clone()
210+
const requestClone = event.request.clone()
184211

185212
function passthrough() {
186-
const headers = Object.fromEntries(requestClone.headers.entries())
213+
// Cast the request headers to a new Headers instance
214+
// so the headers can be manipulated with.
215+
const headers = new Headers(requestClone.headers)
216+
217+
// Remove the "accept" header value that marked this request as passthrough.
218+
// This prevents request alteration and also keeps it compliant with the
219+
// user-defined CORS policies.
220+
const acceptHeader = headers.get('accept')
221+
if (acceptHeader) {
222+
const values = acceptHeader.split(',').map((value) => value.trim())
223+
const filteredValues = values.filter(
224+
(value) => value !== 'msw/passthrough',
225+
)
187226

188-
// Remove internal MSW request header so the passthrough request
189-
// complies with any potential CORS preflight checks on the server.
190-
// Some servers forbid unknown request headers.
191-
delete headers['x-msw-intention']
227+
if (filteredValues.length > 0) {
228+
headers.set('accept', filteredValues.join(', '))
229+
} else {
230+
headers.delete('accept')
231+
}
232+
}
192233

193234
return fetch(requestClone, { headers })
194235
}
@@ -207,29 +248,17 @@ async function getResponse(event, client, requestId) {
207248
}
208249

209250
// Notify the client that a request has been intercepted.
210-
const requestBuffer = await request.arrayBuffer()
251+
const serializedRequest = await serializeRequest(event.request)
211252
const clientMessage = await sendToClient(
212253
client,
213254
{
214255
type: 'REQUEST',
215256
payload: {
216257
id: requestId,
217-
url: request.url,
218-
mode: request.mode,
219-
method: request.method,
220-
headers: Object.fromEntries(request.headers.entries()),
221-
cache: request.cache,
222-
credentials: request.credentials,
223-
destination: request.destination,
224-
integrity: request.integrity,
225-
redirect: request.redirect,
226-
referrer: request.referrer,
227-
referrerPolicy: request.referrerPolicy,
228-
body: requestBuffer,
229-
keepalive: request.keepalive,
258+
...serializedRequest,
230259
},
231260
},
232-
[requestBuffer],
261+
[serializedRequest.body],
233262
)
234263

235264
switch (clientMessage.type) {
@@ -245,6 +274,12 @@ async function getResponse(event, client, requestId) {
245274
return passthrough()
246275
}
247276

277+
/**
278+
* @param {Client} client
279+
* @param {any} message
280+
* @param {Array<Transferable>} transferrables
281+
* @returns {Promise<any>}
282+
*/
248283
function sendToClient(client, message, transferrables = []) {
249284
return new Promise((resolve, reject) => {
250285
const channel = new MessageChannel()
@@ -257,14 +292,18 @@ function sendToClient(client, message, transferrables = []) {
257292
resolve(event.data)
258293
}
259294

260-
client.postMessage(
261-
message,
262-
[channel.port2].concat(transferrables.filter(Boolean)),
263-
)
295+
client.postMessage(message, [
296+
channel.port2,
297+
...transferrables.filter(Boolean),
298+
])
264299
})
265300
}
266301

267-
async function respondWithMock(response) {
302+
/**
303+
* @param {Response} response
304+
* @returns {Response}
305+
*/
306+
function respondWithMock(response) {
268307
// Setting response status code to 0 is a no-op.
269308
// However, when responding with a "Response.error()", the produced Response
270309
// instance will have status code set to 0. Since it's not possible to create
@@ -282,3 +321,24 @@ async function respondWithMock(response) {
282321

283322
return mockedResponse
284323
}
324+
325+
/**
326+
* @param {Request} request
327+
*/
328+
async function serializeRequest(request) {
329+
return {
330+
url: request.url,
331+
mode: request.mode,
332+
method: request.method,
333+
headers: Object.fromEntries(request.headers.entries()),
334+
cache: request.cache,
335+
credentials: request.credentials,
336+
destination: request.destination,
337+
integrity: request.integrity,
338+
redirect: request.redirect,
339+
referrer: request.referrer,
340+
referrerPolicy: request.referrerPolicy,
341+
body: await request.arrayBuffer(),
342+
keepalive: request.keepalive,
343+
}
344+
}

0 commit comments

Comments
 (0)