Warn for inline style mismatches (#10084)
I use the technique of generating a style string and comparing that against the attribute.
Sebastian Markbåge committed
Jun 30, 2017 at 18:24 UTC
e6f1d29f072b2be2e9795ffc781a965cb69f0347
5 files changed
+87
-5
scripts/fiber/tests-passing.txt
+7
@@ -1531,6 +1531,13 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1531
* should error reconnecting missing attributes
1532
* should error reconnecting added attributes
1533
* should error reconnecting different attribute values
1534
+* should error reconnecting missing style attribute
1535
+* should error reconnecting added style attribute
1536
+* should error reconnecting empty style attribute
1537
+* should error reconnecting added style values
1538
+* should error reconnecting different style values
1539
+* should reconnect number and string versions of a number
1540
+* should error reconnecting reordered style values
1541
* should error reconnecting different text
1542
* should reconnect a div with a number and string version of number
1543
* should error reconnecting different numbers
src/renderers/dom/fiber/ReactDOMFiberComponent.js
+7
-1
@@ -915,7 +915,13 @@ var ReactDOMFiberComponent = {
915
} else if (propKey === STYLE) {
916
// $FlowFixMe - Should be inferred as not undefined.
917
extraAttributeNames.delete(propKey);
918
- // TOOD: Validate style sheets.
918
+ const expectedStyle = CSSPropertyOperations.createDangerousStringForStyles(
919
+ nextProp,
920
+ );
921
+ serverValue = domElement.getAttribute('style');
922
+ if (expectedStyle !== serverValue) {
923
+ warnForPropDifference(propKey, serverValue, expectedStyle);
924
+ }
925
} else if (
926
isCustomComponentTag ||
927
DOMProperty.isCustomAttribute(propKey)
src/renderers/dom/shared/CSSPropertyOperations.js
+34
-2
@@ -207,7 +207,6 @@ var CSSPropertyOperations = {
207
serialized += dangerousStyleValue(
208
styleName,
209
styleValue,
210
- component,
210
isCustomProperty,
211
);
212
@@ -217,6 +216,40 @@ var CSSPropertyOperations = {
216
return serialized || null;
217
},
218
219
+ /**
220
+ * This creates a string that is expected to be equivalent to the style
221
+ * attribute generated by server-side rendering. It by-passes warnings and
222
+ * security checks so it's not safe to use this value for anything other than
223
+ * comparison. It is only used in DEV for SSR validation. This is duplicated
224
+ * from createMarkupForStyles because createMarkupForStyles is expected to
225
+ * move out of the client-side renderer and it would be nice to make a clean
226
+ * break.
227
+ */
228
+ createDangerousStringForStyles: function(styles) {
229
+ if (__DEV__) {
230
+ var serialized = '';
231
+ var delimiter = '';
232
+ for (var styleName in styles) {
233
+ if (!styles.hasOwnProperty(styleName)) {
234
+ continue;
235
+ }
236
+ var styleValue = styles[styleName];
237
+ if (styleValue != null) {
238
+ var isCustomProperty = styleName.indexOf('--') === 0;
239
+ serialized += delimiter + hyphenateStyleName(styleName) + ':';
240
+ serialized += dangerousStyleValue(
241
+ styleName,
242
+ styleValue,
243
+ isCustomProperty,
244
+ );
245
+
246
+ delimiter = ';';
247
+ }
248
+ }
249
+ return serialized || null;
250
+ }
251
+ },
252
+
253
/**
254
* Sets the value for multiple styles on a node. If a value is specified as
255
* '' (empty string), the corresponding style property will be unset.
@@ -240,7 +273,6 @@ var CSSPropertyOperations = {
273
var styleValue = dangerousStyleValue(
274
styleName,
275
styles[styleName],
243
- component,
276
isCustomProperty,
277
);
278
if (styleName === 'float') {
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+38
@@ -2075,6 +2075,44 @@ describe('ReactDOMServerIntegration', () => {
2075
expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
2076
});
2077
2078
+ describe('inline styles', function() {
2079
+ it('should error reconnecting missing style attribute', () =>
2080
+ expectMarkupMismatch(<div style={{width: '1px'}} />, <div />));
2081
+
2082
+ it('should error reconnecting added style attribute', () =>
2083
+ expectMarkupMismatch(<div />, <div style={{width: '1px'}} />));
2084
+
2085
+ it('should error reconnecting empty style attribute', () =>
2086
+ expectMarkupMismatch(
2087
+ <div style={{width: '1px'}} />,
2088
+ <div style={{}} />,
2089
+ ));
2090
+
2091
+ it('should error reconnecting added style values', () =>
2092
+ expectMarkupMismatch(
2093
+ <div style={{}} />,
2094
+ <div style={{width: '1px'}} />,
2095
+ ));
2096
+
2097
+ it('should error reconnecting different style values', () =>
2098
+ expectMarkupMismatch(
2099
+ <div style={{width: '1px'}} />,
2100
+ <div style={{width: '2px'}} />,
2101
+ ));
2102
+
2103
+ it('should reconnect number and string versions of a number', () =>
2104
+ expectMarkupMatch(
2105
+ <div style={{width: '1px', height: 2}} />,
2106
+ <div style={{width: 1, height: '2px'}} />,
2107
+ ));
2108
+
2109
+ it('should error reconnecting reordered style values', () =>
2110
+ expectMarkupMismatch(
2111
+ <div style={{width: '1px', fontSize: '2px'}} />,
2112
+ <div style={{fontSize: '2px', width: '1px'}} />,
2113
+ ));
2114
+ });
2115
+
2116
describe('text nodes', function() {
2117
it('should error reconnecting different text', () =>
2118
expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
src/renderers/dom/shared/dangerousStyleValue.js
+1
-2
@@ -22,10 +22,9 @@ var isUnitlessNumber = CSSProperty.isUnitlessNumber;
22
*
23
* @param {string} name CSS property name such as `topMargin`.
24
* @param {*} value CSS property value such as `10px`.
25
- * @param {ReactDOMComponent} component
25
* @return {string} Normalized style value with dimensions applied.
26
*/
28
-function dangerousStyleValue(name, value, component, isCustomProperty) {
27
+function dangerousStyleValue(name, value, isCustomProperty) {
28
// Note that we've removed escapeTextForBrowser() calls here since the
29
// whole string will be escaped when the attribute is injected into
30
// the markup. If you provide unsafe user data here they can inject