@heartlandone/vega-react 2.92.0 → 2.94.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.
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heartlandone/vega-react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.93.0",
|
|
4
4
|
"description": "React specific wrapper for @heartlandone/vega",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"module": "./index.js",
|
|
9
9
|
"types": "./index.d.ts",
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@heartlandone/vega": "2.
|
|
11
|
+
"@heartlandone/vega": "2.93.0"
|
|
12
12
|
},
|
|
13
13
|
"peerDependencies": {
|
|
14
14
|
"react": ">=16.7.0",
|
|
@@ -24,6 +24,16 @@ export const attachProps = (node, newProps, oldProps = {}) => {
|
|
|
24
24
|
syncEvent(node, eventNameLc, newProps[name]);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
else if (
|
|
28
|
+
// Skip re-applying when the JSX value is referentially unchanged.
|
|
29
|
+
// This prevents attachProps from clobbering host-element properties
|
|
30
|
+
// mutated outside React (e.g. devtools, imperative refs) on every
|
|
31
|
+
// parent re-render. See VD-9458.
|
|
32
|
+
oldProps !== newProps &&
|
|
33
|
+
Object.prototype.hasOwnProperty.call(oldProps, name) &&
|
|
34
|
+
oldProps[name] === newProps[name]) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
27
37
|
else if (FeatureFlag.isEnabled('VEGA_REACT.PREVENT_CALL_WATCH_METHOD_BEFORE_COMPONENT_LOAD')) {
|
|
28
38
|
attachProp(node, newProps, name);
|
|
29
39
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for attachProps reference-equality guard (VD-9458).
|
|
3
|
+
*
|
|
4
|
+
* Regression: when a parent React component re-renders, attachProps used to
|
|
5
|
+
* re-assign every prop on the host element unconditionally. On Next.js the
|
|
6
|
+
* assignment is deferred via `componentOnReady().then(...)`, which means any
|
|
7
|
+
* imperative mutation a developer makes on the host element (e.g. from
|
|
8
|
+
* devtools, refs, or web-component watchers) gets clobbered by the next
|
|
9
|
+
* React render even when the JSX value did not change.
|
|
10
|
+
*
|
|
11
|
+
* The fix is a per-prop `oldProps[name] === newProps[name]` early-return.
|
|
12
|
+
*/
|
|
13
|
+
import "jest";
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Tests for attachProps reference-equality guard (VD-9458).
|
|
12
|
+
*
|
|
13
|
+
* Regression: when a parent React component re-renders, attachProps used to
|
|
14
|
+
* re-assign every prop on the host element unconditionally. On Next.js the
|
|
15
|
+
* assignment is deferred via `componentOnReady().then(...)`, which means any
|
|
16
|
+
* imperative mutation a developer makes on the host element (e.g. from
|
|
17
|
+
* devtools, refs, or web-component watchers) gets clobbered by the next
|
|
18
|
+
* React render even when the JSX value did not change.
|
|
19
|
+
*
|
|
20
|
+
* The fix is a per-prop `oldProps[name] === newProps[name]` early-return.
|
|
21
|
+
*/
|
|
22
|
+
import "jest";
|
|
23
|
+
jest.mock("@heartlandone/vega", () => ({
|
|
24
|
+
FeatureFlag: {
|
|
25
|
+
// Use the synchronous attach path so assertions are deterministic.
|
|
26
|
+
isEnabled: (flag) => flag === "VEGA_REACT.PREVENT_CALL_WATCH_METHOD_BEFORE_COMPONENT_LOAD",
|
|
27
|
+
},
|
|
28
|
+
}), { virtual: true });
|
|
29
|
+
import { attachProps } from "./attachProps";
|
|
30
|
+
describe("attachProps reference-equality guard (VD-9458)", () => {
|
|
31
|
+
let node;
|
|
32
|
+
const setSpy = jest.fn();
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
setSpy.mockClear();
|
|
35
|
+
node = document.createElement("div");
|
|
36
|
+
// Intercept every property set so we can assert which props were
|
|
37
|
+
// (re-)assigned without relying on Stencil internals.
|
|
38
|
+
Object.defineProperty(node, "vegaDropdownProps", {
|
|
39
|
+
configurable: true,
|
|
40
|
+
set: setSpy,
|
|
41
|
+
get: () => undefined,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
it("does not re-assign a prop whose value is referentially equal across renders", () => {
|
|
45
|
+
const stableProps = { allowSelectAll: false };
|
|
46
|
+
attachProps(node, { vegaDropdownProps: stableProps }, {});
|
|
47
|
+
expect(setSpy).toHaveBeenCalledTimes(1);
|
|
48
|
+
// Simulate a parent re-render that passes the same object reference.
|
|
49
|
+
attachProps(node, { vegaDropdownProps: stableProps }, { vegaDropdownProps: stableProps });
|
|
50
|
+
// The guard should short-circuit — no second assignment, so any value
|
|
51
|
+
// mutated on the host element between renders is preserved.
|
|
52
|
+
expect(setSpy).toHaveBeenCalledTimes(1);
|
|
53
|
+
});
|
|
54
|
+
it("still assigns when the prop reference changes between renders", () => {
|
|
55
|
+
const firstProps = { allowSelectAll: false };
|
|
56
|
+
const nextProps = { allowSelectAll: true };
|
|
57
|
+
attachProps(node, { vegaDropdownProps: firstProps }, {});
|
|
58
|
+
attachProps(node, { vegaDropdownProps: nextProps }, { vegaDropdownProps: firstProps });
|
|
59
|
+
expect(setSpy).toHaveBeenCalledTimes(2);
|
|
60
|
+
expect(setSpy).toHaveBeenLastCalledWith(nextProps);
|
|
61
|
+
});
|
|
62
|
+
it("assigns on the first render even when oldProps is empty", () => {
|
|
63
|
+
attachProps(node, { vegaDropdownProps: { allowSelectAll: true } }, {});
|
|
64
|
+
expect(setSpy).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
it("assigns on mount when oldProps and newProps are the same object reference", () => {
|
|
67
|
+
const mountProps = { vegaDropdownProps: { allowSelectAll: true } };
|
|
68
|
+
// createReactComponent calls attachProps(this.props, this.props) on mount.
|
|
69
|
+
attachProps(node, mountProps, mountProps);
|
|
70
|
+
// Mount should still attach properties to host element.
|
|
71
|
+
expect(setSpy).toHaveBeenCalledTimes(1);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
/**
|
|
75
|
+
* VD-9458 async path coverage.
|
|
76
|
+
*
|
|
77
|
+
* When the `VEGA_REACT.PREVENT_CALL_WATCH_METHOD_BEFORE_COMPONENT_LOAD` flag
|
|
78
|
+
* is OFF and the host element exposes `componentOnReady()`, attachProps
|
|
79
|
+
* defers the assignment via `node.componentOnReady().then(...)`. This is the
|
|
80
|
+
* exact Next.js code path where the regression was observed — React's deferred
|
|
81
|
+
* write landed in a microtask AFTER an imperative mutation, clobbering it.
|
|
82
|
+
*
|
|
83
|
+
* Re-import attachProps in isolation with the flag mocked to `false` so this
|
|
84
|
+
* suite exercises the async branch independently of the sync-path suite above.
|
|
85
|
+
*/
|
|
86
|
+
describe("attachProps reference-equality guard — async componentOnReady path (VD-9458)", () => {
|
|
87
|
+
let asyncAttachProps;
|
|
88
|
+
beforeAll(() => {
|
|
89
|
+
jest.resetModules();
|
|
90
|
+
jest.doMock("@heartlandone/vega", () => ({
|
|
91
|
+
FeatureFlag: {
|
|
92
|
+
// Force the async componentOnReady().then(...) path.
|
|
93
|
+
isEnabled: () => false,
|
|
94
|
+
},
|
|
95
|
+
}), { virtual: true });
|
|
96
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
97
|
+
asyncAttachProps = require("./attachProps").attachProps;
|
|
98
|
+
});
|
|
99
|
+
function createNodeWithOnReady(setSpy) {
|
|
100
|
+
const el = document.createElement("div");
|
|
101
|
+
Object.defineProperty(el, "vegaDropdownProps", {
|
|
102
|
+
configurable: true,
|
|
103
|
+
set: setSpy,
|
|
104
|
+
get: () => undefined,
|
|
105
|
+
});
|
|
106
|
+
el.componentOnReady =
|
|
107
|
+
() => Promise.resolve(el);
|
|
108
|
+
return el;
|
|
109
|
+
}
|
|
110
|
+
it("does not schedule a deferred re-assignment when the prop reference is stable across renders", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
111
|
+
const setSpy = jest.fn();
|
|
112
|
+
const node = createNodeWithOnReady(setSpy);
|
|
113
|
+
const stableProps = { allowSelectAll: false };
|
|
114
|
+
asyncAttachProps(node, { vegaDropdownProps: stableProps }, {});
|
|
115
|
+
asyncAttachProps(node, { vegaDropdownProps: stableProps }, { vegaDropdownProps: stableProps });
|
|
116
|
+
// Flush any pending componentOnReady().then(...) microtasks.
|
|
117
|
+
yield Promise.resolve();
|
|
118
|
+
yield Promise.resolve();
|
|
119
|
+
// First call must still attach (mount), second call must be skipped by
|
|
120
|
+
// the guard — otherwise the deferred write would clobber any imperative
|
|
121
|
+
// mutation made between renders (the original VD-9458 Next.js bug).
|
|
122
|
+
expect(setSpy).toHaveBeenCalledTimes(1);
|
|
123
|
+
}));
|
|
124
|
+
it("still schedules a deferred re-assignment when the prop reference changes between renders", () => __awaiter(void 0, void 0, void 0, function* () {
|
|
125
|
+
const setSpy = jest.fn();
|
|
126
|
+
const node = createNodeWithOnReady(setSpy);
|
|
127
|
+
const firstProps = { allowSelectAll: false };
|
|
128
|
+
const nextProps = { allowSelectAll: true };
|
|
129
|
+
asyncAttachProps(node, { vegaDropdownProps: firstProps }, {});
|
|
130
|
+
asyncAttachProps(node, { vegaDropdownProps: nextProps }, { vegaDropdownProps: firstProps });
|
|
131
|
+
yield Promise.resolve();
|
|
132
|
+
yield Promise.resolve();
|
|
133
|
+
expect(setSpy).toHaveBeenCalledTimes(2);
|
|
134
|
+
expect(setSpy).toHaveBeenLastCalledWith(nextProps);
|
|
135
|
+
}));
|
|
136
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heartlandone/vega-react",
|
|
3
3
|
"sideEffects": false,
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.94.0",
|
|
5
5
|
"description": "React specific wrapper for @heartlandone/vega",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"scripts": {
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"typescript": "^4.0.0"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@heartlandone/vega": "2.
|
|
47
|
+
"@heartlandone/vega": "2.94.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"react": ">=16.7.0",
|