diff --git a/src/linkClass.js b/src/linkClass.js
index a9883dd..cef60e8 100644
--- a/src/linkClass.js
+++ b/src/linkClass.js
@@ -21,6 +21,19 @@ const mapChildrenWithoutKeyPrefix = (children: ReactElement, mapper: Function, c
return result;
};
+const linkArray = (array: Array, styles: Object, configuration: Object) => {
+ return _.map(array, (value) => {
+ if (React.isValidElement(value)) {
+ // eslint-disable-next-line no-use-before-define
+ return linkElement(React.Children.only(value), styles, configuration);
+ } else if (_.isArray(value)) {
+ return linkArray(value, styles, configuration);
+ }
+
+ return value;
+ });
+};
+
const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => {
let appendClassName;
let elementIsFrozen;
@@ -37,19 +50,37 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec
}
const styleNames = parseStyleName(elementShallowCopy.props.styleName || '', configuration.allowMultiple);
+ const {children, ...restProps} = elementShallowCopy.props;
- if (React.isValidElement(elementShallowCopy.props.children)) {
- elementShallowCopy.props.children = linkElement(React.Children.only(elementShallowCopy.props.children), styles, configuration);
- } else if (_.isArray(elementShallowCopy.props.children) || isIterable(elementShallowCopy.props.children)) {
- elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(elementShallowCopy.props.children, (node) => {
+ if (React.isValidElement(children)) {
+ elementShallowCopy.props.children = linkElement(React.Children.only(children), styles, configuration);
+ } else if (_.isArray(children) || isIterable(children)) {
+ elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(children, (node) => {
if (React.isValidElement(node)) {
- return linkElement(node, styles, configuration);
+ // eslint-disable-next-line no-use-before-define
+ return linkElement(React.Children.only(node), styles, configuration);
} else {
return node;
}
});
}
+ _.forEach(restProps, (propValue, propName) => {
+ if (React.isValidElement(propValue)) {
+ elementShallowCopy.props[propName] = linkElement(React.Children.only(propValue), styles, configuration);
+ } else if (_.isArray(propValue)) {
+ elementShallowCopy.props[propName] = _.map(propValue, (node) => {
+ if (React.isValidElement(node)) {
+ return linkElement(React.Children.only(node), styles, configuration);
+ } else if (_.isArray(node)) {
+ return linkArray(node, styles, configuration);
+ }
+
+ return node;
+ });
+ }
+ });
+
if (styleNames.length) {
appendClassName = generateAppendClassName(styles, styleNames, configuration.errorWhenNotFound);
diff --git a/tests/linkClass.js b/tests/linkClass.js
index 62fbc83..0e3cd06 100644
--- a/tests/linkClass.js
+++ b/tests/linkClass.js
@@ -18,6 +18,10 @@ describe('linkClass', () => {
expect(linkClass(
)).to.deep.equal();
});
+ it('does not affect element properties with a single element child in non-`children` prop', () => {
+ expect(linkClass(} />)).to.deep.equal(} />);
+ });
+
it('does not affect element properties with a single text child', () => {
expect(linkClass(test
)).to.deep.equal(test
);
});
@@ -81,6 +85,26 @@ describe('linkClass', () => {
expect(subject.props.children.props.className).to.equal('foo-1');
});
});
+ context('when a descendant element in non-`children` prop has styleName', () => {
+ it('assigns a generated className', () => {
+ let subject;
+
+ subject = }
+ els={[, []]}
+ />;
+
+ subject = linkClass(subject, {
+ bar: 'bar-1',
+ baz: 'baz-1',
+ foo: 'foo-1'
+ });
+
+ expect(subject.props.el.props.className).to.equal('foo-1');
+ expect(subject.props.els[0].props.className).to.equal('bar-1');
+ expect(subject.props.els[1][0].props.className).to.equal('baz-1');
+ });
+ });
context('when multiple descendant elements have styleName', () => {
it('assigns a generated className', () => {
let subject;
@@ -139,6 +163,32 @@ describe('linkClass', () => {
expect(subject.props.children[1].props.className).to.equal('bar-1');
});
});
+ context('when non-`children` prop is an iterable', () => {
+ it('it is left untouched', () => {
+ let subject;
+
+ const iterable = {
+ 0: ,
+ 1: ,
+ length: 2,
+
+ // eslint-disable-next-line no-use-extend-native/no-use-extend-native
+ [Symbol.iterator]: Array.prototype[Symbol.iterator]
+ };
+
+ subject = ;
+
+ subject = linkClass(subject, {
+ bar: 'bar-1',
+ foo: 'foo-1'
+ });
+
+ expect(subject.props.els[0].props.styleName).to.equal('foo');
+ expect(subject.props.els[1].props.styleName).to.equal('bar');
+ expect(subject.props.els[0].props).not.to.have.property('className');
+ expect(subject.props.els[1].props).not.to.have.property('className');
+ });
+ });
context('when ReactElement does not have an existing className', () => {
it('uses the generated class name to set the className property', () => {
let subject;
@@ -277,24 +327,35 @@ describe('linkClass', () => {
it('deletes styleName property from the target element (deep)', () => {
let subject;
- subject =
+ subject =
}
+ els={[
, [
]]}
+ styleName='foo'
+ >
;
subject = linkClass(subject, {
bar: 'bar-1',
+ baz: 'baz-1',
foo: 'foo-1'
});
expect(subject.props.children[0].props.className).to.deep.equal('bar-1');
expect(subject.props.children[0].props).not.to.have.property('styleName');
+ expect(subject.props.el.props.className).to.deep.equal('baz-1');
+ expect(subject.props.el.props).not.to.have.property('styleName');
+ expect(subject.props.els[0].props.className).to.deep.equal('foo-1');
+ expect(subject.props.els[0].props).not.to.have.property('styleName');
+ expect(subject.props.els[1][0].props.className).to.deep.equal('bar-1');
+ expect(subject.props.els[1][0].props).not.to.have.property('styleName');
});
it('does not change defined keys of children if there are multiple children', () => {
let subject;
- subject = ;
@@ -303,5 +364,7 @@ describe('linkClass', () => {
expect(subject.props.children[0].key).to.equal('foo');
expect(subject.props.children[1].key).to.equal('bar');
+ expect(subject.props.els[0].key).to.equal('foo');
+ expect(subject.props.els[1].key).to.equal('bar');
});
});