Skip to content

Commit b51b1f3

Browse files
le0nikgajus
authored andcommitted
feat: add support for non-children props (#243)
1 parent 407cdb4 commit b51b1f3

File tree

2 files changed

+101
-7
lines changed

2 files changed

+101
-7
lines changed

src/linkClass.js

+36-5
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ const mapChildrenWithoutKeyPrefix = (children: ReactElement, mapper: Function, c
2121
return result;
2222
};
2323

24+
const linkArray = (array: Array, styles: Object, configuration: Object) => {
25+
return _.map(array, (value) => {
26+
if (React.isValidElement(value)) {
27+
// eslint-disable-next-line no-use-before-define
28+
return linkElement(React.Children.only(value), styles, configuration);
29+
} else if (_.isArray(value)) {
30+
return linkArray(value, styles, configuration);
31+
}
32+
33+
return value;
34+
});
35+
};
36+
2437
const linkElement = (element: ReactElement, styles: Object, configuration: Object): ReactElement => {
2538
let appendClassName;
2639
let elementIsFrozen;
@@ -37,19 +50,37 @@ const linkElement = (element: ReactElement, styles: Object, configuration: Objec
3750
}
3851

3952
const styleNames = parseStyleName(elementShallowCopy.props.styleName || '', configuration.allowMultiple);
53+
const {children, ...restProps} = elementShallowCopy.props;
4054

41-
if (React.isValidElement(elementShallowCopy.props.children)) {
42-
elementShallowCopy.props.children = linkElement(React.Children.only(elementShallowCopy.props.children), styles, configuration);
43-
} else if (_.isArray(elementShallowCopy.props.children) || isIterable(elementShallowCopy.props.children)) {
44-
elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(elementShallowCopy.props.children, (node) => {
55+
if (React.isValidElement(children)) {
56+
elementShallowCopy.props.children = linkElement(React.Children.only(children), styles, configuration);
57+
} else if (_.isArray(children) || isIterable(children)) {
58+
elementShallowCopy.props.children = mapChildrenWithoutKeyPrefix(children, (node) => {
4559
if (React.isValidElement(node)) {
46-
return linkElement(node, styles, configuration);
60+
// eslint-disable-next-line no-use-before-define
61+
return linkElement(React.Children.only(node), styles, configuration);
4762
} else {
4863
return node;
4964
}
5065
});
5166
}
5267

68+
_.forEach(restProps, (propValue, propName) => {
69+
if (React.isValidElement(propValue)) {
70+
elementShallowCopy.props[propName] = linkElement(React.Children.only(propValue), styles, configuration);
71+
} else if (_.isArray(propValue)) {
72+
elementShallowCopy.props[propName] = _.map(propValue, (node) => {
73+
if (React.isValidElement(node)) {
74+
return linkElement(React.Children.only(node), styles, configuration);
75+
} else if (_.isArray(node)) {
76+
return linkArray(node, styles, configuration);
77+
}
78+
79+
return node;
80+
});
81+
}
82+
});
83+
5384
if (styleNames.length) {
5485
appendClassName = generateAppendClassName(styles, styleNames, configuration.errorWhenNotFound);
5586

tests/linkClass.js

+65-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ describe('linkClass', () => {
1818
expect(linkClass(<div><p /></div>)).to.deep.equal(<div><p /></div>);
1919
});
2020

21+
it('does not affect element properties with a single element child in non-`children` prop', () => {
22+
expect(linkClass(<div el={<p />} />)).to.deep.equal(<div el={<p />} />);
23+
});
24+
2125
it('does not affect element properties with a single text child', () => {
2226
expect(linkClass(<div>test</div>)).to.deep.equal(<div>test</div>);
2327
});
@@ -81,6 +85,26 @@ describe('linkClass', () => {
8185
expect(subject.props.children.props.className).to.equal('foo-1');
8286
});
8387
});
88+
context('when a descendant element in non-`children` prop has styleName', () => {
89+
it('assigns a generated className', () => {
90+
let subject;
91+
92+
subject = <div
93+
el={<p styleName='foo' />}
94+
els={[<p key='bar' styleName='bar' />, [<p key='baz' styleName='baz' />]]}
95+
/>;
96+
97+
subject = linkClass(subject, {
98+
bar: 'bar-1',
99+
baz: 'baz-1',
100+
foo: 'foo-1'
101+
});
102+
103+
expect(subject.props.el.props.className).to.equal('foo-1');
104+
expect(subject.props.els[0].props.className).to.equal('bar-1');
105+
expect(subject.props.els[1][0].props.className).to.equal('baz-1');
106+
});
107+
});
84108
context('when multiple descendant elements have styleName', () => {
85109
it('assigns a generated className', () => {
86110
let subject;
@@ -139,6 +163,32 @@ describe('linkClass', () => {
139163
expect(subject.props.children[1].props.className).to.equal('bar-1');
140164
});
141165
});
166+
context('when non-`children` prop is an iterable', () => {
167+
it('it is left untouched', () => {
168+
let subject;
169+
170+
const iterable = {
171+
0: <p key='1' styleName='foo' />,
172+
1: <p key='2' styleName='bar' />,
173+
length: 2,
174+
175+
// eslint-disable-next-line no-use-extend-native/no-use-extend-native
176+
[Symbol.iterator]: Array.prototype[Symbol.iterator]
177+
};
178+
179+
subject = <div els={iterable} />;
180+
181+
subject = linkClass(subject, {
182+
bar: 'bar-1',
183+
foo: 'foo-1'
184+
});
185+
186+
expect(subject.props.els[0].props.styleName).to.equal('foo');
187+
expect(subject.props.els[1].props.styleName).to.equal('bar');
188+
expect(subject.props.els[0].props).not.to.have.property('className');
189+
expect(subject.props.els[1].props).not.to.have.property('className');
190+
});
191+
});
142192
context('when ReactElement does not have an existing className', () => {
143193
it('uses the generated class name to set the className property', () => {
144194
let subject;
@@ -277,24 +327,35 @@ describe('linkClass', () => {
277327
it('deletes styleName property from the target element (deep)', () => {
278328
let subject;
279329

280-
subject = <div styleName='foo'>
330+
subject = <div
331+
el={<span styleName='baz' />}
332+
els={[<span key='foo' styleName='foo' />, [<span key='bar' styleName='bar' />]]}
333+
styleName='foo'
334+
>
281335
<div styleName='bar' />
282336
<div styleName='bar' />
283337
</div>;
284338

285339
subject = linkClass(subject, {
286340
bar: 'bar-1',
341+
baz: 'baz-1',
287342
foo: 'foo-1'
288343
});
289344

290345
expect(subject.props.children[0].props.className).to.deep.equal('bar-1');
291346
expect(subject.props.children[0].props).not.to.have.property('styleName');
347+
expect(subject.props.el.props.className).to.deep.equal('baz-1');
348+
expect(subject.props.el.props).not.to.have.property('styleName');
349+
expect(subject.props.els[0].props.className).to.deep.equal('foo-1');
350+
expect(subject.props.els[0].props).not.to.have.property('styleName');
351+
expect(subject.props.els[1][0].props.className).to.deep.equal('bar-1');
352+
expect(subject.props.els[1][0].props).not.to.have.property('styleName');
292353
});
293354

294355
it('does not change defined keys of children if there are multiple children', () => {
295356
let subject;
296357

297-
subject = <div>
358+
subject = <div els={[<span key='foo' />, <span key='bar' />]}>
298359
<span key='foo' />
299360
<span key='bar' />
300361
</div>;
@@ -303,5 +364,7 @@ describe('linkClass', () => {
303364

304365
expect(subject.props.children[0].key).to.equal('foo');
305366
expect(subject.props.children[1].key).to.equal('bar');
367+
expect(subject.props.els[0].key).to.equal('foo');
368+
expect(subject.props.els[1].key).to.equal('bar');
306369
});
307370
});

0 commit comments

Comments
 (0)