Skip to content

Commit

Permalink
fix: React 16 issues (fixes #269, #200, #259)
Browse files Browse the repository at this point in the history
* Check if both element and props are frozen or not extensible (#200)

Fixes #200

* Only check extensiblity of props

Also run preventExtensions on it after modifications

* Check if just the props are frozen (#266)

* Recursively map element arrays

Fixes #259

* Add tests

For element arrays and frozen elements/props
  • Loading branch information
EmmaSimon authored and gajus committed Mar 21, 2018
1 parent 1952fb5 commit 76a760f
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 5 deletions.
23 changes: 19 additions & 4 deletions src/linkClass.js
Expand Up @@ -22,17 +22,26 @@ const linkArray = (array: Array, styles: Object, configuration: Object) => {

const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => {
let appendClassName;
let elementIsFrozen;
let elementShallowCopy;

elementShallowCopy = element;

if (Object.isFrozen && Object.isFrozen(elementShallowCopy)) {
elementIsFrozen = true;
if (Array.isArray(elementShallowCopy)) {
return elementShallowCopy.map((arrayElement) => {
return linkElement(arrayElement, styles, configuration);
});
}

// https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131
const elementIsFrozen = Object.isFrozen && Object.isFrozen(elementShallowCopy);
const propsFrozen = Object.isFrozen && Object.isFrozen(elementShallowCopy.props);
const propsNotExtensible = Object.isExtensible && !Object.isExtensible(elementShallowCopy.props);

if (elementIsFrozen) {
// https://github.com/facebook/react/blob/v0.13.3/src/classic/element/ReactElement.js#L131
elementShallowCopy = objectUnfreeze(elementShallowCopy);
elementShallowCopy.props = objectUnfreeze(elementShallowCopy.props);
} else if (propsFrozen || propsNotExtensible) {
elementShallowCopy.props = objectUnfreeze(elementShallowCopy.props);
}

const styleNames = parseStyleName(elementShallowCopy.props.styleName || '', configuration.allowMultiple);
Expand Down Expand Up @@ -76,6 +85,12 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec
if (elementIsFrozen) {
Object.freeze(elementShallowCopy.props);
Object.freeze(elementShallowCopy);
} else if (propsFrozen) {
Object.freeze(elementShallowCopy.props);
}

if (propsNotExtensible) {
Object.preventExtensions(elementShallowCopy.props);
}

return elementShallowCopy;
Expand Down
72 changes: 71 additions & 1 deletion tests/linkClass.js
@@ -1,4 +1,4 @@
/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console */
/* eslint-disable max-nested-callbacks, react/prefer-stateless-function, class-methods-use-this, no-console, no-unused-expressions */

import chai, {
expect
Expand Down Expand Up @@ -239,6 +239,76 @@ describe('linkClass', () => {
});
});

context('can\'t write to properties', () => {
context('when the element is frozen', () => {
it('adds className but is still frozen', () => {
let subject;

subject = <div styleName='foo' />;

Object.freeze(subject);
subject = linkClass(subject, {
foo: 'foo-1'
});

expect(subject).to.be.frozen;
expect(subject.props.className).to.equal('foo-1');
});
});
context('when the element\'s props are frozen', () => {
it('adds className and only props are still frozen', () => {
let subject;

subject = <div styleName='foo' />;

Object.freeze(subject.props);
subject = linkClass(subject, {
foo: 'foo-1'
});

expect(subject.props).to.be.frozen;
expect(subject.props.className).to.equal('foo-1');
});
});
context('when the element\'s props are not extensible', () => {
it('adds className and props are still not extensible', () => {
let subject;

subject = <div styleName='foo' />;

Object.preventExtensions(subject.props);
subject = linkClass(subject, {
foo: 'foo-1'
});

expect(subject.props).to.not.be.extensible;
expect(subject.props.className).to.equal('foo-1');
});
});
});

context('when element is an array', () => {
it('handles each element individually', () => {
let subject;

subject = [
<div key={1} styleName='foo' />,
<div key={2}>
<p styleName='bar' />
</div>
];

subject = linkClass(subject, {
bar: 'bar-1',
foo: 'foo-1'
});

expect(subject).to.be.an('array');
expect(subject[0].props.className).to.equal('foo-1');
expect(subject[1].props.children.props.className).to.equal('bar-1');
});
});

describe('options.allowMultiple', () => {
context('when multiple module names are used', () => {
context('when false', () => {
Expand Down

0 comments on commit 76a760f

Please sign in to comment.