fontdue-js 3.2.5 → 3.3.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 (40) hide show
  1. package/CHANGELOG.md +23 -15
  2. package/dist/__generated__/CharacterViewerIDQuery.graphql.d.ts +1 -1
  3. package/dist/__generated__/CharacterViewerIDQuery.graphql.js +33 -3
  4. package/dist/__generated__/CharacterViewerSlugQuery.graphql.d.ts +1 -1
  5. package/dist/__generated__/CharacterViewerSlugQuery.graphql.js +33 -3
  6. package/dist/__generated__/CharacterViewerStyleRefetchQuery.graphql.d.ts +1 -1
  7. package/dist/__generated__/CharacterViewerStyleRefetchQuery.graphql.js +34 -4
  8. package/dist/__generated__/CharacterViewer_style.graphql.d.ts +2 -2
  9. package/dist/__generated__/CharacterViewer_style.graphql.js +6 -2
  10. package/dist/__generated__/FeatureTesterStandaloneQuery.graphql.d.ts +1 -1
  11. package/dist/__generated__/FeatureTesterStandaloneQuery.graphql.js +15 -20
  12. package/dist/__generated__/FeatureTestersIdQuery.graphql.d.ts +1 -1
  13. package/dist/__generated__/FeatureTestersIdQuery.graphql.js +15 -20
  14. package/dist/__generated__/FeatureTestersSlugQuery.graphql.d.ts +1 -1
  15. package/dist/__generated__/FeatureTestersSlugQuery.graphql.js +15 -20
  16. package/dist/__generated__/TypeTesterStandaloneChangedStylesQuery.graphql.d.ts +1 -1
  17. package/dist/__generated__/TypeTesterStandaloneChangedStylesQuery.graphql.js +14 -20
  18. package/dist/__generated__/TypeTesterStandaloneQuery.graphql.d.ts +1 -1
  19. package/dist/__generated__/TypeTesterStandaloneQuery.graphql.js +14 -20
  20. package/dist/__generated__/TypeTestersChangedStylesQuery.graphql.d.ts +1 -1
  21. package/dist/__generated__/TypeTestersChangedStylesQuery.graphql.js +14 -20
  22. package/dist/__generated__/TypeTestersIDQuery.graphql.d.ts +1 -1
  23. package/dist/__generated__/TypeTestersIDQuery.graphql.js +17 -23
  24. package/dist/__generated__/TypeTestersRefetchQuery.graphql.d.ts +1 -1
  25. package/dist/__generated__/TypeTestersRefetchQuery.graphql.js +17 -23
  26. package/dist/__generated__/TypeTestersSlugQuery.graphql.d.ts +1 -1
  27. package/dist/__generated__/TypeTestersSlugQuery.graphql.js +17 -23
  28. package/dist/__generated__/useFeaturesData_fontStyle.graphql.d.ts +4 -4
  29. package/dist/__generated__/useFeaturesData_fontStyle.graphql.js +6 -6
  30. package/dist/__tests__/trackingRouteChanges.test.js +110 -0
  31. package/dist/__tests__/typeTestersServerEntry.test.js +98 -0
  32. package/dist/components/CharacterViewer/index.js +17 -4
  33. package/dist/components/Tracking/index.js +10 -18
  34. package/dist/components/Tracking/routeChanges.d.ts +19 -0
  35. package/dist/components/Tracking/routeChanges.js +62 -0
  36. package/dist/components/TypeTester/useFeaturesData.d.ts +9 -9
  37. package/dist/components/TypeTester/useFeaturesData.js +13 -23
  38. package/dist/components/TypeTesters/index.server.js +14 -2
  39. package/dist/relay/environment.js +1 -1
  40. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { useContext, useEffect } from 'react';
4
4
  import ConfigContext from '../ConfigContext.js';
5
5
  import { useFontdueUrl } from '../UrlContext.js';
6
6
  import { hasConsent, onConsent, setClientAnonymousId, setEntryAttribution } from '../ConsentBanner/consent.js';
