Last active
April 14, 2021 07:34
-
-
Save coryhouse/f972ade3ce0ed2215a8211487ebdb751 to your computer and use it in GitHub Desktop.
Only allow certain child element types in React component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Suspense } from "react"; | |
import "./InlineAlert.scss"; | |
import cx from "classnames"; | |
import * as ReactIs from "react-is"; | |
const allowedChildren = ["string", "span", "em", "b", "i", "strong"]; | |
type InlineAlertProps = { | |
/** Message to display */ | |
children: React.ReactNode; | |
}; | |
export default function InlineAlert(props: InlineAlertProps) { | |
function isSupportedElement(child: React.ReactElement) { | |
if (ReactIs.isFragment(child)) return true; | |
return allowedChildren.some((c) => c === child.type); | |
} | |
// Only certain child elements are accepted. Recursively check child elements to assure all elements are supported. | |
function validateChildren(children: React.ReactNode): any { | |
return React.Children.map(children, (child) => { | |
if (!React.isValidElement(child)) return child; | |
let elementChild: React.ReactElement = child; | |
if (child.props.children) validateChildren(elementChild.props.children); | |
if (!isSupportedElement(elementChild)) { | |
throw new Error( | |
`Children of type ${ | |
child.type | |
} aren't permitted. Only the following child elements are allowed in Inline Alert: ${allowedChildren.join( | |
", " | |
)}` | |
); | |
} | |
return elementChild; | |
}); | |
} | |
validateChildren(props.children); | |
return ( | |
<div role="alert"> | |
{props.children} | |
</div> | |
); | |
} | |
// Associated tests: | |
import React from "react"; | |
import InlineAlert from "../components/InlineAlert"; | |
import { render } from "@testing-library/react"; | |
describe("InlineAlert", () => { | |
// These settings avoid adding noise to the console when expected errors below occur. | |
const spiedError = jest.spyOn(console, "error"); | |
beforeEach(() => { | |
spiedError.mockImplementation(() => {}); | |
}); | |
afterEach(() => { | |
spiedError.mockRestore(); | |
}); | |
it("should support using allowed elements", () => { | |
render( | |
<InlineAlert type="info"> | |
<span>test</span> | |
</InlineAlert> | |
); | |
}); | |
it("should throw an error when passed an unsupported root element", () => { | |
expect(() => | |
render( | |
<InlineAlert type="info"> | |
<div>This root div should fail</div> | |
</InlineAlert> | |
) | |
).toThrowError( | |
"Children of type div aren't permitted. Only the following child elements are allowed in Inline Alert: string, span, em, b, i, strong" | |
); | |
}); | |
it("should throw an error when passed unsupported nested elements", () => { | |
expect(() => | |
render( | |
<InlineAlert type="info"> | |
<span> | |
<p>This nested p should throw since p tags aren't allowed</p> | |
</span> | |
</InlineAlert> | |
) | |
).toThrow( | |
"Children of type p aren't permitted. Only the following child elements are allowed in Inline Alert: string, span, em, b, i, strong" | |
); | |
}); | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment