main
js 290 lines 8.18 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails react-core
8 */
9
10 'use strict';
11
12 const React = require('react');
13 const ReactDOMClient = require('react-dom/client');
14 const ReactDOMServer = require('react-dom/server');
15 const act = require('internal-test-utils').act;
16 const assertConsoleErrorDev =
17 require('internal-test-utils').assertConsoleErrorDev;
18
19 describe('CSSPropertyOperations', () => {
20 it('should automatically append `px` to relevant styles', () => {
21 const styles = {
22 left: 0,
23 margin: 16,
24 opacity: 0.5,
25 padding: '4px',
26 };
27 const div = <div style={styles} />;
28 const html = ReactDOMServer.renderToString(div);
29 expect(html).toContain('"left:0;margin:16px;opacity:0.5;padding:4px"');
30 });
31
32 it('should trim values', () => {
33 const styles = {
34 left: '16 ',
35 opacity: 0.5,
36 right: ' 4 ',
37 };
38 const div = <div style={styles} />;
39 const html = ReactDOMServer.renderToString(div);
40 expect(html).toContain('"left:16;opacity:0.5;right:4"');
41 });
42
43 it('should not append `px` to styles that might need a number', () => {
44 const styles = {
45 flex: 0,
46 opacity: 0.5,
47 };
48 const div = <div style={styles} />;
49 const html = ReactDOMServer.renderToString(div);
50 expect(html).toContain('"flex:0;opacity:0.5"');
51 });
52
53 it('should create vendor-prefixed markup correctly', () => {
54 const styles = {
55 msTransition: 'none',
56 MozTransition: 'none',
57 };
58 const div = <div style={styles} />;
59 const html = ReactDOMServer.renderToString(div);
60 expect(html).toContain('"-ms-transition:none;-moz-transition:none"');
61 });
62
63 it('should not hyphenate custom CSS property', () => {
64 const styles = {
65 '--someColor': '#000000',
66 };
67 const div = <div style={styles} />;
68 const html = ReactDOMServer.renderToString(div);
69 expect(html).toContain('"--someColor:#000000"');
70 });
71
72 it('should set style attribute when styles exist', async () => {
73 const styles = {
74 backgroundColor: '#000',
75 display: 'none',
76 };
77 const container = document.createElement('div');
78 const root = ReactDOMClient.createRoot(container);
79 await act(() => {
80 root.render(<div style={styles} />);
81 });
82
83 const div = container.firstChild;
84 expect(/style=".*"/.test(container.innerHTML)).toBe(true);
85 });
86
87 it('should not set style attribute when no styles exist', () => {
88 const styles = {
89 backgroundColor: null,
90 display: null,
91 };
92 const div = <div style={styles} />;
93 const html = ReactDOMServer.renderToString(div);
94 expect(/style=/.test(html)).toBe(false);
95 });
96
97 it('should warn when using hyphenated style names', async () => {
98 class Comp extends React.Component {
99 static displayName = 'Comp';
100
101 render() {
102 return <div style={{'background-color': 'crimson'}} />;
103 }
104 }
105
106 const container = document.createElement('div');
107 const root = ReactDOMClient.createRoot(container);
108 await act(() => {
109 root.render(<Comp />);
110 });
111 assertConsoleErrorDev([
112 'Unsupported style property background-color. Did you mean backgroundColor?' +
113 '\n in div (at **)' +
114 '\n in Comp (at **)',
115 ]);
116 });
117
118 it('should warn when updating hyphenated style names', async () => {
119 class Comp extends React.Component {
120 static displayName = 'Comp';
121
122 render() {
123 return <div style={this.props.style} />;
124 }
125 }
126
127 const styles = {
128 '-ms-transform': 'translate3d(0, 0, 0)',
129 '-webkit-transform': 'translate3d(0, 0, 0)',
130 };
131 const container = document.createElement('div');
132 const root = ReactDOMClient.createRoot(container);
133 await act(() => {
134 root.render(<Comp />);
135 });
136 await act(() => {
137 root.render(<Comp style={styles} />);
138 });
139 assertConsoleErrorDev([
140 'Unsupported style property -ms-transform. Did you mean msTransform?' +
141 '\n in div (at **)' +
142 '\n in Comp (at **)',
143 'Unsupported style property -webkit-transform. Did you mean WebkitTransform?' +
144 '\n in div (at **)' +
145 '\n in Comp (at **)',
146 ]);
147 });
148
149 it('warns when miscapitalizing vendored style names', async () => {
150 class Comp extends React.Component {
151 static displayName = 'Comp';
152
153 render() {
154 return (
155 <div
156 style={{
157 msTransform: 'translate3d(0, 0, 0)',
158 oTransform: 'translate3d(0, 0, 0)',
159 webkitTransform: 'translate3d(0, 0, 0)',
160 }}
161 />
162 );
163 }
164 }
165
166 const container = document.createElement('div');
167 const root = ReactDOMClient.createRoot(container);
168 await act(() => {
169 root.render(<Comp />);
170 });
171 assertConsoleErrorDev([
172 // msTransform is correct already and shouldn't warn
173 'Unsupported vendor-prefixed style property oTransform. ' +
174 'Did you mean OTransform?' +
175 '\n in div (at **)' +
176 '\n in Comp (at **)',
177 'Unsupported vendor-prefixed style property webkitTransform. ' +
178 'Did you mean WebkitTransform?' +
179 '\n in div (at **)' +
180 '\n in Comp (at **)',
181 ]);
182 });
183
184 it('should warn about style having a trailing semicolon', async () => {
185 class Comp extends React.Component {
186 static displayName = 'Comp';
187
188 render() {
189 return (
190 <div
191 style={{
192 fontFamily: 'Helvetica, arial',
193 backgroundImage: 'url(foo;bar)',
194 backgroundColor: 'blue;',
195 color: 'red; ',
196 }}
197 />
198 );
199 }
200 }
201
202 const container = document.createElement('div');
203 const root = ReactDOMClient.createRoot(container);
204 await act(() => {
205 root.render(<Comp />);
206 });
207 assertConsoleErrorDev([
208 "Style property values shouldn't contain a semicolon. " +
209 'Try "backgroundColor: blue" instead.' +
210 '\n in div (at **)' +
211 '\n in Comp (at **)',
212 "Style property values shouldn't contain a semicolon. " +
213 'Try "color: red" instead.' +
214 '\n in div (at **)' +
215 '\n in Comp (at **)',
216 ]);
217 });
218
219 it('should warn about style containing a NaN value', async () => {
220 class Comp extends React.Component {
221 static displayName = 'Comp';
222
223 render() {
224 return <div style={{fontSize: NaN}} />;
225 }
226 }
227
228 const container = document.createElement('div');
229 const root = ReactDOMClient.createRoot(container);
230 await act(() => {
231 root.render(<Comp />);
232 });
233 assertConsoleErrorDev([
234 '`NaN` is an invalid value for the `fontSize` css style property.' +
235 '\n in div (at **)' +
236 '\n in Comp (at **)',
237 ]);
238 });
239
240 it('should not warn when setting CSS custom properties', async () => {
241 class Comp extends React.Component {
242 render() {
243 return <div style={{'--foo-primary': 'red', backgroundColor: 'red'}} />;
244 }
245 }
246
247 const container = document.createElement('div');
248 const root = ReactDOMClient.createRoot(container);
249 await act(() => {
250 root.render(<Comp />);
251 });
252 });
253
254 it('should warn about style containing an Infinity value', async () => {
255 class Comp extends React.Component {
256 static displayName = 'Comp';
257
258 render() {
259 return <div style={{fontSize: 1 / 0}} />;
260 }
261 }
262
263 const container = document.createElement('div');
264 const root = ReactDOMClient.createRoot(container);
265 await act(() => {
266 root.render(<Comp />);
267 });
268 assertConsoleErrorDev([
269 '`Infinity` is an invalid value for the `fontSize` css style property.' +
270 '\n in div (at **)' +
271 '\n in Comp (at **)',
272 ]);
273 });
274
275 it('should not add units to CSS custom properties', async () => {
276 class Comp extends React.Component {
277 render() {
278 return <div style={{'--foo': '5'}} />;
279 }
280 }
281
282 const container = document.createElement('div');
283 const root = ReactDOMClient.createRoot(container);
284 await act(() => {
285 root.render(<Comp />);
286 });
287
288 expect(container.children[0].style.getPropertyValue('--foo')).toEqual('5');
289 });
290 });