7
+ import { trackRouteChanges } from './routeChanges.js';
7
8
  function getCampaignDataFromUrl() {
8
9
  const urlParams = new URLSearchParams(window.location.search);
9
10
  const campaign = {};
@@ -155,26 +156,17 @@ const Tracking = () => {
155
156
  }
156
157
 
157
158
  // Track client-side route changes
158
- const originalPushState = history.pushState.bind(history);
159
- const originalReplaceState = history.replaceState.bind(history);
160
- const handleRouteChange = () => {
161
- // Defer slightly to let document.title update
162
- setTimeout(() => sendPageView(url), 0);
163
- };
164
- history.pushState = function () {
165
- originalPushState(...arguments);
166
- handleRouteChange();
167
- };
168
- history.replaceState = function () {
169
- originalReplaceState(...arguments);
170
- handleRouteChange();
171
- };
172
- window.addEventListener('popstate', handleRouteChange);
159
+ const untrackRouteChanges = trackRouteChanges({
160
+ history,
161
+ window,
162
+ currentUrl: () => window.location.href,
163
+ onNavigate: () => {
164
+ sendPageView(url);
165
+ }
166
+ });
173
167
  return () => {
174
168
  var _consentCleanup;
175
- history.pushState = originalPushState;
176
- history.replaceState = originalReplaceState;
177
- window.removeEventListener('popstate', handleRouteChange);
169
+ untrackRouteChanges();
178
170
  (_consentCleanup = consentCleanup) === null || _consentCleanup === void 0 ? void 0 : _consentCleanup();
179
171
  };
180
172
  }, []);
@@ -0,0 +1,19 @@
1
+ type HistoryLike = Pick<History, 'pushState' | 'replaceState'>;
2
+ type WindowLike = Pick<Window, 'addEventListener' | 'removeEventListener'>;
3
+ export type RouteChangeOptions = {
4
+ history: HistoryLike;
5
+ window: WindowLike;
6
+ /** The URL as it stands right now, e.g. `() => window.location.href`. */
7
+ currentUrl: () => string;
8
+ /** Called once per navigation to a URL different from the previous one. */
9
+ onNavigate: () => void;
10
+ /** Overridable so tests can run the check synchronously. */
11
+ defer?: (callback: () => void) => void;
12
+ };
13
+ /**
14
+ * Wrap `pushState`/`replaceState` and call `onNavigate` when the URL changes.
15
+ *
16
+ * Returns a cleanup function that restores the original History methods.
17
+ */
18
+ export declare function trackRouteChanges({ history, window: windowLike, currentUrl, onNavigate, defer, }: RouteChangeOptions): () => void;
19
+ export {};
@@ -0,0 +1,62 @@
1
+ // Client-side route-change detection for the Tracking component.
2
+ //
3
+ // There is no navigation event a page can listen to for SPA routing, so the
4
+ // only way to notice one is to wrap the History API and watch for the URL
5
+ // changing underneath us. Kept in its own module — and taking its history,
6
+ // window and clock as arguments — so the wrapping and the de-duplication can
7
+ // be tested without a DOM.
8
+
9
+ /**
10
+ * Wrap `pushState`/`replaceState` and call `onNavigate` when the URL changes.
11
+ *
12
+ * Returns a cleanup function that restores the original History methods.
13
+ */
14
+ export function trackRouteChanges(_ref) {
15
+ let {
16
+ history,
17
+ window: windowLike,
18
+ currentUrl,
19
+ onNavigate,
20
+ defer = callback => {
21
+ setTimeout(callback, 0);
22
+ }
23
+ } = _ref;
24
+ // The caller has already reported the URL we start on, so it is the first
25
+ // thing we compare against.
26
+ let lastUrl = currentUrl();
27
+ const handleRouteChange = () => {
28
+ // Defer slightly to let document.title update
29
+ defer(() => {
30
+ const url = currentUrl();
31
+
32
+ // A history entry gets rewritten in place for all sorts of reasons that
33
+ // are not navigation: saving scroll position, stashing UI state, marking
34
+ // how the entry was reached. Phoenix LiveView, for one, calls
35
+ // `history.replaceState(state, "", window.location.href)` 100ms after
36
+ // every scroll stop to remember the scroll offset — which billed a page
37
+ // view per scroll pause on the site of anyone running LiveView. Only a
38
+ // URL that actually differs from the last one we reported can be a new
39
+ // page view. Note this compares against the *previous* URL rather than
40
+ // every URL seen, so A → B → A is three page views, as it should be.
41
+ if (url === lastUrl) return;
42
+ lastUrl = url;
43
+ onNavigate();
44
+ });
45
+ };
46
+ const originalPushState = history.pushState.bind(history);
47
+ const originalReplaceState = history.replaceState.bind(history);
48
+ history.pushState = function () {
49
+ originalPushState(...arguments);
50
+ handleRouteChange();
51
+ };
52
+ history.replaceState = function () {
53
+ originalReplaceState(...arguments);
54
+ handleRouteChange();
55
+ };
56
+ windowLike.addEventListener('popstate', handleRouteChange);
57
+ return () => {
58
+ history.pushState = originalPushState;
59
+ history.replaceState = originalReplaceState;
60
+ windowLike.removeEventListener('popstate', handleRouteChange);
61
+ };
62
+ }
@@ -1,18 +1,18 @@
1
1
  import { useFeaturesData_fontStyle$key } from '__generated__/useFeaturesData_fontStyle.graphql.js';
