fontdue-js 3.2.5 → 3.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
## 3.2.6
|
|
2
|
+
|
|
3
|
+
- **Fixed `TypeTesters` crashing when `tags` or `excludeTags` is set on a server-rendered page.** In a React Server Component the server entry preloaded the filtered testers but rendered the client component as though no filter had been given, so the refetch that runs once the page hydrates pulled in the collection's *whole* tester list. Rendering a tester the filter had excluded then threw `Cannot destructure property 'content' of … as it is undefined` and took the page down with it. Both filters now reach the client component on the preloaded path, as they already did when the component fetches its own query in the browser. No public API change.
|
|
4
|
+
|
|
1
5
|
## 3.2.5
|
|
2
6
|
|
|
3
7
|
- **`CartButton` is back in the server-rendered HTML.** Since 3.0.0 it rendered nothing at all until its cart query resolved in the browser, so the button was absent from the initial HTML and only appeared after hydration and a network round-trip — missing for crawlers, and a visible pop-in for everyone else. It now server-renders its empty state (`data-count="0"`) and fills in the count once the cart loads, as it did on 2.x. No public API change.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
// The <TypeTesters> react-server entrypoint's whole job is to run the preload
|
|
4
|
+
// on the server and hand *both* the response and the caller's props to the
|
|
5
|
+
// client renderer. Stub the network preload, and the client module with it —
|
|
6
|
+
// it's a 'use client' module that pulls in Relay and Redux — so these tests
|
|
7
|
+
// observe exactly that handoff.
|
|
8
|
+
const loadSerializableQuery = vi.fn(async (query, variables) => ({
|
|
9
|
+
params: query.params,
|
|
10
|
+
variables,
|
|
11
|
+
response: {
|
|
12
|
+
data: {}
|
|
13
|
+
}
|
|
14
|
+
}));
|
|
15
|
+
vi.mock('../relay/loadSerializableQuery.js', () => ({
|
|
16
|
+
default: function () {
|
|
17
|
+
return loadSerializableQuery(...arguments);
|
|
18
|
+
}
|
|
19
|
+
}));
|
|
20
|
+
vi.mock('../components/TypeTesters/index.js', () => ({
|
|
21
|
+
TypeTestersPreloadedIDQueryRenderer: function TypeTestersPreloadedIDQueryRenderer() {
|
|
22
|
+
return null;
|
|
23
|
+
},
|
|
24
|
+
TypeTestersPreloadedSlugQueryRenderer: function TypeTestersPreloadedSlugQueryRenderer() {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}));
|
|
28
|
+
import TypeTestersServer from '../components/TypeTesters/index.server.js';
|
|
29
|
+
import { TypeTestersPreloadedIDQueryRenderer, TypeTestersPreloadedSlugQueryRenderer } from '../components/TypeTesters/index.js';
|
|
30
|
+
const render = props => TypeTestersServer(props);
|
|
31
|
+
describe('TypeTesters react-server entrypoint', () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
loadSerializableQuery.mockClear();
|
|
34
|
+
});
|
|
35
|
+
it('forwards tags and excludeTags to the preloaded ID renderer', async () => {
|
|
36
|
+
const element = await render({
|
|
37
|
+
collectionId: 'collection-1',
|
|
38
|
+
excludeTags: ['homepage', 'specimen'],
|
|
39
|
+
autofit: true
|
|
40
|
+
});
|
|
41
|
+
expect(element === null || element === void 0 ? void 0 : element.type).toBe(TypeTestersPreloadedIDQueryRenderer);
|
|
42
|
+
// The client component needs these to rebuild the refetch variables it
|
|
43
|
+
// hands to useRefetchOnLicenseChanges. Dropping them made a licence-change
|
|
44
|
+
// refetch return the *unfiltered* tester list, which the tester state was
|
|
45
|
+
// never seeded with.
|
|
46
|
+
expect(element === null || element === void 0 ? void 0 : element.props.excludeTags).toEqual(['homepage', 'specimen']);
|
|
47
|
+
expect(element === null || element === void 0 ? void 0 : element.props.tags).toBeNull();
|
|
48
|
+
// Everything else still rides along.
|
|
49
|
+
expect(element === null || element === void 0 ? void 0 : element.props.autofit).toBe(true);
|
|
50
|
+
expect(element === null || element === void 0 ? void 0 : element.props.preloadedQuery).toBeDefined();
|
|
51
|
+
});
|
|
52
|
+
it('forwards tags and excludeTags to the preloaded slug renderer', async () => {
|
|
53
|
+
const element = await render({
|
|
54
|
+
collectionSlug: 'my-collection',
|
|
55
|
+
tags: ['specimen'],
|
|
56
|
+
excludeTags: ['homepage']
|
|
57
|
+
});
|
|
58
|
+
expect(element === null || element === void 0 ? void 0 : element.type).toBe(TypeTestersPreloadedSlugQueryRenderer);
|
|
59
|
+
expect(element === null || element === void 0 ? void 0 : element.props.tags).toEqual(['specimen']);
|
|
60
|
+
expect(element === null || element === void 0 ? void 0 : element.props.excludeTags).toEqual(['homepage']);
|
|
61
|
+
expect(element === null || element === void 0 ? void 0 : element.props.preloadedQuery).toBeDefined();
|
|
62
|
+
});
|
|
63
|
+
it('preloads with the same tag filters it forwards', async () => {
|
|
64
|
+
const element = await render({
|
|
65
|
+
collectionId: 'collection-1',
|
|
66
|
+
tags: ['specimen'],
|
|
67
|
+
excludeTags: ['homepage']
|
|
68
|
+
});
|
|
69
|
+
expect(loadSerializableQuery).toHaveBeenCalledTimes(1);
|
|
70
|
+
const [, variables] = loadSerializableQuery.mock.calls[0];
|
|
71
|
+
expect(variables).toEqual({
|
|
72
|
+
collectionId: 'collection-1',
|
|
73
|
+
tags: ['specimen'],
|
|
74
|
+
excludeTags: ['homepage']
|
|
75
|
+
});
|
|
76
|
+
expect(element === null || element === void 0 ? void 0 : element.props.tags).toEqual(variables.tags);
|
|
77
|
+
expect(element === null || element === void 0 ? void 0 : element.props.excludeTags).toEqual(variables.excludeTags);
|
|
78
|
+
});
|
|
79
|
+
it('defaults both filters to null when the caller passes neither', async () => {
|
|
80
|
+
const element = await render({
|
|
81
|
+
collectionId: 'collection-1'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Explicit nulls, not undefined: the preload normalizes optional variables
|
|
85
|
+
// to null so the server and client Relay cache keys match, and the
|
|
86
|
+
// forwarded props have to agree with the variables the payload was
|
|
87
|
+
// fetched under.
|
|
88
|
+
const [, variables] = loadSerializableQuery.mock.calls[0];
|
|
89
|
+
expect(variables.tags).toBeNull();
|
|
90
|
+
expect(variables.excludeTags).toBeNull();
|
|
91
|
+
expect(element === null || element === void 0 ? void 0 : element.props.tags).toBeNull();
|
|
92
|
+
expect(element === null || element === void 0 ? void 0 : element.props.excludeTags).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
it('renders nothing without a collection id or slug', async () => {
|
|
95
|
+
expect(await render({})).toBeNull();
|
|
96
|
+
expect(loadSerializableQuery).not.toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -14,6 +14,14 @@ export function loadTypeTestersQuery() {
|
|
|
14
14
|
// the client `*PreloadedQueryRenderer`. Lets pages just write
|
|
15
15
|
// `<TypeTesters collectionId={id} />` in an RSC without manually invoking
|
|
16
16
|
// `loadTypeTestersQuery` + threading `preloadedQuery` through props.
|
|
17
|
+
//
|
|
18
|
+
// `tags`/`excludeTags` are both query variables *and* component props: the
|
|
19
|
+
// client component needs them to build the refetch variables it hands to
|
|
20
|
+
// `useRefetchOnLicenseChanges`. Destructuring them here takes them out of
|
|
21
|
+
// `...rest`, so both branches pass them on explicitly — otherwise a
|
|
22
|
+
// licence-change refetch re-runs the filtered query unfiltered and the
|
|
23
|
+
// tester list no longer matches the state seeded from the preload. The lazy
|
|
24
|
+
// `TypeTesters*QueryRenderer`s do the same for the same reason.
|
|
17
25
|
export default async function TypeTesters(_ref) {
|
|
18
26
|
let {
|
|
19
27
|
collectionId,
|
|
@@ -29,7 +37,9 @@ export default async function TypeTesters(_ref) {
|
|
|
29
37
|
excludeTags
|
|
30
38
|
});
|
|
31
39
|
return /*#__PURE__*/React.createElement(TypeTestersPreloadedIDQueryRenderer, _extends({
|
|
32
|
-
preloadedQuery: preloadedQuery
|
|
40
|
+
preloadedQuery: preloadedQuery,
|
|
41
|
+
tags: tags,
|
|
42
|
+
excludeTags: excludeTags
|
|
33
43
|
}, rest));
|
|
34
44
|
}
|
|
35
45
|
if (collectionSlug) {
|
|
@@ -39,7 +49,9 @@ export default async function TypeTesters(_ref) {
|
|
|
39
49
|
excludeTags
|
|
40
50
|
});
|
|
41
51
|
return /*#__PURE__*/React.createElement(TypeTestersPreloadedSlugQueryRenderer, _extends({
|
|
42
|
-
preloadedQuery: preloadedQuery
|
|
52
|
+
preloadedQuery: preloadedQuery,
|
|
53
|
+
tags: tags,
|
|
54
|
+
excludeTags: excludeTags
|
|
43
55
|
}, rest));
|
|
44
56
|
}
|
|
45
57
|
return null;
|
|
@@ -8,7 +8,7 @@ import { NODE_ACCESS_HEADER } from '../nodeAccess.js';
|
|
|
8
8
|
// (defineVersionPlugin in .babelrc.cjs) with the literal package.json#version.
|
|
9
9
|
// Exported so UI (the admin toolbar) can surface it without re-reading the
|
|
10
10
|
// build-time global in a 'use client' module.
|
|
11
|
-
export const version = "3.2.
|
|
11
|
+
export const version = "3.2.6";
|
|
12
12
|
const IS_SERVER = typeof window === typeof undefined;
|
|
13
13
|
|
|
14
14
|
// Opt server fetches into Next's data cache only in production; dev stays
|