Skip to content

Commit 236ba7d

Browse files
docs(ui): add stories for Recharging page (#2496)
Co-authored-by: Willow (GHOST) <git@willow.sh>
1 parent c145e99 commit 236ba7d

File tree

9 files changed

+699
-0
lines changed

9 files changed

+699
-0
lines changed
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/* eslint-disable */
2+
3+
/**
4+
* Mock Service Worker.
5+
* @see https://github.com/mswjs/msw
6+
* - Please do NOT modify this file.
7+
*/
8+
9+
const PACKAGE_VERSION = '2.13.2'
10+
const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
11+
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
12+
const activeClientIds = new Set()
13+
14+
addEventListener('install', function () {
15+
self.skipWaiting()
16+
})
17+
18+
addEventListener('activate', function (event) {
19+
event.waitUntil(self.clients.claim())
20+
})
21+
22+
addEventListener('message', async function (event) {
23+
const clientId = Reflect.get(event.source || {}, 'id')
24+
25+
if (!clientId || !self.clients) {
26+
return
27+
}
28+
29+
const client = await self.clients.get(clientId)
30+
31+
if (!client) {
32+
return
33+
}
34+
35+
const allClients = await self.clients.matchAll({
36+
type: 'window',
37+
})
38+
39+
switch (event.data) {
40+
case 'KEEPALIVE_REQUEST': {
41+
sendToClient(client, {
42+
type: 'KEEPALIVE_RESPONSE',
43+
})
44+
break
45+
}
46+
47+
case 'INTEGRITY_CHECK_REQUEST': {
48+
sendToClient(client, {
49+
type: 'INTEGRITY_CHECK_RESPONSE',
50+
payload: {
51+
packageVersion: PACKAGE_VERSION,
52+
checksum: INTEGRITY_CHECKSUM,
53+
},
54+
})
55+
break
56+
}
57+
58+
case 'MOCK_ACTIVATE': {
59+
activeClientIds.add(clientId)
60+
61+
sendToClient(client, {
62+
type: 'MOCKING_ENABLED',
63+
payload: {
64+
client: {
65+
id: client.id,
66+
frameType: client.frameType,
67+
},
68+
},
69+
})
70+
break
71+
}
72+
73+
case 'CLIENT_CLOSED': {
74+
activeClientIds.delete(clientId)
75+
76+
const remainingClients = allClients.filter(client => {
77+
return client.id !== clientId
78+
})
79+
80+
// Unregister itself when there are no more clients
81+
if (remainingClients.length === 0) {
82+
self.registration.unregister()
83+
}
84+
85+
break
86+
}
87+
}
88+
})
89+
90+
addEventListener('fetch', function (event) {
91+
const requestInterceptedAt = Date.now()
92+
93+
// Bypass navigation requests.
94+
if (event.request.mode === 'navigate') {
95+
return
96+
}
97+
98+
// Opening the DevTools triggers the "only-if-cached" request
99+
// that cannot be handled by the worker. Bypass such requests.
100+
if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
101+
return
102+
}
103+
104+
// Bypass all requests when there are no active clients.
105+
// Prevents the self-unregistered worked from handling requests
106+
// after it's been terminated (still remains active until the next reload).
107+
if (activeClientIds.size === 0) {
108+
return
109+
}
110+
111+
const requestId = crypto.randomUUID()
112+
event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
113+
})
114+
115+
/**
116+
* @param {FetchEvent} event
117+
* @param {string} requestId
118+
* @param {number} requestInterceptedAt
119+
*/
120+
async function handleRequest(event, requestId, requestInterceptedAt) {
121+
const client = await resolveMainClient(event)
122+
const requestCloneForEvents = event.request.clone()
123+
const response = await getResponse(event, client, requestId, requestInterceptedAt)
124+
125+
// Send back the response clone for the "response:*" life-cycle events.
126+
// Ensure MSW is active and ready to handle the message, otherwise
127+
// this message will pend indefinitely.
128+
if (client && activeClientIds.has(client.id)) {
129+
const serializedRequest = await serializeRequest(requestCloneForEvents)
130+
131+
// Clone the response so both the client and the library could consume it.
132+
const responseClone = response.clone()
133+
134+
sendToClient(
135+
client,
136+
{
137+
type: 'RESPONSE',
138+
payload: {
139+
isMockedResponse: IS_MOCKED_RESPONSE in response,
140+
request: {
141+
id: requestId,
142+
...serializedRequest,
143+
},
144+
response: {
145+
type: responseClone.type,
146+
status: responseClone.status,
147+
statusText: responseClone.statusText,
148+
headers: Object.fromEntries(responseClone.headers.entries()),
149+
body: responseClone.body,
150+
},
151+
},
152+
},
153+
responseClone.body ? [serializedRequest.body, responseClone.body] : [],
154+
)
155+
}
156+
157+
return response
158+
}
159+
160+
/**
161+
* Resolve the main client for the given event.
162+
* Client that issues a request doesn't necessarily equal the client
163+
* that registered the worker. It's with the latter the worker should
164+
* communicate with during the response resolving phase.
165+
* @param {FetchEvent} event
166+
* @returns {Promise<Client | undefined>}
167+
*/
168+
async function resolveMainClient(event) {
169+
const client = await self.clients.get(event.clientId)
170+
171+
if (activeClientIds.has(event.clientId)) {
172+
return client
173+
}
174+
175+
if (client?.frameType === 'top-level') {
176+
return client
177+
}
178+
179+
const allClients = await self.clients.matchAll({
180+
type: 'window',
181+
})
182+
183+
return allClients
184+
.filter(client => {
185+
// Get only those clients that are currently visible.
186+
return client.visibilityState === 'visible'
187+
})
188+
.find(client => {
189+
// Find the client ID that's recorded in the
190+
// set of clients that have registered the worker.
191+
return activeClientIds.has(client.id)
192+
})
193+
}
194+
195+
/**
196+
* @param {FetchEvent} event
197+
* @param {Client | undefined} client
198+
* @param {string} requestId
199+
* @param {number} requestInterceptedAt
200+
* @returns {Promise<Response>}
201+
*/
202+
async function getResponse(event, client, requestId, requestInterceptedAt) {
203+
// Clone the request because it might've been already used
204+
// (i.e. its body has been read and sent to the client).
205+
const requestClone = event.request.clone()
206+
207+
function passthrough() {
208+
// Cast the request headers to a new Headers instance
209+
// so the headers can be manipulated with.
210+
const headers = new Headers(requestClone.headers)
211+
212+
// Remove the "accept" header value that marked this request as passthrough.
213+
// This prevents request alteration and also keeps it compliant with the
214+
// user-defined CORS policies.
215+
const acceptHeader = headers.get('accept')
216+
if (acceptHeader) {
217+
const values = acceptHeader.split(',').map(value => value.trim())
218+
const filteredValues = values.filter(value => value !== 'msw/passthrough')
219+
220+
if (filteredValues.length > 0) {
221+
headers.set('accept', filteredValues.join(', '))
222+
} else {
223+
headers.delete('accept')
224+
}
225+
}
226+
227+
return fetch(requestClone, { headers })
228+
}
229+
230+
// Bypass mocking when the client is not active.
231+
if (!client) {
232+
return passthrough()
233+
}
234+
235+
// Bypass initial page load requests (i.e. static assets).
236+
// The absence of the immediate/parent client in the map of the active clients
237+
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
238+
// and is not ready to handle requests.
239+
if (!activeClientIds.has(client.id)) {
240+
return passthrough()
241+
}
242+
243+
// Notify the client that a request has been intercepted.
244+
const serializedRequest = await serializeRequest(event.request)
245+
const clientMessage = await sendToClient(
246+
client,
247+
{
248+
type: 'REQUEST',
249+
payload: {
250+
id: requestId,
251+
interceptedAt: requestInterceptedAt,
252+
...serializedRequest,
253+
},
254+
},
255+
[serializedRequest.body],
256+
)
257+
258+
switch (clientMessage.type) {
259+
case 'MOCK_RESPONSE': {
260+
return respondWithMock(clientMessage.data)
261+
}
262+
263+
case 'PASSTHROUGH': {
264+
return passthrough()
265+
}
266+
}
267+
268+
return passthrough()
269+
}
270+
271+
/**
272+
* @param {Client} client
273+
* @param {any} message
274+
* @param {Array<Transferable>} transferrables
275+
* @returns {Promise<any>}
276+
*/
277+
function sendToClient(client, message, transferrables = []) {
278+
return new Promise((resolve, reject) => {
279+
const channel = new MessageChannel()
280+
281+
channel.port1.onmessage = event => {
282+
if (event.data && event.data.error) {
283+
return reject(event.data.error)
284+
}
285+
286+
resolve(event.data)
287+
}
288+
289+
client.postMessage(message, [channel.port2, ...transferrables.filter(Boolean)])
290+
})
291+
}
292+
293+
/**
294+
* @param {Response} response
295+
* @returns {Response}
296+
*/
297+
function respondWithMock(response) {
298+
// Setting response status code to 0 is a no-op.
299+
// However, when responding with a "Response.error()", the produced Response
300+
// instance will have status code set to 0. Since it's not possible to create
301+
// a Response instance with status code 0, handle that use-case separately.
302+
if (response.status === 0) {
303+
return Response.error()
304+
}
305+
306+
const mockedResponse = new Response(response.body, response)
307+
308+
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
309+
value: true,
310+
enumerable: true,
311+
})
312+
313+
return mockedResponse
314+
}
315+
316+
/**
317+
* @param {Request} request
318+
*/
319+
async function serializeRequest(request) {
320+
return {
321+
url: request.url,
322+
mode: request.mode,
323+
method: request.method,
324+
headers: Object.fromEntries(request.headers.entries()),
325+
cache: request.cache,
326+
credentials: request.credentials,
327+
destination: request.destination,
328+
integrity: request.integrity,
329+
redirect: request.redirect,
330+
referrer: request.referrer,
331+
referrerPolicy: request.referrerPolicy,
332+
body: await request.arrayBuffer(),
333+
keepalive: request.keepalive,
334+
}
335+
}