2
2
  interface useFeatures_props {
3
- fontStyle: useFeaturesData_fontStyle$key;
3
+ fontStyle: useFeaturesData_fontStyle$key | null | undefined;
4
4
  }
5
+ export type FeatureNames = {
6
+ [feature: string]: string;
7
+ };
5
8
  declare const useFeaturesData: ({ fontStyle }: useFeatures_props) => {
6
- featureNames: {
7
- [feature: string]: string;
8
- };
9
9
  fontFeatures: {
10
- readonly stylisticSetNames: ReadonlyArray<{
11
- readonly featureName: string;
12
- readonly humanName: string;
10
+ readonly featureNames: ReadonlyArray<{
11
+ readonly name: string;
12
+ readonly tag: string;
13
13
  }>;
14
14
  readonly supportedFeatures: ReadonlyArray<string>;
15
- };
16
- " $fragmentType": "useFeaturesData_fontStyle";
15
+ } | null;
16
+ featureNames: FeatureNames;
17
17
  };
18
18
  export default useFeaturesData;
@@ -1,32 +1,22 @@
1
1
  import _useFeaturesData_fontStyle from "../../__generated__/useFeaturesData_fontStyle.graphql.js";
2
+ import { useMemo } from 'react';
2
3
  import { graphql, useFragment } from 'react-relay';
3
- import featureNameData from './features.js';
4
- const getStylisticSetName = (feature, stylisticSetNames) => stylisticSetNames.find(_ref => {
5
- let {
6
- featureName
7
- } = _ref;
8
- return featureName === feature;
9
- });
10
- const useFeaturesData = _ref2 => {
11
- var _data$fontFeatures;
4
+ const useFeaturesData = _ref => {
12
5
  let {
13
6
  fontStyle
14
- } = _ref2;
15
- const data = useFragment((_useFeaturesData_fontStyle.hash && _useFeaturesData_fontStyle.hash !== "3c48a87b1b19f52e3dda10fc11f0030a" && console.error("The definition of 'useFeaturesData_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _useFeaturesData_fontStyle), fontStyle);
16
- const featureNames = ((_data$fontFeatures = data.fontFeatures) === null || _data$fontFeatures === void 0 ? void 0 : _data$fontFeatures.supportedFeatures.reduce((acc, feature) => {
17
- let featureName = feature;
18
- if (featureNameData.hasOwnProperty(feature)) {
19
- featureName = featureNameData[feature];
20
- }
21
- const stylisticSetName = getStylisticSetName(feature, data.fontFeatures.stylisticSetNames);
22
- if (stylisticSetName) {
23
- featureName = stylisticSetName.humanName;
24
- }
25
- acc[feature] = featureName;
7
+ } = _ref;
8
+ const data = useFragment((_useFeaturesData_fontStyle.hash && _useFeaturesData_fontStyle.hash !== "6a05a092096a4a0c1dd10fc0610bbee2" && console.error("The definition of 'useFeaturesData_fontStyle' appears to have changed. Run `relay-compiler` to update the generated files to receive the expected data."), _useFeaturesData_fontStyle), fontStyle);
9
+ const fontFeatures = (data === null || data === void 0 ? void 0 : data.fontFeatures) ?? null;
10
+ const featureNames = useMemo(() => (fontFeatures === null || fontFeatures === void 0 ? void 0 : fontFeatures.featureNames.reduce((acc, _ref2) => {
11
+ let {
12
+ tag,
13
+ name
14
+ } = _ref2;
15
+ acc[tag] = name;
26
16
  return acc;
27
- }, {})) ?? [];
17
+ }, {})) ?? {}, [fontFeatures]);
28
18
  return {
29
- ...data,
19
+ fontFeatures,
30
20
  featureNames
31
21
  };
32
22
  };
@@ -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.5";
11
+ export const version = "3.3.0";
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fontdue-js",
3
- "version": "3.2.5",
3
+ "version": "3.3.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "npm run relay && run-p build-js build-css build-ts",