diff --git a/site/src/components/Conditionals/ChooseOne.stories.tsx b/site/src/components/Conditionals/ChooseOne.stories.tsx
new file mode 100644
index 0000000000..f2fc75fd43
--- /dev/null
+++ b/site/src/components/Conditionals/ChooseOne.stories.tsx
@@ -0,0 +1,36 @@
+import { Story } from "@storybook/react"
+import { ChooseOne, Cond } from "./ChooseOne"
+
+export default {
+ title: "components/Conditionals/ChooseOne",
+ component: ChooseOne,
+ subcomponents: { Cond },
+}
+
+export const FirstIsTrue: Story = () => (
+
+ The first one shows.
+ The second one does not show.
+
+)
+
+export const SecondIsTrue: Story = () => (
+
+ The first one does not show.
+ The second one shows.
+
+)
+
+export const AllAreTrue: Story = () => (
+
+ Only the first one shows.
+ The second one does not show.
+
+)
+
+export const NoneAreTrue: Story = () => (
+
+ The first one does not show.
+ The second shows because it is the fallback.
+
+)
diff --git a/site/src/components/Conditionals/ChooseOne.tsx b/site/src/components/Conditionals/ChooseOne.tsx
new file mode 100644
index 0000000000..71b28d95db
--- /dev/null
+++ b/site/src/components/Conditionals/ChooseOne.tsx
@@ -0,0 +1,29 @@
+import { Children, PropsWithChildren } from "react"
+
+export interface CondProps {
+ 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
+ * @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 => {
+ return <>{children}>
+}
+
+/**
+ * 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
+ */
+export const ChooseOne = ({ children }: PropsWithChildren): JSX.Element => {
+ const childArray = Children.toArray(children) as JSX.Element[]
+ const chosen = childArray.find((child) => child.props.condition)
+ return chosen ?? childArray[childArray.length - 1]
+}
diff --git a/site/src/components/Conditionals/Maybe.stories.tsx b/site/src/components/Conditionals/Maybe.stories.tsx
new file mode 100644
index 0000000000..d1baf45b13
--- /dev/null
+++ b/site/src/components/Conditionals/Maybe.stories.tsx
@@ -0,0 +1,19 @@
+import { Story } from "@storybook/react"
+import { Maybe, MaybeProps } from "./Maybe"
+
+export default {
+ title: "components/Conditionals/Maybe",
+ component: Maybe,
+}
+
+const Template: Story = (args: MaybeProps) => Now you see me
+
+export const ConditionIsTrue = Template.bind({})
+ConditionIsTrue.args = {
+ condition: true,
+}
+
+export const ConditionIsFalse = Template.bind({})
+ConditionIsFalse.args = {
+ condition: false,
+}
diff --git a/site/src/components/Conditionals/Maybe.tsx b/site/src/components/Conditionals/Maybe.tsx
new file mode 100644
index 0000000000..63af6ddb19
--- /dev/null
+++ b/site/src/components/Conditionals/Maybe.tsx
@@ -0,0 +1,17 @@
+import { PropsWithChildren } from "react"
+
+export interface MaybeProps {
+ condition: boolean
+}
+
+/**
+ * Wrapper component for conditionally rendering a child component without using "curly brace mode."
+ * @param condition boolean expression that determines whether the child will be rendered
+ * @returns the child or null
+ */
+export const Maybe = ({
+ children,
+ condition,
+}: PropsWithChildren): JSX.Element | null => {
+ return condition ? <>{children}> : null
+}
diff --git a/site/src/pages/TemplatesPage/TemplatesPageView.tsx b/site/src/pages/TemplatesPage/TemplatesPageView.tsx
index 76f5a89d50..64a3518a80 100644
--- a/site/src/pages/TemplatesPage/TemplatesPageView.tsx
+++ b/site/src/pages/TemplatesPage/TemplatesPageView.tsx
@@ -8,6 +8,8 @@ import TableHead from "@material-ui/core/TableHead"
import TableRow from "@material-ui/core/TableRow"
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
import useTheme from "@material-ui/styles/useTheme"
+import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
+import { Maybe } from "components/Conditionals/Maybe"
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
import { FC } from "react"
import { useTranslation } from "react-i18next"
@@ -103,7 +105,7 @@ export const TemplatesPageView: FC
- {props.templates && props.templates.length > 0 && (
+ 0)}>
Choose a template to create a new workspace
{props.canCreateTemplate ? (
@@ -121,113 +123,122 @@ export const TemplatesPageView: FC
- )}
+
- {props.getOrganizationsError ? (
-
- ) : props.getTemplatesError ? (
-
- ) : (
-
-
-
-
- {Language.nameLabel}
- {Language.usedByLabel}
- {Language.lastUpdatedLabel}
- {Language.createdByLabel}
-
-
-
-
- {props.loading && }
-
- {empty ? (
+
+
+
+
+
+
+
+
+
+
+
-
- }
- />
-
+ {Language.nameLabel}
+ {Language.usedByLabel}
+ {Language.lastUpdatedLabel}
+ {Language.createdByLabel}
+
- ) : (
- props.templates?.map((template) => {
- const templatePageLink = `/templates/${template.name}`
- const hasIcon = template.icon && template.icon !== ""
+
+
+
+
+
- return (
- {
- if (event.key === "Enter") {
- navigate(templatePageLink)
- }
- }}
- className={styles.clickableTableRow}
- >
-
-
-
-
- )
+
+
+
+
+ }
/>
-
-
-
-
- {Language.developerCount(template.active_user_count)}
-
-
-
-
-
- {createDayString(template.updated_at)}
-
-
-
-
-
- {template.created_by_name}
-
-
-
-
-
-
-
-
+
- )
- })
- )}
-
-
-
- )}
+
+
+ {props.templates?.map((template) => {
+ const templatePageLink = `/templates/${template.name}`
+ const hasIcon = template.icon && template.icon !== ""
+
+ return (
+ {
+ if (event.key === "Enter") {
+ navigate(templatePageLink)
+ }
+ }}
+ className={styles.clickableTableRow}
+ >
+
+
+
+
+ )
+ }
+ />
+
+
+
+
+ {Language.developerCount(template.active_user_count)}
+
+
+
+
+
+ {createDayString(template.updated_at)}
+
+
+
+
+
+ {template.created_by_name}
+
+
+
+
+
+
+
+
+
+ )
+ })}
+
+
+
+
+
+
+
)
}