.storybook/handlers.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { http, HttpResponse } from 'msw'
2+
3+
export const repoStatsHandler = http.get('/api/repo-stats', () => {
4+
return HttpResponse.json({
5+
contributors: 123,
6+
commits: 1234,
7+
pullRequests: 1234,
8+
})
9+
})
10+
11+
export const contributorsHandler = http.get('/api/contributors', () => {
12+
return HttpResponse.json([
13+
{
14+
login: 'mock-steward-a',
15+
id: 1001,
16+
avatar_url: 'https://api.dicebear.com/9.x/initials/svg?seed=steward-a',
17+
html_url: 'https://github.com/mock-steward-a',
18+
contributions: 2800,
19+
role: 'steward',
20+
sponsors_url: 'https://github.com/sponsors/',
21+
},
22+
{
23+
login: 'mock-steward-b',
24+
id: 1002,
25+
avatar_url: 'https://api.dicebear.com/9.x/initials/svg?seed=steward-b',
26+
html_url: 'https://github.com/mock-steward-b',
27+
contributions: 420,
28+
role: 'steward',
29+
sponsors_url: null,
30+
},
31+
{
32+
login: 'mock-maintainer-a',
33+
id: 1003,
34+
avatar_url: 'https://api.dicebear.com/9.x/initials/svg?seed=maintainer-a',
35+
html_url: 'https://github.com/mock-maintainer-a',
36+
contributions: 210,
37+
role: 'maintainer',
38+
sponsors_url: null,
39+
},
40+
{
41+
login: 'mock-contributor-a',
42+
id: 1004,
43+
avatar_url: 'https://api.dicebear.com/9.x/initials/svg?seed=contributor-a',
44+
html_url: 'https://github.com/mock-contributor-a',
45+
contributions: 95,
46+
role: 'contributor',
47+
sponsors_url: 'https://github.com/sponsors/',
48+
},
49+
{
50+
login: 'mock-contributor-b',
51+
id: 1005,
52+
avatar_url: 'https://api.dicebear.com/9.x/initials/svg?seed=contributor-b',
53+
html_url: 'https://github.com/mock-contributor-b',
54+
contributions: 47,
55+
role: 'contributor',
56+
sponsors_url: null,
57+
},
58+
])
59+
})

0 commit comments

Comments
 (0)