chore: Change usage of ChooseOne (no final condition) (#4158)

* Change contract of Cond: no condition on default case

* Handle no children case

* Format
This commit is contained in:
Presley Pizzo
2022-09-27 15:58:25 -04:00
committed by GitHub
parent 21e6bea792
commit fedb180735
3 changed files with 36 additions and 13 deletions
@@ -11,6 +11,7 @@ export const FirstIsTrue: Story = () => (
<ChooseOne>
<Cond condition>The first one shows.</Cond>
<Cond condition={false}>The second one does not show.</Cond>
<Cond>The default does not show.</Cond>
</ChooseOne>
)
@@ -18,6 +19,7 @@ export const SecondIsTrue: Story = () => (
<ChooseOne>
<Cond condition={false}>The first one does not show.</Cond>
<Cond condition>The second one shows.</Cond>
<Cond>The default does not show.</Cond>
</ChooseOne>
)
@@ -25,12 +27,20 @@ export const AllAreTrue: Story = () => (
<ChooseOne>
<Cond condition>Only the first one shows.</Cond>
<Cond condition>The second one does not show.</Cond>
<Cond>The default does not show.</Cond>
</ChooseOne>
)
export const NoneAreTrue: Story = () => (
<ChooseOne>
<Cond condition={false}>The first one does not show.</Cond>
<Cond condition={false}>The second shows because it is the fallback.</Cond>
<Cond condition={false}>The second one does not show.</Cond>
<Cond>The default shows.</Cond>
</ChooseOne>
)
export const OneCond: Story = () => (
<ChooseOne>
<Cond>An only child renders.</Cond>
</ChooseOne>
)
+23 -10
View File
@@ -1,18 +1,17 @@
import { Children, PropsWithChildren } from "react"
export interface CondProps {
condition: boolean
condition?: boolean
}
/**
* Wrapper component that attaches a condition to a child component so that ChooseOne can
* determine which child to render. The last Cond in a ChooseOne is the fallback case; set
* its `condition` to `true` to avoid confusion.
* @param condition boolean expression indicating whether the child should be rendered
* determine which child to render. The last Cond in a ChooseOne is the fallback case and
* should not have a condition.
* @param condition boolean expression indicating whether the child should be rendered, or undefined
* @returns child. Note that Cond alone does not enforce the condition; it should be used inside ChooseOne.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const Cond = ({ children, condition }: PropsWithChildren<CondProps>): JSX.Element => {
export const Cond = ({ children }: PropsWithChildren<CondProps>): JSX.Element => {
return <>{children}</>
}
@@ -20,10 +19,24 @@ export const Cond = ({ children, condition }: PropsWithChildren<CondProps>): JSX
* Wrapper component for rendering exactly one of its children. Wrap each child in Cond to associate it
* with a condition under which it should be rendered. If no conditions are met, the final child
* will be rendered.
* @returns one of its children
* @returns one of its children, or null if there are no children
* @throws an error if its last child has a condition prop, or any non-final children do not have a condition prop
*/
export const ChooseOne = ({ children }: PropsWithChildren): JSX.Element => {
export const ChooseOne = ({ children }: PropsWithChildren): JSX.Element | null => {
const childArray = Children.toArray(children) as JSX.Element[]
const chosen = childArray.find((child) => child.props.condition)
return chosen ?? childArray[childArray.length - 1]
if (childArray.length === 0) {
return null
}
const conditionedOptions = childArray.slice(0, childArray.length - 1)
const defaultCase = childArray[childArray.length - 1]
if (defaultCase.props.condition !== undefined) {
throw new Error(
"The last Cond in a ChooseOne was given a condition prop, but it is the default case.",
)
}
if (conditionedOptions.some((cond) => cond.props.condition === undefined)) {
throw new Error("A non-final Cond in a ChooseOne does not have a condition prop.")
}
const chosen = conditionedOptions.find((child) => child.props.condition)
return chosen ?? defaultCase
}
@@ -139,7 +139,7 @@ export const TemplatesPageView: FC<React.PropsWithChildren<TemplatesPageViewProp
defaultMessage={t("errors.getTemplatesError")}
/>
</Cond>
<Cond condition>
<Cond>
<TableContainer>
<Table>
<TableHead>
@@ -173,7 +173,7 @@ export const TemplatesPageView: FC<React.PropsWithChildren<TemplatesPageViewProp
</TableCell>
</TableRow>
</Cond>
<Cond condition>
<Cond>
{props.templates?.map((template) => {
const templatePageLink = `/templates/${template.name}`
const hasIcon = template.icon && template.icon !== ""