From fedb1807352d1a804a49146c61e18f981090367b Mon Sep 17 00:00:00 2001
From: Presley Pizzo <1290996+presleyp@users.noreply.github.com>
Date: Tue, 27 Sep 2022 15:58:25 -0400
Subject: [PATCH] chore: Change usage of ChooseOne (no final condition) (#4158)
* Change contract of Cond: no condition on default case
* Handle no children case
* Format
---
.../Conditionals/ChooseOne.stories.tsx | 12 ++++++-
.../src/components/Conditionals/ChooseOne.tsx | 33 +++++++++++++------
.../pages/TemplatesPage/TemplatesPageView.tsx | 4 +--
3 files changed, 36 insertions(+), 13 deletions(-)
diff --git a/site/src/components/Conditionals/ChooseOne.stories.tsx b/site/src/components/Conditionals/ChooseOne.stories.tsx
index f2fc75fd43..a6c1bf8483 100644
--- a/site/src/components/Conditionals/ChooseOne.stories.tsx
+++ b/site/src/components/Conditionals/ChooseOne.stories.tsx
@@ -11,6 +11,7 @@ export const FirstIsTrue: Story = () => (
The first one shows.
The second one does not show.
+ The default does not show.
)
@@ -18,6 +19,7 @@ export const SecondIsTrue: Story = () => (
The first one does not show.
The second one shows.
+ The default does not show.
)
@@ -25,12 +27,20 @@ export const AllAreTrue: Story = () => (
Only the first one shows.
The second one does not show.
+ The default does not show.
)
export const NoneAreTrue: Story = () => (
The first one does not show.
- The second shows because it is the fallback.
+ The second one does not show.
+ The default shows.
+
+)
+
+export const OneCond: Story = () => (
+
+ An only child renders.
)
diff --git a/site/src/components/Conditionals/ChooseOne.tsx b/site/src/components/Conditionals/ChooseOne.tsx
index 71b28d95db..4eca8130bb 100644
--- a/site/src/components/Conditionals/ChooseOne.tsx
+++ b/site/src/components/Conditionals/ChooseOne.tsx
@@ -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): JSX.Element => {
+export const Cond = ({ children }: PropsWithChildren): JSX.Element => {
return <>{children}>
}
@@ -20,10 +19,24 @@ export const Cond = ({ children, condition }: PropsWithChildren): 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
}
diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx
index 64a3518a80..af2d3e9de6 100644
--- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx
+++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx
@@ -139,7 +139,7 @@ export const TemplatesPageView: FC
-
+
@@ -173,7 +173,7 @@ export const TemplatesPageView: FC
-
+
{props.templates?.map((template) => {
const templatePageLink = `/templates/${template.name}`
const hasIcon = template.icon && template.icon !== ""