From d65a42bbbf6cf947fe66fd13965dd891fe97f097 Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Mon, 14 May 2018 14:28:32 -0700 Subject: [PATCH 1/2] ensureElement wraps React Fragment in tag (and tests!!) --- packages/core/src/common/utils.ts | 8 ++++-- .../common/{utilsTests.ts => utilsTests.tsx} | 28 +++++++++++++++++++ packages/core/test/index.ts | 6 ++-- 3 files changed, 36 insertions(+), 6 deletions(-) rename packages/core/test/common/{utilsTests.ts => utilsTests.tsx} (84%) diff --git a/packages/core/src/common/utils.ts b/packages/core/src/common/utils.ts index 821112eef6..09908314b1 100644 --- a/packages/core/src/common/utils.ts +++ b/packages/core/src/common/utils.ts @@ -25,15 +25,17 @@ export function isFunction(value: any): value is Function { } /** - * Converts a React child to an element: non-empty strings or numbers are wrapped in ``; - * empty strings are discarded. + * Converts a React child to an element: non-empty string or number or + * `React.Fragment` (React 16.3+) is wrapped in given tag name; empty strings + * are discarded. */ export function ensureElement(child: React.ReactChild | undefined, tagName: keyof JSX.IntrinsicElements = "span") { // wrap text in a so children are always elements if (typeof child === "string") { // cull whitespace strings return child.trim().length > 0 ? React.createElement(tagName, {}, child) : undefined; - } else if (typeof child === "number") { + } else if (typeof child === "number" || typeof child.type === "symbol") { + // React.Fragment has a symbol type return React.createElement(tagName, {}, child); } else { return child; diff --git a/packages/core/test/common/utilsTests.ts b/packages/core/test/common/utilsTests.tsx similarity index 84% rename from packages/core/test/common/utilsTests.ts rename to packages/core/test/common/utilsTests.tsx index eb7780a62c..04f153837a 100644 --- a/packages/core/test/common/utilsTests.ts +++ b/packages/core/test/common/utilsTests.tsx @@ -92,6 +92,34 @@ describe("Utils", () => { // TODO: not sure how to test this. perhaps with the help of https://github.com/alexreardon/raf-stub? it.skip("throttleEvent"); + describe("ensureElement", () => { + it("wraps strings & numbers", () => { + assert.strictEqual(Utils.ensureElement("foo").type, "span"); + assert.strictEqual(Utils.ensureElement(1234).type, "span"); + }); + + it("returns undefined for whitespace strings", () => { + assert.isUndefined(Utils.ensureElement(" ")); + }); + + it("passes through JSX elements", () => { + const el =
my element
; + assert.strictEqual(Utils.ensureElement(el), el); + }); + + // React 16 only + if (React.Fragment !== undefined) { + it("wraps JSX fragments in element", () => { + const el = Utils.ensureElement( + <> + one two three + , + ); + assert.strictEqual(el.type, "span"); + }); + } + }); + describe("throttleReactEventCallback", () => { let callback: SinonSpy; let fakeEvent: any; // cast as `any` to avoid having to set every required property on the event diff --git a/packages/core/test/index.ts b/packages/core/test/index.ts index 6e0fd245eb..71ea773604 100644 --- a/packages/core/test/index.ts +++ b/packages/core/test/index.ts @@ -12,9 +12,9 @@ import "./callout/calloutTests"; import "./card/cardTests"; import "./collapse/collapseTests"; import "./collapsible-list/collapsibleListTests"; -import "./common/propsTests.ts"; -import "./common/utils/compareUtilsTests.ts"; -import "./common/utilsTests.ts"; +import "./common/propsTests"; +import "./common/utils/compareUtilsTests"; +import "./common/utilsTests"; import "./context-menu/contextMenuTests"; import "./controls/controlsTests"; import "./controls/inputGroupTests"; From 24cd9c5dcaf8202952fcd6a34642c759791c0d6f Mon Sep 17 00:00:00 2001 From: Gilad Gray Date: Mon, 14 May 2018 14:41:37 -0700 Subject: [PATCH 2/2] undefined support --- packages/core/src/common/utils.ts | 5 +++-- packages/core/test/common/utilsTests.tsx | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/core/src/common/utils.ts b/packages/core/src/common/utils.ts index 09908314b1..317d71397a 100644 --- a/packages/core/src/common/utils.ts +++ b/packages/core/src/common/utils.ts @@ -30,8 +30,9 @@ export function isFunction(value: any): value is Function { * are discarded. */ export function ensureElement(child: React.ReactChild | undefined, tagName: keyof JSX.IntrinsicElements = "span") { - // wrap text in a so children are always elements - if (typeof child === "string") { + if (child == null) { + return undefined; + } else if (typeof child === "string") { // cull whitespace strings return child.trim().length > 0 ? React.createElement(tagName, {}, child) : undefined; } else if (typeof child === "number" || typeof child.type === "symbol") { diff --git a/packages/core/test/common/utilsTests.tsx b/packages/core/test/common/utilsTests.tsx index 04f153837a..202ca24154 100644 --- a/packages/core/test/common/utilsTests.tsx +++ b/packages/core/test/common/utilsTests.tsx @@ -93,6 +93,11 @@ describe("Utils", () => { it.skip("throttleEvent"); describe("ensureElement", () => { + it("handles undefined/null", () => { + assert.isUndefined(Utils.ensureElement(undefined)); + assert.isUndefined(Utils.ensureElement(null)); + }); + it("wraps strings & numbers", () => { assert.strictEqual(Utils.ensureElement("foo").type, "span"); assert.strictEqual(Utils.ensureElement(1234).type, "span");