From f843f23cb7023ffa872de0d8511124078cbd3761 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 12 Sep 2024 21:28:08 +0000 Subject: [PATCH 01/17] chore: finish draft work for FeatureBadge component --- .../FeatureBadge/FeatureBadge.stories.tsx | 22 ++++++ .../components/FeatureBadge/FeatureBadge.tsx | 67 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 site/src/components/FeatureBadge/FeatureBadge.stories.tsx create mode 100644 site/src/components/FeatureBadge/FeatureBadge.tsx diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx new file mode 100644 index 0000000000000..32072c3facaad --- /dev/null +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -0,0 +1,22 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { FeatureBadge } from "./FeatureBadge"; + +const meta: Meta = { + title: "components/FeatureBadge", + component: FeatureBadge, +}; + +export default meta; +type Story = StoryObj; + +export const Small: Story = { + args: { + size: "sm", + }, +}; + +export const Medium: Story = { + args: { + size: "md", + }, +}; diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx new file mode 100644 index 0000000000000..73ef11d7f2fa3 --- /dev/null +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -0,0 +1,67 @@ +import type { Interpolation, Theme } from "@emotion/react"; +import type { FC, HTMLAttributes, ReactNode } from "react"; + +/** + * All types of feature that we are currently supporting. Defined as record to + * ensure that we can't accidentally make typos when writing the badge text. + */ +const featureBadgeTypes = { + beta: "beta", + earlyAccess: "early access", +} as const satisfies Record; + +const styles = { + badge: (theme) => ({ + // Base type is based on a span so that the element can be placed inside + // more types of HTML elements without creating invalid markdown, but we + // still want the default display behavior to be div-like + display: "block", + maxWidth: "fit-content", + flexShrink: 0, + padding: "8px 4px", + border: `1px solid ${theme.palette.divider}`, + color: theme.palette.text.secondary, + backgroundColor: theme.palette.background.default, + borderRadius: "6px", + + // Base style assumes that small badges will be the default + fontSize: "0.75rem", + }), + + highlighted: (theme) => ({ + color: theme.palette.text.primary, + borderColor: theme.palette.text.primary, + }), + + mediumText: { + fontSize: "1rem", + }, +} as const satisfies Record>; + +type FeatureBadgeProps = Readonly< + Omit, "children"> & { + type: keyof typeof featureBadgeTypes; + size?: "sm" | "md"; + isHighlighted?: boolean; + } +>; + +export const FeatureBadge: FC = ({ + type, + size = "sm", + isHighlighted = false, + ...delegatedProps +}) => { + return ( + + {featureBadgeTypes[type]} + + ); +}; From 43874a4a239aa8282d255a10f42e0d817f88de28 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 12 Sep 2024 21:36:42 +0000 Subject: [PATCH 02/17] fix: add visually-hidden helper text for screen readers --- site/src/components/FeatureBadge/FeatureBadge.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 73ef11d7f2fa3..7c10a404858a4 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -1,4 +1,5 @@ import type { Interpolation, Theme } from "@emotion/react"; +import { visuallyHidden } from "@mui/utils"; import type { FC, HTMLAttributes, ReactNode } from "react"; /** @@ -61,7 +62,9 @@ export const FeatureBadge: FC = ({ ]} {...delegatedProps} > + (This feature is {featureBadgeTypes[type]} + ) ); }; From fb70781f771ed2f6190674ddb55883fb283fc691 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 12 Sep 2024 21:39:55 +0000 Subject: [PATCH 03/17] chore: add stories for highlighted state --- .../FeatureBadge/FeatureBadge.stories.tsx | 14 ++++++++++++++ site/src/components/FeatureBadge/FeatureBadge.tsx | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 32072c3facaad..3db8210787d2a 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -20,3 +20,17 @@ export const Medium: Story = { size: "md", }, }; + +export const HighlightedSmall: Story = { + args: { + size: "sm", + highlighted: true, + }, +}; + +export const HighlightedMedium: Story = { + args: { + size: "md", + highlighted: true, + }, +}; diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 7c10a404858a4..dd63ea48a4a43 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -43,14 +43,14 @@ type FeatureBadgeProps = Readonly< Omit, "children"> & { type: keyof typeof featureBadgeTypes; size?: "sm" | "md"; - isHighlighted?: boolean; + highlighted?: boolean; } >; export const FeatureBadge: FC = ({ type, size = "sm", - isHighlighted = false, + highlighted = false, ...delegatedProps }) => { return ( @@ -58,7 +58,7 @@ export const FeatureBadge: FC = ({ css={[ styles.badge, size === "md" && styles.mediumText, - isHighlighted && styles.highlighted, + highlighted && styles.highlighted, ]} {...delegatedProps} > From c2470f97f5868964494e4667c785f2a1d3f2ac14 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 12 Sep 2024 21:50:49 +0000 Subject: [PATCH 04/17] fix: update base styles --- .../components/FeatureBadge/FeatureBadge.stories.tsx | 3 +++ site/src/components/FeatureBadge/FeatureBadge.tsx | 11 +++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 3db8210787d2a..8b5236eb774a1 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -4,6 +4,9 @@ import { FeatureBadge } from "./FeatureBadge"; const meta: Meta = { title: "components/FeatureBadge", component: FeatureBadge, + args: { + type: "beta", + }, }; export default meta; diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index dd63ea48a4a43..0163292133a67 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -17,16 +17,19 @@ const styles = { // more types of HTML elements without creating invalid markdown, but we // still want the default display behavior to be div-like display: "block", + + // Base style assumes that small badges will be the default + fontSize: "0.75rem", + maxWidth: "fit-content", flexShrink: 0, - padding: "8px 4px", + padding: "4px 8px", + lineHeight: 1, + whiteSpace: "nowrap", border: `1px solid ${theme.palette.divider}`, color: theme.palette.text.secondary, backgroundColor: theme.palette.background.default, borderRadius: "6px", - - // Base style assumes that small badges will be the default - fontSize: "0.75rem", }), highlighted: (theme) => ({ From 693946f78df903c8e6b815e5500be766dcc4e87e Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Thu, 12 Sep 2024 21:54:48 +0000 Subject: [PATCH 05/17] chore: remove debug display option --- site/src/components/FeatureBadge/FeatureBadge.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 0163292133a67..0872a4b82faa4 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -8,7 +8,6 @@ import type { FC, HTMLAttributes, ReactNode } from "react"; */ const featureBadgeTypes = { beta: "beta", - earlyAccess: "early access", } as const satisfies Record; const styles = { From 129613b743b206eeeac2bbf4ffc630dd891402cf Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 19:46:43 +0000 Subject: [PATCH 06/17] chore: update Popover to propagate events --- site/src/components/Popover/Popover.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/site/src/components/Popover/Popover.tsx b/site/src/components/Popover/Popover.tsx index 7db3c4eda1799..43db214b0f097 100644 --- a/site/src/components/Popover/Popover.tsx +++ b/site/src/components/Popover/Popover.tsx @@ -1,10 +1,10 @@ import MuiPopover, { type PopoverProps as MuiPopoverProps, - // biome-ignore lint/nursery/noRestrictedImports: Used as base component } from "@mui/material/Popover"; import { type FC, type HTMLAttributes, + type PointerEvent, type ReactElement, type ReactNode, type RefObject, @@ -95,17 +95,20 @@ export const PopoverTrigger = ( const { children, ...elementProps } = props; const clickProps = { - onClick: () => { + onClick: (event: PointerEvent) => { popover.setOpen(true); + elementProps.onClick?.(event); }, }; const hoverProps = { - onPointerEnter: () => { + onPointerEnter: (event: PointerEvent) => { popover.setOpen(true); + elementProps.onPointerEnter?.(event); }, - onPointerLeave: () => { + onPointerLeave: (event: PointerEvent) => { popover.setOpen(false); + elementProps.onPointerLeave?.(event); }, }; From 6adea6b60652a7691c749b2df0b3d970089ecefe Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 19:47:04 +0000 Subject: [PATCH 07/17] wip: commit progress on FeatureBadge update --- .../FeatureBadge/FeatureBadge.stories.tsx | 23 +-- .../components/FeatureBadge/FeatureBadge.tsx | 134 ++++++++++++++++-- 2 files changed, 123 insertions(+), 34 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 8b5236eb774a1..1d522ed83748c 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -12,28 +12,9 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Small: Story = { +export const SmallInteractive: Story = { args: { size: "sm", - }, -}; - -export const Medium: Story = { - args: { - size: "md", - }, -}; - -export const HighlightedSmall: Story = { - args: { - size: "sm", - highlighted: true, - }, -}; - -export const HighlightedMedium: Story = { - args: { - size: "md", - highlighted: true, + variant: "interactive", }, }; diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 0872a4b82faa4..ff1105cc8426f 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -1,6 +1,16 @@ import type { Interpolation, Theme } from "@emotion/react"; +import Link from "@mui/material/Link"; import { visuallyHidden } from "@mui/utils"; -import type { FC, HTMLAttributes, ReactNode } from "react"; +import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip"; +import { Popover, PopoverTrigger } from "components/Popover/Popover"; +import { + useEffect, + useState, + type FC, + type HTMLAttributes, + type ReactNode, +} from "react"; +import { docs } from "utils/docs"; /** * All types of feature that we are currently supporting. Defined as record to @@ -8,6 +18,7 @@ import type { FC, HTMLAttributes, ReactNode } from "react"; */ const featureBadgeTypes = { beta: "beta", + experimental: "experimental", } as const satisfies Record; const styles = { @@ -16,11 +27,12 @@ const styles = { // more types of HTML elements without creating invalid markdown, but we // still want the default display behavior to be div-like display: "block", + maxWidth: "fit-content", // Base style assumes that small badges will be the default fontSize: "0.75rem", - maxWidth: "fit-content", + cursor: "default", flexShrink: 0, padding: "4px 8px", lineHeight: 1, @@ -31,42 +43,138 @@ const styles = { borderRadius: "6px", }), - highlighted: (theme) => ({ + badgeHover: (theme) => ({ color: theme.palette.text.primary, borderColor: theme.palette.text.primary, }), - mediumText: { + badgeLargeText: { fontSize: "1rem", }, + + tooltipTitle: { + fontWeight: 600, + fontFamily: "inherit", + fontSize: 18, + margin: 0, + lineHeight: 1, + paddingBottom: "8px", + }, + + tooltipDescription: { + margin: 0, + lineHeight: 1.2, + }, } as const satisfies Record>; type FeatureBadgeProps = Readonly< Omit, "children"> & { type: keyof typeof featureBadgeTypes; - size?: "sm" | "md"; - highlighted?: boolean; + size?: "sm" | "lg"; + + /** + * Defines how the FeatureBadge should render. + * - interactive (default) - The badge functions like a link and + * controls its own hover styling. + * - static - The badge is completely static and has no interaction + * behavior. + * - staticHover - The badge is completely static, but displays badge + hover styling (but nothing related to links). Useful if you want a + parent component to control the hover styling. + */ + variant?: "interactive" | "static" | "staticHover"; } >; export const FeatureBadge: FC = ({ type, size = "sm", - highlighted = false, + variant = "interactive", + onPointerEnter, + onPointerLeave, ...delegatedProps }) => { - return ( + const [isBadgeHovering, setIsBadgeHovering] = useState(false); + useEffect(() => { + const onWindowBlur = () => { + setIsBadgeHovering(false); + }; + + window.addEventListener("blur", onWindowBlur); + return () => window.removeEventListener("blur", onWindowBlur); + }, []); + + const featureType = featureBadgeTypes[type]; + const showHoverStyles = + variant === "staticHover" || (variant === "interactive" && isBadgeHovering); + + const coreContent = ( - (This feature is - {featureBadgeTypes[type]} - ) + (This is a + {featureType} + feature) ); + + if (variant !== "interactive") { + return coreContent; + } + + return ( + + { + setIsBadgeHovering(true); + onPointerEnter?.(event); + }} + onPointerLeave={(event) => { + setIsBadgeHovering(false); + onPointerLeave?.(event); + }} + > + {coreContent} + + + +
+ {capitalizeFirstLetter(featureType)} Feature +
+ +

+ This is {getGrammaticalArticle(featureType)} {featureType} feature. It + has not yet been marked for general availability. +

+ + + Feature stage documentation + (link opens in new tab) + +
+
+ ); }; + +function getGrammaticalArticle(nextWord: string): string { + const vowels = ["a", "e", "i", "o", "u"]; + const firstLetter = nextWord.slice(0, 1).toLowerCase(); + return vowels.includes(firstLetter) ? "an" : "a"; +} + +function capitalizeFirstLetter(text: string): string { + return text.slice(0, 1).toUpperCase() + text.slice(1); +} From d4455a89f91d88cb6017ab11a9fb962d7d1dd33d Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 20:05:09 +0000 Subject: [PATCH 08/17] wip: commit more progress --- .../FeatureBadge/FeatureBadge.stories.tsx | 11 ++++++++++- site/src/components/FeatureBadge/FeatureBadge.tsx | 15 +++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 1d522ed83748c..a4610a160802d 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -12,8 +12,17 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const SmallInteractive: Story = { +export const SmallInteractiveBeta: Story = { args: { + type: "beta", + size: "sm", + variant: "interactive", + }, +}; + +export const SmallInteractiveExperimental: Story = { + args: { + type: "experimental", size: "sm", variant: "interactive", }, diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index ff1105cc8426f..3ec1267a764ff 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -52,17 +52,24 @@ const styles = { fontSize: "1rem", }, - tooltipTitle: { + tooltipTitle: (theme) => ({ + color: theme.palette.text.primary, fontWeight: 600, fontFamily: "inherit", fontSize: 18, margin: 0, lineHeight: 1, paddingBottom: "8px", - }, + }), tooltipDescription: { margin: 0, + lineHeight: 1.4, + paddingBottom: "8px", + }, + + tooltipLink: { + fontWeight: 600, lineHeight: 1.2, }, } as const satisfies Record>; @@ -159,9 +166,9 @@ export const FeatureBadge: FC = ({ href={docs("/contributing/feature-stages")} target="_blank" rel="noreferrer" - css={{ fontWeight: 600 }} + css={styles.tooltipLink} > - Feature stage documentation + Learn about feature stages (link opens in new tab) From 8ae71d389e2040435cab04c7d881ff61e51cbcac Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 20:17:04 +0000 Subject: [PATCH 09/17] chore: update tag definitions to satify Biome --- .../AppearancePage/AppearanceForm.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx index a4e520c2b7137..6559e31723156 100644 --- a/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx +++ b/site/src/pages/UserSettingsPage/AppearancePage/AppearanceForm.tsx @@ -175,23 +175,23 @@ const ThemePreview: FC = ({
-
-
-
+
+
+
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
From 0ad68afb99fc5bcf2d1708637b7e1dd22fd30f8c Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 21:00:40 +0000 Subject: [PATCH 10/17] chore: update all colors for preview role --- .../components/FeatureBadge/FeatureBadge.tsx | 14 +++++--- site/src/theme/dark/roles.ts | 36 +++++++++++++++---- site/src/theme/darkBlue/roles.ts | 36 +++++++++++++++---- site/src/theme/index.ts | 1 - site/src/theme/light/roles.ts | 36 +++++++++++++++---- site/src/theme/roles.ts | 2 +- 6 files changed, 97 insertions(+), 28 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 3ec1267a764ff..353a44b527962 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -10,6 +10,7 @@ import { type HTMLAttributes, type ReactNode, } from "react"; +import tailwindColors from "theme/tailwindColors"; import { docs } from "utils/docs"; /** @@ -37,15 +38,18 @@ const styles = { padding: "4px 8px", lineHeight: 1, whiteSpace: "nowrap", - border: `1px solid ${theme.palette.divider}`, - color: theme.palette.text.secondary, - backgroundColor: theme.palette.background.default, + border: `1px solid ${theme.roles.preview.outline}`, + color: theme.roles.preview.text, + backgroundColor: theme.roles.preview.background, borderRadius: "6px", + transition: + "color 0.4s ease-in-out, border-color 0.4s ease-in-out, background-color 0.4s ease-in-out", }), badgeHover: (theme) => ({ - color: theme.palette.text.primary, - borderColor: theme.palette.text.primary, + color: theme.roles.preview.hover.text, + borderColor: theme.roles.preview.hover.outline, + backgroundColor: theme.roles.preview.hover.background, }), badgeLargeText: { diff --git a/site/src/theme/dark/roles.ts b/site/src/theme/dark/roles.ts index 32a9ea4f12992..dfefd1d10909b 100644 --- a/site/src/theme/dark/roles.ts +++ b/site/src/theme/dark/roles.ts @@ -1,7 +1,7 @@ import type { Roles } from "../roles"; import colors from "../tailwindColors"; -export default { +const roles: Roles = { danger: { background: colors.orange[950], outline: colors.orange[500], @@ -143,13 +143,35 @@ export default { }, }, preview: { - background: colors.violet[950], - outline: colors.violet[500], - text: colors.violet[50], + background: colors.cyan[950], + outline: colors.cyan[600], + text: colors.cyan[400], fill: { - solid: colors.violet[400], - outline: colors.violet[400], + solid: colors.cyan[400], + outline: colors.cyan[400], text: colors.white, }, + hover: { + background: colors.zinc[950], + outline: colors.cyan[500], + text: colors.cyan[300], + fill: { + text: colors.white, + outline: colors.cyan[600], + solid: colors.cyan[600], + }, + }, + disabled: { + background: colors.zinc[950], + outline: colors.cyan[500], + text: colors.cyan[300], + fill: { + text: colors.white, + outline: colors.cyan[600], + solid: colors.cyan[600], + }, + }, }, -} satisfies Roles; +}; + +export default roles; diff --git a/site/src/theme/darkBlue/roles.ts b/site/src/theme/darkBlue/roles.ts index 744b7329249b9..a0f36daa810b9 100644 --- a/site/src/theme/darkBlue/roles.ts +++ b/site/src/theme/darkBlue/roles.ts @@ -1,7 +1,7 @@ import type { Roles } from "../roles"; import colors from "../tailwindColors"; -export default { +const roles: Roles = { danger: { background: colors.orange[950], outline: colors.orange[500], @@ -143,13 +143,35 @@ export default { }, }, preview: { - background: colors.violet[950], - outline: colors.violet[500], - text: colors.violet[50], + background: colors.cyan[950], + outline: colors.cyan[600], + text: colors.cyan[400], fill: { - solid: colors.violet[400], - outline: colors.violet[400], + solid: colors.cyan[400], + outline: colors.cyan[400], text: colors.white, }, + hover: { + background: colors.zinc[950], + outline: colors.cyan[500], + text: colors.cyan[300], + fill: { + text: colors.white, + outline: colors.cyan[600], + solid: colors.cyan[600], + }, + }, + disabled: { + background: colors.zinc[950], + outline: colors.cyan[500], + text: colors.cyan[300], + fill: { + text: colors.white, + outline: colors.cyan[600], + solid: colors.cyan[600], + }, + }, }, -} satisfies Roles; +}; + +export default roles; diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index 8509ccb4135a6..7516fe1207929 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noRestrictedImports: We still use `Theme` as a basis for our actual theme, for now. import type { Theme as MuiTheme } from "@mui/material/styles"; import type * as monaco from "monaco-editor"; import type { Branding } from "./branding"; diff --git a/site/src/theme/light/roles.ts b/site/src/theme/light/roles.ts index fe3d1d9687bfa..905d45384f247 100644 --- a/site/src/theme/light/roles.ts +++ b/site/src/theme/light/roles.ts @@ -1,7 +1,7 @@ import type { Roles } from "../roles"; import colors from "../tailwindColors"; -export default { +const roles: Roles = { danger: { background: colors.orange[50], outline: colors.orange[400], @@ -143,13 +143,35 @@ export default { }, }, preview: { - background: colors.violet[50], - outline: colors.violet[500], - text: colors.violet[950], + background: colors.cyan[100], + outline: colors.cyan[500], + text: colors.cyan[950], fill: { - solid: colors.violet[600], - outline: colors.violet[600], + solid: colors.cyan[600], + outline: colors.cyan[600], text: colors.white, }, + hover: { + background: colors.cyan[50], + outline: colors.cyan[600], + text: colors.cyan[950], + fill: { + outline: colors.cyan[500], + solid: colors.cyan[500], + text: colors.white, + }, + }, + disabled: { + background: colors.cyan[50], + outline: colors.cyan[800], + text: colors.cyan[200], + fill: { + solid: colors.cyan[800], + outline: colors.cyan[800], + text: colors.white, + }, + }, }, -} satisfies Roles; +}; + +export default roles; diff --git a/site/src/theme/roles.ts b/site/src/theme/roles.ts index 13d54f8840d20..87620bd43ef8c 100644 --- a/site/src/theme/roles.ts +++ b/site/src/theme/roles.ts @@ -36,7 +36,7 @@ export interface Roles { /** This isn't quite ready for prime-time, but you're welcome to look around! * Preview features, experiments, unstable, etc. */ - preview: Role; + preview: InteractiveRole; } /** From 781a6099b81089b5dc1e3d3b6332d0177145db79 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 21:45:02 +0000 Subject: [PATCH 11/17] fix: make sure badge shows as hovered while inside tooltip --- .../FeatureBadge/FeatureBadge.stories.tsx | 16 +++++++++++++++ .../components/FeatureBadge/FeatureBadge.tsx | 20 ++++++++++++++----- site/src/components/Popover/Popover.tsx | 17 ++++++++++++---- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index a4610a160802d..7326015b23dae 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -27,3 +27,19 @@ export const SmallInteractiveExperimental: Story = { variant: "interactive", }, }; + +export const LargeInteractiveBeta: Story = { + args: { + type: "beta", + size: "lg", + variant: "interactive", + }, +}; + +export const LargeStaticBeta: Story = { + args: { + type: "beta", + size: "lg", + variant: "static", + }, +}; diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 353a44b527962..57ba3e05b754e 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -43,7 +43,7 @@ const styles = { backgroundColor: theme.roles.preview.background, borderRadius: "6px", transition: - "color 0.4s ease-in-out, border-color 0.4s ease-in-out, background-color 0.4s ease-in-out", + "color 0.2s ease-in-out, border-color 0.2s ease-in-out, background-color 0.2s ease-in-out", }), badgeHover: (theme) => ({ @@ -105,10 +105,15 @@ export const FeatureBadge: FC = ({ onPointerLeave, ...delegatedProps }) => { + // Not a big fan of having two hover variables, but we need to make sure the + // badge maintains its hover styling while the mouse is inside the tooltip const [isBadgeHovering, setIsBadgeHovering] = useState(false); + const [isTooltipHovering, setIsTooltipHovering] = useState(false); + useEffect(() => { const onWindowBlur = () => { setIsBadgeHovering(false); + setIsTooltipHovering(false); }; window.addEventListener("blur", onWindowBlur); @@ -116,16 +121,19 @@ export const FeatureBadge: FC = ({ }, []); const featureType = featureBadgeTypes[type]; - const showHoverStyles = - variant === "staticHover" || (variant === "interactive" && isBadgeHovering); + const showBadgeHoverStyle = + variant === "staticHover" || + (variant === "interactive" && (isBadgeHovering || isTooltipHovering)); const coreContent = ( (This is a @@ -156,6 +164,8 @@ export const FeatureBadge: FC = ({ setIsTooltipHovering(true)} + onPointerLeave={() => setIsTooltipHovering(false)} >
{capitalizeFirstLetter(featureType)} Feature @@ -163,7 +173,7 @@ export const FeatureBadge: FC = ({

This is {getGrammaticalArticle(featureType)} {featureType} feature. It - has not yet been marked for general availability. + has not yet reached generally availability (GA).

= ({ horizontal = "left", + onPointerEnter, + onPointerLeave, ...popoverProps }) => { const popover = usePopover(); @@ -155,7 +158,7 @@ export const PopoverContent: FC = ({ }, }} {...horizontalProps(horizontal)} - {...modeProps(popover)} + {...modeProps(popover, onPointerEnter, onPointerLeave)} {...popoverProps} id={popover.id} open={popover.open} @@ -165,14 +168,20 @@ export const PopoverContent: FC = ({ ); }; -const modeProps = (popover: PopoverContextValue) => { +const modeProps = ( + popover: PopoverContextValue, + externalOnPointerEnter: PointerEventHandler | undefined, + externalOnPointerLeave: PointerEventHandler | undefined, +) => { if (popover.mode === "hover") { return { - onPointerEnter: () => { + onPointerEnter: (event: PointerEvent) => { popover.setOpen(true); + externalOnPointerEnter?.(event); }, - onPointerLeave: () => { + onPointerLeave: (event: PointerEvent) => { popover.setOpen(false); + externalOnPointerLeave?.(event); }, }; } From a98186479dec68ecd4e1a1dc0d25d3d6e955610d Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 13 Sep 2024 22:10:25 +0000 Subject: [PATCH 12/17] wip: commit progress on adding story for controlled variant --- .../FeatureBadge/FeatureBadge.stories.tsx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 7326015b23dae..842dbebd0353d 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -1,5 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { FeatureBadge } from "./FeatureBadge"; +import { useState } from "react"; +import { useTheme } from "@emotion/react"; const meta: Meta = { title: "components/FeatureBadge", @@ -43,3 +45,44 @@ export const LargeStaticBeta: Story = { variant: "static", }, }; + +export const HoverControlledByParent: Story = { + args: { + type: "experimental", + size: "sm", + }, + + decorators: (Story, context) => { + const theme = useTheme(); + const [isHovering, setIsHovering] = useState(false); + + return ( + + ); + }, +}; From f47d0598d7ee1ccb4ce5416a4ab2b1939595a5a3 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Mon, 16 Sep 2024 13:37:54 +0000 Subject: [PATCH 13/17] fix: sort imports --- site/src/components/FeatureBadge/FeatureBadge.stories.tsx | 4 ++-- site/src/components/FeatureBadge/FeatureBadge.tsx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 842dbebd0353d..9143c130d809b 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -1,7 +1,7 @@ +import { useTheme } from "@emotion/react"; import type { Meta, StoryObj } from "@storybook/react"; -import { FeatureBadge } from "./FeatureBadge"; import { useState } from "react"; -import { useTheme } from "@emotion/react"; +import { FeatureBadge } from "./FeatureBadge"; const meta: Meta = { title: "components/FeatureBadge", diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 57ba3e05b754e..eeb160e622118 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -10,7 +10,6 @@ import { type HTMLAttributes, type ReactNode, } from "react"; -import tailwindColors from "theme/tailwindColors"; import { docs } from "utils/docs"; /** From ad61763536431103318b6a1295392e3f9c8c2660 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Mon, 16 Sep 2024 13:50:20 +0000 Subject: [PATCH 14/17] refactor: change component API to be more obvious/ergonomic --- .../FeatureBadge/FeatureBadge.stories.tsx | 3 +- .../components/FeatureBadge/FeatureBadge.tsx | 54 ++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx index 9143c130d809b..714b461ea4140 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.stories.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.stories.tsx @@ -79,7 +79,8 @@ export const HoverControlledByParent: Story = { {Story({ args: { ...context.initialArgs, - variant: isHovering ? "staticHover" : "static", + variant: "static", + highlighted: isHovering, }, })} diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index eeb160e622118..208f6f1c9db22 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -77,29 +77,43 @@ const styles = { }, } as const satisfies Record>; +function grammaticalArticle(nextWord: string): string { + const vowels = ["a", "e", "i", "o", "u"]; + const firstLetter = nextWord.slice(0, 1).toLowerCase(); + return vowels.includes(firstLetter) ? "an" : "a"; +} + +function capitalizeFirstLetter(text: string): string { + return text.slice(0, 1).toUpperCase() + text.slice(1); +} + type FeatureBadgeProps = Readonly< Omit, "children"> & { type: keyof typeof featureBadgeTypes; size?: "sm" | "lg"; - - /** - * Defines how the FeatureBadge should render. - * - interactive (default) - The badge functions like a link and - * controls its own hover styling. - * - static - The badge is completely static and has no interaction - * behavior. - * - staticHover - The badge is completely static, but displays badge - hover styling (but nothing related to links). Useful if you want a - parent component to control the hover styling. - */ - variant?: "interactive" | "static" | "staticHover"; - } + } & ( + | { + /** + * Defines whether the FeatureBadge should act as a + * controlled or uncontrolled component with its hover and + * general interaction styling. + */ + variant: "interactive"; + + // Had to specify the highlighted key for this union option + // even though it won't be used, because otherwise the type + // ergonomics for users would be too clunky. + highlighted?: undefined; + } + | { variant: "static"; highlighted?: boolean } + ) >; export const FeatureBadge: FC = ({ type, size = "sm", variant = "interactive", + highlighted = false, onPointerEnter, onPointerLeave, ...delegatedProps @@ -121,7 +135,7 @@ export const FeatureBadge: FC = ({ const featureType = featureBadgeTypes[type]; const showBadgeHoverStyle = - variant === "staticHover" || + highlighted || (variant === "interactive" && (isBadgeHovering || isTooltipHovering)); const coreContent = ( @@ -171,7 +185,7 @@ export const FeatureBadge: FC = ({

- This is {getGrammaticalArticle(featureType)} {featureType} feature. It + This is {grammaticalArticle(featureType)} {featureType} feature. It has not yet reached generally availability (GA).

@@ -188,13 +202,3 @@ export const FeatureBadge: FC = ({ ); }; - -function getGrammaticalArticle(nextWord: string): string { - const vowels = ["a", "e", "i", "o", "u"]; - const firstLetter = nextWord.slice(0, 1).toLowerCase(); - return vowels.includes(firstLetter) ? "an" : "a"; -} - -function capitalizeFirstLetter(text: string): string { - return text.slice(0, 1).toUpperCase() + text.slice(1); -} From 6e16aaabb0f2a8a9650450d6d81c75e87f8deabd Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Mon, 16 Sep 2024 13:54:37 +0000 Subject: [PATCH 15/17] fix: add biome-ignore comments to more base files --- site/src/components/Popover/Popover.tsx | 1 + site/src/theme/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/site/src/components/Popover/Popover.tsx b/site/src/components/Popover/Popover.tsx index 0e12728667537..8b4479d95b3a4 100644 --- a/site/src/components/Popover/Popover.tsx +++ b/site/src/components/Popover/Popover.tsx @@ -1,5 +1,6 @@ import MuiPopover, { type PopoverProps as MuiPopoverProps, + // biome-ignore lint/nursery/noRestrictedImports: This is the base component that our custom popover is based on } from "@mui/material/Popover"; import { type FC, diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index 7516fe1207929..3079ce01b27a3 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -1,3 +1,4 @@ +// biome-ignore lint/nursery/noRestrictedImports: Have to use MUI styles as base import type { Theme as MuiTheme } from "@mui/material/styles"; import type * as monaco from "monaco-editor"; import type { Branding } from "./branding"; From 6a19b611ab02128674f9a460f2becfbd6aee0c7b Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Mon, 16 Sep 2024 13:59:54 +0000 Subject: [PATCH 16/17] fix: update import order again --- site/src/components/FeatureBadge/FeatureBadge.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/FeatureBadge/FeatureBadge.tsx b/site/src/components/FeatureBadge/FeatureBadge.tsx index 208f6f1c9db22..75b7c15045cde 100644 --- a/site/src/components/FeatureBadge/FeatureBadge.tsx +++ b/site/src/components/FeatureBadge/FeatureBadge.tsx @@ -4,11 +4,11 @@ import { visuallyHidden } from "@mui/utils"; import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip"; import { Popover, PopoverTrigger } from "components/Popover/Popover"; import { - useEffect, - useState, type FC, type HTMLAttributes, type ReactNode, + useEffect, + useState, } from "react"; import { docs } from "utils/docs"; From a2338673b592a6459c49373292d3568044b0c536 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Mon, 16 Sep 2024 14:03:30 +0000 Subject: [PATCH 17/17] chore: revert biome-ignore comment --- site/src/theme/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts index 3079ce01b27a3..8509ccb4135a6 100644 --- a/site/src/theme/index.ts +++ b/site/src/theme/index.ts @@ -1,4 +1,4 @@ -// biome-ignore lint/nursery/noRestrictedImports: Have to use MUI styles as base +// biome-ignore lint/nursery/noRestrictedImports: We still use `Theme` as a basis for our actual theme, for now. import type { Theme as MuiTheme } from "@mui/material/styles"; import type * as monaco from "monaco-editor"; import type { Branding } from "./branding"; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy