fontdue-js 3.8.0 → 3.10.0

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.
Files changed (32) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +3 -1
  3. package/dist/__generated__/StoreModalIndexItem_fontCollection.graphql.d.ts +10 -1
  4. package/dist/__generated__/StoreModalIndexItem_fontCollection.graphql.js +45 -35
  5. package/dist/__generated__/StoreModalIndexPaginationQuery.graphql.d.ts +1 -1
  6. package/dist/__generated__/StoreModalIndexPaginationQuery.graphql.js +101 -93
  7. package/dist/__generated__/StoreModalIndexQuery.graphql.d.ts +3 -1
  8. package/dist/__generated__/StoreModalIndexQuery.graphql.js +24 -17
  9. package/dist/__generated__/TestFontsForm_Query.graphql.d.ts +14 -2
  10. package/dist/__generated__/TestFontsForm_Query.graphql.js +76 -18
  11. package/dist/__tests__/storeModalIndexUnlisted.test.js +71 -0
  12. package/dist/__tests__/testFontsFamilyChoice.test.js +150 -0
  13. package/dist/__tests__/typeTesterTruncate.test.js +71 -1
  14. package/dist/components/ConfigContext.d.ts +11 -0
  15. package/dist/components/ConfigContext.js +7 -1
  16. package/dist/components/StoreModal/StoreModalIndex.js +8 -2
  17. package/dist/components/StoreModal/StoreModalIndexItem.d.ts +1 -1
  18. package/dist/components/StoreModal/StoreModalIndexItem.js +26 -8
  19. package/dist/components/TestFontsForm/TestFontsFormElement.d.ts +4 -1
  20. package/dist/components/TestFontsForm/TestFontsFormElement.js +11 -3
  21. package/dist/components/TestFontsForm/index.d.ts +6 -3
  22. package/dist/components/TestFontsForm/index.js +82 -13
  23. package/dist/components/TestFontsForm/index.server.js +2 -1
  24. package/dist/components/TestFontsForm/variables.d.ts +8 -0
  25. package/dist/components/TestFontsForm/variables.js +22 -0
  26. package/dist/components/TypeTester/truncate.d.ts +10 -0
  27. package/dist/components/TypeTester/truncate.js +31 -0
  28. package/dist/components/TypeTester/useTypeTesterStyler.d.ts +1 -6
  29. package/dist/components/TypeTester/useTypeTesterStyler.js +22 -15
  30. package/dist/fontdue.css +22 -10
  31. package/dist/version.js +1 -1
  32. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @generated SignedSource<<ee5a584fb3013fee58346747b616f67d>>
2
+ * @generated SignedSource<<47f718aeae16846330759690c693dce6>>
3
3
  * @lightSyntaxTransform
4
4
  * @nogrep
5
5
  */
@@ -10,11 +10,20 @@
10
10
 
11
11
  const node = function () {
12
12
  var v0 = [{
13
+ "defaultValue": null,
14
+ "kind": "LocalArgument",
15
+ "name": "collectionIds"
16
+ }, {
17
+ "defaultValue": null,
18
+ "kind": "LocalArgument",
19
+ "name": "collectionSlugs"
20
+ }],
21
+ v1 = [{
13
22
  "kind": "Literal",
14
23
  "name": "format",
15
24
  "value": "HTML"
16
25
  }],
17
- v1 = {
26
+ v2 = {
18
27
  "alias": null,
19
28
  "args": null,
20
29
  "concreteType": "Settings",
@@ -23,13 +32,13 @@ const node = function () {
23
32
  "plural": false,
24
33
  "selections": [{
25
34
  "alias": null,
26
- "args": v0 /*: any*/,
35
+ "args": v1 /*: any*/,
27
36
  "kind": "ScalarField",
28
37
  "name": "eulaAgreementLabel",
29
38
  "storageKey": "eulaAgreementLabel(format:\"HTML\")"
30
39
  }, {
31
40
  "alias": null,
32
- "args": v0 /*: any*/,
41
+ "args": v1 /*: any*/,
33
42
  "kind": "ScalarField",
34
43
  "name": "newsletterOptInLabel",
35
44
  "storageKey": "newsletterOptInLabel(format:\"HTML\")"
@@ -48,9 +57,49 @@ const node = function () {
48
57
  }],
49
58
  "storageKey": null
50
59
  },
51
- v2 = {
60
+ v3 = {
61
+ "alias": null,
62
+ "args": null,
63
+ "concreteType": "FontdueConfig",
64
+ "kind": "LinkedField",
65
+ "name": "fontdueConfig",
66
+ "plural": false,
67
+ "selections": [{
68
+ "alias": null,
69
+ "args": null,
70
+ "concreteType": "FontdueTestFontsConfig",
71
+ "kind": "LinkedField",
72
+ "name": "testFonts",
73
+ "plural": false,
74
+ "selections": [{
75
+ "alias": null,
76
+ "args": null,
77
+ "kind": "ScalarField",
78
+ "name": "familySelection",
79
+ "storageKey": null
80
+ }],
81
+ "storageKey": null
82
+ }],
83
+ "storageKey": null
84
+ },
85
+ v4 = {
52
86
  "alias": null,
53
87
  "args": null,
88
+ "kind": "ScalarField",
89
+ "name": "id",
90
+ "storageKey": null
91
+ },
92
+ v5 = {
93
+ "alias": null,
94
+ "args": [{
95
+ "kind": "Variable",
96
+ "name": "collectionIds",
97
+ "variableName": "collectionIds"
98
+ }, {
99
+ "kind": "Variable",
100
+ "name": "collectionSlugs",
101
+ "variableName": "collectionSlugs"
102
+ }],
54
103
  "concreteType": "TestFonts",
55
104
  "kind": "LinkedField",
56
105
  "name": "testFonts",
@@ -61,12 +110,27 @@ const node = function () {
61
110
  "kind": "ScalarField",
62
111
  "name": "archiveUrl",
63
112
  "storageKey": null
113
+ }, {
114
+ "alias": null,
115
+ "args": null,
116
+ "concreteType": "FontCollection",
117
+ "kind": "LinkedField",
118
+ "name": "families",
119
+ "plural": true,
120
+ "selections": [v4 /*: any*/, {
121
+ "alias": null,
122
+ "args": null,
123
+ "kind": "ScalarField",
124
+ "name": "name",
125
+ "storageKey": null
126
+ }],
127
+ "storageKey": null
64
128
  }],
65
129
  "storageKey": null
66
130
  };
67
131
  return {
68
132
  "fragment": {
69
- "argumentDefinitions": [],
133
+ "argumentDefinitions": v0 /*: any*/,
70
134
  "kind": "Fragment",
71
135
  "metadata": null,
72
136
  "name": "TestFontsForm_Query",
@@ -77,7 +141,7 @@ const node = function () {
77
141
  "kind": "LinkedField",
78
142
  "name": "viewer",
79
143
  "plural": false,
80
- "selections": [v1 /*: any*/, v2 /*: any*/],
144
+ "selections": [v2 /*: any*/, v3 /*: any*/, v5 /*: any*/],
81
145
  "storageKey": null
82
146
  }],
83
147
  "type": "RootQueryType",
@@ -85,7 +149,7 @@ const node = function () {
85
149
  },
86
150
  "kind": "Request",
87
151
  "operation": {
88
- "argumentDefinitions": [],
152
+ "argumentDefinitions": v0 /*: any*/,
89
153
  "kind": "Operation",
90
154
  "name": "TestFontsForm_Query",
91
155
  "selections": [{
@@ -95,25 +159,19 @@ const node = function () {
95
159
  "kind": "LinkedField",
96
160
  "name": "viewer",
97
161
  "plural": false,
98
- "selections": [v1 /*: any*/, v2 /*: any*/, {
99
- "alias": null,
100
- "args": null,
101
- "kind": "ScalarField",
102
- "name": "id",
103
- "storageKey": null
104
- }],
162
+ "selections": [v2 /*: any*/, v3 /*: any*/, v5 /*: any*/, v4 /*: any*/],
105
163
  "storageKey": null
106
164
  }]
107
165
  },
108
166
  "params": {
109
- "cacheID": "328cab794a68cbe666374e901da4d26b",
167
+ "cacheID": "42a58842afe113b89a0bc969738246a8",
110
168
  "id": null,
111
169
  "metadata": {},
112
170
  "name": "TestFontsForm_Query",
113
171
  "operationKind": "query",
114
- "text": "query TestFontsForm_Query {\n viewer {\n settings {\n eulaAgreementLabel(format: HTML)\n newsletterOptInLabel(format: HTML)\n recaptchaEnabled\n recaptchaSiteKey\n }\n testFonts {\n archiveUrl\n }\n id\n }\n}\n"
172
+ "text": "query TestFontsForm_Query(\n $collectionIds: [ID!]\n $collectionSlugs: [String!]\n) {\n viewer {\n settings {\n eulaAgreementLabel(format: HTML)\n newsletterOptInLabel(format: HTML)\n recaptchaEnabled\n recaptchaSiteKey\n }\n fontdueConfig {\n testFonts {\n familySelection\n }\n }\n testFonts(collectionIds: $collectionIds, collectionSlugs: $collectionSlugs) {\n archiveUrl\n families {\n id\n name\n }\n }\n id\n }\n}\n"
115
173
  }
116
174
  };
117
175
  }();
118
- node.hash = "cd43f0cacc4dcf01cf94fb1ff97197ca";
176
+ node.hash = "da75e312a602ac0ee2dfb79271299c77";
119
177
  export default node;
@@ -0,0 +1,71 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ // The index lists root collections. An unlisted one isn't offered on its own
4
+ // (FD-1239): an unlisted family drops out, and an unlisted superfamily is
5
+ // replaced by its listed families, each opening the superfamily's page so a
6
+ // selection there still upgrades to it.
7
+
8
+ import React from 'react';
9
+ import { renderToStaticMarkup } from 'react-dom/server';
10
+ import { describe, expect, it, vi } from 'vitest';
11
+
12
+ // The fragment reader hands back the data it was given.
13
+ vi.mock('react-relay', () => ({
14
+ graphql: () => null,
15
+ useFragment: (_fragment, key) => key
16
+ }));
17
+
18
+ // Each entry renders what it links to, so the markup shows the navigation.
19
+ vi.mock('../components/StoreModal/StoreModalIndexEntry.js', () => ({
20
+ default: _ref => {
21
+ let {
22
+ productId,
23
+ name,
24
+ label
25
+ } = _ref;
26
+ return /*#__PURE__*/React.createElement("li", {
27
+ "data-product": productId
28
+ }, name, " (", label, ")");
29
+ }
30
+ }));
31
+ import StoreModalIndexItem from '../components/StoreModal/StoreModalIndexItem.js';
32
+ const family = function (name, visibility) {
33
+ let totalStyles = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 4;
34
+ let isVariableFont = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
35
+ return {
36
+ id: `collection:${name}`,
37
+ name,
38
+ visibility,
39
+ collectionType: 'FAMILY',
40
+ isVariableFont,
41
+ totalStyles,
42
+ featureStyle: null,
43
+ children: []
44
+ };
45
+ };
46
+ const superfamily = (visibility, children) => ({
47
+ id: 'collection:Super',
48
+ name: 'Super',
49
+ visibility,
50
+ collectionType: 'SUPERFAMILY',
51
+ isVariableFont: false,
52
+ totalStyles: 10,
53
+ featureStyle: null,
54
+ children
55
+ });
56
+ const markup = collection => renderToStaticMarkup( /*#__PURE__*/React.createElement(StoreModalIndexItem, {
57
+ collection: collection
58
+ }));
59
+ describe('StoreModalIndexItem visibility', () => {
60
+ it('lists a visible superfamily as one entry', () => {
61
+ const html = markup(superfamily('VISIBLE', [family('Sans', 'VISIBLE')]));
62
+ expect(html).toBe('<li data-product="collection:Super">Super (10 styles)</li>');
63
+ });
64
+ it('leaves out an unlisted root family', () => {
65
+ expect(markup(family('Sans', 'UNLISTED'))).toBe('');
66
+ });
67
+ it('replaces an unlisted superfamily with its listed families, each opening the superfamily', () => {
68
+ const html = markup(superfamily('UNLISTED', [family('Sans', 'VISIBLE'), family('Serif', 'VISIBLE', 6), family('Sans Variable', 'UNLISTED', 2, true), family('Mono Variable', 'VISIBLE', 1, true)]));
69
+ expect(html).toBe('<li data-product="collection:Super">Sans (4 styles)</li>' + '<li data-product="collection:Super">Serif (6 styles)</li>' + '<li data-product="collection:Super">Mono Variable (1 variable font)</li>');
70
+ });
71
+ });
@@ -0,0 +1,150 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ // The test fonts form's family choice (FD-28): the archive endpoint takes
4
+ // `collection_ids[]` to narrow the ZIP, and the form fills it from checkboxes
5
+ // (`familySelection: 'choice'`) or from its own `collectionId`/`collectionSlug`.
6
+
7
+ import React, { act } from 'react';
8
+ import { createRoot } from 'react-dom/client';
9
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
10
+ const {
11
+ queryRef
12
+ } = vi.hoisted(() => ({
13
+ queryRef: {
14
+ data: null,
15
+ variables: null
16
+ }
17
+ }));
18
+ vi.mock('react-relay', () => ({
19
+ graphql: () => null,
20
+ commitMutation: vi.fn(),
21
+ useRelayEnvironment: () => ({}),
22
+ usePreloadedQuery: () => queryRef.data,
23
+ useLazyLoadQuery: (_query, variables) => {
24
+ queryRef.variables = variables;
25
+ return queryRef.data;
26
+ }
27
+ }));
28
+ vi.mock('../components/FontdueContextProvider/index.js', () => ({
29
+ EnsureFontdueContext: _ref => {
30
+ let {
31
+ children
32
+ } = _ref;
33
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
34
+ },
35
+ LazyQueryBoundary: _ref2 => {
36
+ let {
37
+ children
38
+ } = _ref2;
39
+ return /*#__PURE__*/React.createElement(React.Fragment, null, children);
40
+ }
41
+ }));
42
+ import TestFontsForm from '../components/TestFontsForm/index.js';
43
+ import ConfigContext, { makeConfig } from '../components/ConfigContext.js';
44
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
45
+ const families = [{
46
+ id: 'RmFtaWx5OjE=',
47
+ name: 'Alpha'
48
+ }, {
49
+ id: 'RmFtaWx5OjI=',
50
+ name: 'Beta'
51
+ }];
52
+ const viewer = function (familySelection) {
53
+ let list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : families;
54
+ return {
55
+ viewer: {
56
+ settings: {
57
+ eulaAgreementLabel: 'I agree',
58
+ newsletterOptInLabel: null,
59
+ recaptchaEnabled: false,
60
+ recaptchaSiteKey: null
61
+ },
62
+ fontdueConfig: {
63
+ testFonts: {
64
+ familySelection
65
+ }
66
+ },
67
+ testFonts: {
68
+ archiveUrl: 'https://example.test/test-fonts/archive',
69
+ families: list
70
+ }
71
+ }
72
+ };
73
+ };
74
+ let container;
75
+ let root;
76
+ beforeEach(() => {
77
+ container = document.createElement('div');
78
+ document.body.append(container);
79
+ root = createRoot(container);
80
+ });
81
+ afterEach(() => {
82
+ act(() => root.unmount());
83
+ container.remove();
84
+ });
85
+ const render = function () {
86
+ let props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
87
+ let config = arguments.length > 1 ? arguments[1] : undefined;
88
+ return act(() => root.render( /*#__PURE__*/React.createElement(ConfigContext.Provider, {
89
+ value: makeConfig(config)
90
+ }, /*#__PURE__*/React.createElement(TestFontsForm, props))));
91
+ };
92
+ const familyCheckboxes = () => [...container.querySelectorAll('.test-fonts__family input')];
93
+ const hiddenIds = () => [...container.querySelectorAll('input[type="hidden"]')].filter(input => input.name === 'collection_ids[]').map(input => input.value);
94
+ describe('TestFontsForm family choice', () => {
95
+ it('downloads everything without checkboxes by default', () => {
96
+ queryRef.data = viewer('all');
97
+ render();
98
+ expect(familyCheckboxes()).toHaveLength(0);
99
+ expect(hiddenIds()).toEqual([]);
100
+ expect(queryRef.variables).toEqual({
101
+ collectionIds: null,
102
+ collectionSlugs: null
103
+ });
104
+ });
105
+ it('lists the families when the dashboard says choice, and sends the ticked ones', () => {
106
+ queryRef.data = viewer('choice');
107
+ render();
108
+ expect(familyCheckboxes().map(box => box.value)).toEqual(families.map(f => f.id));
109
+ expect(container.textContent).toContain('Beta');
110
+ expect(hiddenIds()).toEqual([]);
111
+ act(() => familyCheckboxes()[1].click());
112
+ expect(hiddenIds()).toEqual(['RmFtaWx5OjI=']);
113
+ act(() => container.querySelector('.test-fonts__families__all input').click());
114
+ expect(hiddenIds()).toEqual(families.map(f => f.id));
115
+ });
116
+ it('lets code config and the prop override the dashboard setting', () => {
117
+ queryRef.data = viewer('choice');
118
+ render({}, {
119
+ testFonts: {
120
+ familySelection: 'all'
121
+ }
122
+ });
123
+ expect(familyCheckboxes()).toHaveLength(0);
124
+ queryRef.data = viewer('all');
125
+ render({
126
+ familySelection: 'choice'
127
+ });
128
+ expect(familyCheckboxes()).toHaveLength(2);
129
+ });
130
+ it('downloads exactly the named collections without checkboxes', () => {
131
+ queryRef.data = viewer('choice', [families[0]]);
132
+ render({
133
+ collectionSlug: 'alpha, gamma'
134
+ });
135
+ expect(queryRef.variables).toEqual({
136
+ collectionIds: null,
137
+ collectionSlugs: ['alpha', 'gamma']
138
+ });
139
+ expect(familyCheckboxes()).toHaveLength(0);
140
+ expect(hiddenIds()).toEqual(['RmFtaWx5OjE=']);
141
+ });
142
+ it('says so when the named collections have no test fonts', () => {
143
+ queryRef.data = viewer('all', []);
144
+ render({
145
+ collectionId: 'RmFtaWx5Ojk='
146
+ });
147
+ expect(container.querySelector('form')).toBeNull();
148
+ expect(container.textContent).toContain('Test fonts aren’t available for this family.');
149
+ });
150
+ });
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect, vi, afterEach } from 'vitest';
2
- import { normalizeTruncate, parseTruncateAttribute, shouldTruncate } from "../components/TypeTester/truncate.js";
2
+ import { normalizeTruncate, parseTruncateAttribute, shouldTruncate, truncateColumnStyle } from "../components/TypeTester/truncate.js";
3
3
  import { mergeConfig } from "../components/ConfigContext.js";
4
4
  describe('normalizeTruncate', () => {
5
5
  it('is off when unset or false', () => {
@@ -215,4 +215,74 @@ describe('mergeConfig with the object truncate form', () => {
215
215
  });
216
216
  expect((_merged$typeTester2 = merged.typeTester) === null || _merged$typeTester2 === void 0 ? void 0 : _merged$typeTester2.truncate).toBe(20);
217
217
  });
218
+ });
219
+
220
+ // The stylesheet carries the column-width that WebKit before Safari 26.1 needs
221
+ // to build a single column (FD-191); these pin what the inline style leaves
222
+ // for it to reach, and what it must never touch.
223
+ describe('truncateColumnStyle', () => {
224
+ it('is the bare single column with columns off', () => {
225
+ expect(truncateColumnStyle(false)).toEqual({
226
+ columnCount: 1
227
+ });
228
+ });
229
+ it('writes configured columns as they are', () => {
230
+ expect(truncateColumnStyle({
231
+ count: 3,
232
+ width: '20ch',
233
+ gap: '1em'
234
+ })).toEqual({
235
+ columnCount: 3,
236
+ columnWidth: '20ch',
237
+ columnGap: '1em'
238
+ });
239
+ expect(truncateColumnStyle({
240
+ count: 'auto',
241
+ width: '40ch',
242
+ gap: '1em'
243
+ })).toEqual({
244
+ columnCount: 'auto',
245
+ columnWidth: '40ch',
246
+ columnGap: '1em'
247
+ });
248
+ });
249
+ it('leaves the width off a configured single column with an auto width', () => {
250
+ expect(truncateColumnStyle({
251
+ count: 1,
252
+ width: 'auto',
253
+ gap: '1em'
254
+ })).toEqual({
255
+ columnCount: 1,
256
+ columnGap: '1em'
257
+ });
258
+ });
259
+
260
+ // A script-tag config is untyped JSON, so the count can arrive as a string
261
+ // and the width as an empty string.
262
+ it('reads a loosely typed single column the same way', () => {
263
+ const loose = {
264
+ count: '1',
265
+ width: '',
266
+ gap: '1em'
267
+ };
268
+ expect(truncateColumnStyle(loose)).toEqual({
269
+ columnCount: 1,
270
+ columnGap: '1em'
271
+ });
272
+ });
273
+
274
+ // With `column-count: auto` a 1px column-width would mean one column per
275
+ // pixel, so an auto count keeps its inline auto width, which the stylesheet
276
+ // cannot override.
277
+ it('keeps the auto width of an auto column count', () => {
278
+ expect(truncateColumnStyle({
279
+ count: 'auto',
280
+ width: 'auto',
281
+ gap: '1em'
282
+ })).toEqual({
283
+ columnCount: 'auto',
284
+ columnWidth: 'auto',
285
+ columnGap: '1em'
286
+ });
287
+ });
218
288
  });
@@ -28,8 +28,13 @@ interface PreviewConfig {
28
28
  revalidateEndpoint?: string;
29
29
  clientSide?: boolean;
30
30
  }
31
+ export type TestFontsFamilySelection = 'all' | 'choice';
32
+ interface TestFontsConfig {
33
+ familySelection?: TestFontsFamilySelection;
34
+ }
31
35
  export interface Config {
32
36
  form?: FormConfig;
37
+ testFonts?: TestFontsConfig;
33
38
  storeModal?: StoreModalConfig;
34
39
  typeTester?: TypeTesterConfig;
35
40
  stripe?: StripeConfig;
@@ -94,6 +99,9 @@ export declare const makeConfig: (config?: Config) => {
94
99
  form: {
95
100
  checkboxStyle: "check" | "cross";
96
101
  };
102
+ testFonts: {
103
+ familySelection: TestFontsFamilySelection | undefined;
104
+ };
97
105
  storeModal: {
98
106
  indexLayout: "styled-aa" | "styled-font-names";
99
107
  indexExcludeTags: string[] | undefined;
@@ -172,6 +180,9 @@ declare const _default: React.Context<{
172
180
  form: {
173
181
  checkboxStyle: "check" | "cross";
174
182
  };
183
+ testFonts: {
184
+ familySelection: TestFontsFamilySelection | undefined;
185
+ };
175
186
  storeModal: {
176
187
  indexLayout: "styled-aa" | "styled-font-names";
177
188
  indexExcludeTags: string[] | undefined;
@@ -88,12 +88,18 @@ export const mergeConfig = (base, override) => {
88
88
  return deepMerge(base, override);
89
89
  };
90
90
  export const makeConfig = config => {
91
- var _config$form, _config$storeModal, _config$storeModal2, _config$storeModal3, _config$storeModal4, _config$stripe, _config$tracking, _config$tracking2, _config$tracking3, _config$tracking4, _config$preview, _config$preview2, _config$preview3;
91
+ var _config$form, _config$testFonts, _config$storeModal, _config$storeModal2, _config$storeModal3, _config$storeModal4, _config$stripe, _config$tracking, _config$tracking2, _config$tracking3, _config$tracking4, _config$preview, _config$preview2, _config$preview3;
92
92
  return {
93
93
  typeTester: makeTypeTesterConfig(config === null || config === void 0 ? void 0 : config.typeTester),
94
94
  form: {
95
95
  checkboxStyle: (config === null || config === void 0 ? void 0 : (_config$form = config.form) === null || _config$form === void 0 ? void 0 : _config$form.checkboxStyle) ?? 'check'
96
96
  },
97
+ testFonts: {
98
+ // Deliberately no default: the form falls back to the dashboard setting
99
+ // it reads in its own query (the npm path never merges server config
100
+ // into `children`'s context – see ServerConfigProvider).
101
+ familySelection: config === null || config === void 0 ? void 0 : (_config$testFonts = config.testFonts) === null || _config$testFonts === void 0 ? void 0 : _config$testFonts.familySelection
102
+ },
97
103
  storeModal: {
98
104
  indexLayout: (config === null || config === void 0 ? void 0 : (_config$storeModal = config.storeModal) === null || _config$storeModal === void 0 ? void 0 : _config$storeModal.indexLayout) ?? 'styled-aa',
99
105
  indexExcludeTags: config === null || config === void 0 ? void 0 : (_config$storeModal2 = config.storeModal) === null || _config$storeModal2 === void 0 ? void 0 : _config$storeModal2.indexExcludeTags,
@@ -29,7 +29,7 @@ const StoreModalIndex = _ref => {
29
29
  let {
30
30
  prepared
31
31
  } = _ref;
32
- const queryData = usePreloadedQuery((_StoreModalIndexQuery.hash && _StoreModalIndexQuery.hash !== "cea53722e4ce3c40a68997218f7e979f" && console.error("The definition of 'StoreModalIndexQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexQuery), prepared.indexQuery);
32
+ const queryData = usePreloadedQuery((_StoreModalIndexQuery.hash && _StoreModalIndexQuery.hash !== "13fce0376ef1b098e7f1f34dfdc7dad4" && console.error("The definition of 'StoreModalIndexQuery' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexQuery), prepared.indexQuery);
33
33
 
34
34
  // The query type only carries the fragment spread, not its data type, so
35
35
  // both sides are named explicitly for `data` to be typed.
@@ -53,7 +53,13 @@ const StoreModalIndex = _ref => {
53
53
  rootRef: scrollContainer,
54
54
  rootMargin: LOAD_MORE_MARGIN
55
55
  });
56
- const packEdges = ((_queryData$viewer = queryData.viewer) === null || _queryData$viewer === void 0 ? void 0 : (_queryData$viewer$fon = _queryData$viewer.fontPacks) === null || _queryData$viewer$fon === void 0 ? void 0 : _queryData$viewer$fon.edges) ?? [];
56
+
57
+ // An unlisted pack is sold through its own Buy Button or page, not offered
58
+ // in the index; the API returns it so a site can build that page.
59
+ const packEdges = (((_queryData$viewer = queryData.viewer) === null || _queryData$viewer === void 0 ? void 0 : (_queryData$viewer$fon = _queryData$viewer.fontPacks) === null || _queryData$viewer$fon === void 0 ? void 0 : _queryData$viewer$fon.edges) ?? []).filter(edge => {
60
+ var _edge$node;
61
+ return (edge === null || edge === void 0 ? void 0 : (_edge$node = edge.node) === null || _edge$node === void 0 ? void 0 : _edge$node.visibility) !== 'UNLISTED';
62
+ });
57
63
  const edges = (data === null || data === void 0 ? void 0 : (_data$rootCollections = data.rootCollections) === null || _data$rootCollections === void 0 ? void 0 : _data$rootCollections.edges) ?? [];
58
64
  return /*#__PURE__*/React.createElement(StoreModalPageContainer, null, {
59
65
  body: /*#__PURE__*/React.createElement(React.Fragment, null, packEdges.map(edge => /*#__PURE__*/React.createElement(StoreModalIndexPackItem, {
@@ -3,5 +3,5 @@ import { StoreModalIndexItem_fontCollection$key } from '__generated__/StoreModal
3
3
  interface StoreModalIndexItem_props {
4
4
  collection: StoreModalIndexItem_fontCollection$key;
5
5
  }
6
- declare const StoreModalIndexItem: ({ collection }: StoreModalIndexItem_props) => React.JSX.Element;
6
+ declare const StoreModalIndexItem: ({ collection }: StoreModalIndexItem_props) => React.JSX.Element | null;
7
7
  export default StoreModalIndexItem;
@@ -4,29 +4,47 @@ import { graphql, useFragment } from 'react-relay';
4
4
  import StoreModalIndexEntry from './StoreModalIndexEntry.js';
5
5
  import useTotalStyles from '../useTotalStyles.js';
6
6
  import { pluralize } from '../../utils.js';
7
+ const styleLabel = (totalStyles, variableFonts) => {
8
+ const label = [];
9
+ if (totalStyles > 0) label.push(pluralize(totalStyles, 'style'));
10
+ if (variableFonts > 0) label.push(pluralize(variableFonts, 'variable font'));
11
+ return label.join(', ');
12
+ };
13
+
14
+ // One root collection on the index page. An unlisted collection isn't offered
15
+ // on its own (FD-1239), so it has no entry of its own here either: an unlisted
16
+ // family drops out, and an unlisted superfamily is replaced by its listed
17
+ // families. Those still open the superfamily's page, where selecting them
18
+ // upgrades to the superfamily.
7
19
  const StoreModalIndexItem = _ref => {
8
20
  let {
9
21
  collection
10
22
  } = _ref;
11
- const data = useFragment((_StoreModalIndexItem_fontCollection.hash && _StoreModalIndexItem_fontCollection.hash !== "8a990fad8dc50c9f00b391902e9bc482" && console.error("The definition of 'StoreModalIndexItem_fontCollection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexItem_fontCollection), collection);
23
+ const data = useFragment((_StoreModalIndexItem_fontCollection.hash && _StoreModalIndexItem_fontCollection.hash !== "46573dcfb540ddfba2e28144704bd554" && console.error("The definition of 'StoreModalIndexItem_fontCollection' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _StoreModalIndexItem_fontCollection), collection);
12
24
  const {
13
25
  totalStyles,
14
26
  variableFonts
15
27
  } = useTotalStyles({
16
28
  fontCollection: data
17
29
  });
18
- let label = [];
19
- if (totalStyles > 0) {
20
- label.push(pluralize(totalStyles, 'style'));
21
- }
22
- if (variableFonts > 0) {
23
- label.push(pluralize(variableFonts, 'variable font'));
30
+ if (data.visibility === 'UNLISTED') {
31
+ if (data.collectionType === 'FAMILY') return null;
32
+ return /*#__PURE__*/React.createElement(React.Fragment, null, (data.children ?? []).filter(family => family.visibility !== 'UNLISTED').map(family => {
33
+ const styles = family.totalStyles ?? 0;
34
+ return /*#__PURE__*/React.createElement(StoreModalIndexEntry, {
35
+ key: family.id,
36
+ productId: data.id,
37
+ name: family.name,
38
+ featureStyle: family.featureStyle,
39
+ label: family.isVariableFont ? styleLabel(0, styles) : styleLabel(styles, 0)
40
+ });
41
+ }));
24
42
  }
25
43
  return /*#__PURE__*/React.createElement(StoreModalIndexEntry, {
26
44
  productId: data.id,
27
45
  name: data.name,
28
46
  featureStyle: data.featureStyle,
29
- label: label.join(', ')
47
+ label: styleLabel(totalStyles, variableFonts)
30
48
  });
31
49
  };
32
50
  export default StoreModalIndexItem;
@@ -3,6 +3,9 @@ interface TestFontsFormElement_props {
3
3
  agreementLabel?: string;
4
4
  downloadLabel?: string;
5
5
  newsletterCheckboxChecked?: string;
6
+ collectionId?: string;
7
+ collectionSlug?: string;
8
+ familySelection?: string;
6
9
  }
7
- declare const TestFontsFormElement: (props: TestFontsFormElement_props) => React.JSX.Element;
10
+ declare const TestFontsFormElement: ({ familySelection, ...props }: TestFontsFormElement_props) => React.JSX.Element;
8
11
  export default TestFontsFormElement;
@@ -1,7 +1,15 @@
1
1
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2
2
  import React from 'react';
3
3
  import TestFontsFormFetcher from './index.js';
4
- const TestFontsFormElement = props => /*#__PURE__*/React.createElement(TestFontsFormFetcher, _extends({}, props, {
5
- newsletterCheckboxChecked: props.newsletterCheckboxChecked === 'true' ? true : false
6
- }));
4
+ const FAMILY_SELECTIONS = ['all', 'choice'];
5
+ const TestFontsFormElement = _ref => {
6
+ let {
7
+ familySelection,
8
+ ...props
9
+ } = _ref;
10
+ return /*#__PURE__*/React.createElement(TestFontsFormFetcher, _extends({}, props, {
11
+ familySelection: familySelection && FAMILY_SELECTIONS.includes(familySelection) ? familySelection : undefined,
12
+ newsletterCheckboxChecked: props.newsletterCheckboxChecked === 'true' ? true : false
13
+ }));
14
+ };
7
15
  export default TestFontsFormElement;