Skip to content

Commit ec1d42d

Browse files
blushingpenguinstasm
authored andcommitted
Allow Localized with no children (#230)
1 parent f0eb098 commit ec1d42d

File tree

3 files changed

+83
-12
lines changed

3 files changed

+83
-12
lines changed

fluent-react/src/localized.js

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isValidElement, cloneElement, Component, Children } from "react";
1+
import { isValidElement, cloneElement, Component } from "react";
22
import PropTypes from "prop-types";
33
import { isReactLocalization } from "./localization";
44
import VOID_ELEMENTS from "../vendor/voidElementTags";
@@ -79,8 +79,13 @@ export default class Localized extends Component {
7979

8080
render() {
8181
const { l10n, parseMarkup } = this.context;
82-
const { id, attrs, children } = this.props;
83-
const elem = Children.only(children);
82+
const { id, attrs, children: elem } = this.props;
83+
84+
// Validate that the child element isn't an array
85+
if (Array.isArray(elem)) {
86+
throw new Error("<Localized/> expected to receive a single " +
87+
"React node child");
88+
}
8489

8590
if (!l10n) {
8691
// Use the wrapped component as fallback.
@@ -101,6 +106,13 @@ export default class Localized extends Component {
101106
attrs: messageAttrs
102107
} = l10n.formatCompound(bundle, msg, args);
103108

109+
// Check if the fallback is a valid element -- if not then it's not
110+
// markup (e.g. nothing or a fallback string) so just use the
111+
// formatted message value
112+
if (!isValidElement(elem)) {
113+
return messageValue;
114+
}
115+
104116
// The default is to forbid all message attributes. If the attrs prop exists
105117
// on the Localized instance, only set message attributes which have been
106118
// explicitly allowed by the developer.
@@ -175,5 +187,5 @@ Localized.contextTypes = {
175187
};
176188

177189
Localized.propTypes = {
178-
children: PropTypes.element.isRequired,
190+
children: PropTypes.node
179191
};

fluent-react/test/localized_render_test.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,4 +262,65 @@ foo = { $arg }
262262
const { args } = format.getCall(0);
263263
assert.deepEqual(args[1], { arg: 'ARG' });
264264
});
265+
266+
test('render with a string fallback and no message returns the fallback',
267+
function() {
268+
const mcx = new FluentBundle();
269+
const l10n = new ReactLocalization([mcx]);
270+
271+
const wrapper = shallow(
272+
<Localized id="foo">
273+
String fallback
274+
</Localized>,
275+
{ context: { l10n } }
276+
);
277+
278+
assert.equal(wrapper.text(), 'String fallback');
279+
});
280+
281+
test('render with a string fallback returns the message', function() {
282+
const mcx = new FluentBundle();
283+
const l10n = new ReactLocalization([mcx]);
284+
mcx.addMessages(`
285+
foo = Test message
286+
`)
287+
288+
const wrapper = shallow(
289+
<Localized id="foo">
290+
String fallback
291+
</Localized>,
292+
{ context: { l10n } }
293+
);
294+
295+
assert.equal(wrapper.text(), 'Test message');
296+
});
297+
298+
test('render without a fallback returns the message', function() {
299+
const mcx = new FluentBundle();
300+
const l10n = new ReactLocalization([mcx]);
301+
302+
mcx.addMessages(`
303+
foo = Message
304+
`)
305+
306+
const wrapper = shallow(
307+
<Localized id="foo" />,
308+
{ context: { l10n } }
309+
);
310+
311+
assert.equal(wrapper.text(), 'Message');
312+
});
313+
314+
test('render without a fallback and no message returns nothing',
315+
function() {
316+
const mcx = new FluentBundle();
317+
const l10n = new ReactLocalization([mcx]);
318+
319+
const wrapper = shallow(
320+
<Localized id="foo" />,
321+
{ context: { l10n } }
322+
);
323+
324+
assert.equal(wrapper.text(), '');
325+
});
265326
});

fluent-react/test/localized_valid_test.js

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,11 @@ suite('Localized - validation', function() {
4545
});
4646

4747
test('without a child', function() {
48-
function render() {
49-
shallow(
50-
<Localized />,
51-
{ context: { l10n: new ReactLocalization([]) } }
52-
);
53-
}
54-
assert.throws(render, /a single React element child/);
48+
const wrapper = shallow(
49+
<Localized />,
50+
{ context: { l10n: new ReactLocalization([]) } }
51+
);
52+
assert.equal(wrapper.length, 1);
5553
});
5654

5755
test('with multiple children', function() {
@@ -64,7 +62,7 @@ suite('Localized - validation', function() {
6462
{ context: { l10n: new ReactLocalization([]) } }
6563
);
6664
}
67-
assert.throws(render, /a single React element child/);
65+
assert.throws(render, /a single React node child/);
6866
});
6967

7068
test('without id', function() {

0 commit comments

Comments
 (0)