Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: create UI badges for labeling beta features#14661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Parkreiner merged 48 commits intomainfrommes/beta-badges
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
48 commits
Select commitHold shift + click to select a range
f843f23
chore: finish draft work for FeatureBadge component
ParkreinerSep 12, 2024
43874a4
fix: add visually-hidden helper text for screen readers
ParkreinerSep 12, 2024
fb70781
chore: add stories for highlighted state
ParkreinerSep 12, 2024
c2470f9
fix: update base styles
ParkreinerSep 12, 2024
693946f
chore: remove debug display option
ParkreinerSep 12, 2024
c2d7cda
Merge branch 'main' into mes/beta-badges
ParkreinerSep 13, 2024
129613b
chore: update Popover to propagate events
ParkreinerSep 13, 2024
6adea6b
wip: commit progress on FeatureBadge update
ParkreinerSep 13, 2024
d4455a8
wip: commit more progress
ParkreinerSep 13, 2024
8ae71d3
chore: update tag definitions to satify Biome
ParkreinerSep 13, 2024
0ad68af
chore: update all colors for preview role
ParkreinerSep 13, 2024
781a609
fix: make sure badge shows as hovered while inside tooltip
ParkreinerSep 13, 2024
a981864
wip: commit progress on adding story for controlled variant
ParkreinerSep 13, 2024
f47d059
fix: sort imports
ParkreinerSep 16, 2024
ad61763
refactor: change component API to be more obvious/ergonomic
ParkreinerSep 16, 2024
6e16aaa
fix: add biome-ignore comments to more base files
ParkreinerSep 16, 2024
6a19b61
fix: update import order again
ParkreinerSep 16, 2024
a233867
chore: revert biome-ignore comment
ParkreinerSep 16, 2024
8cbe639
Merge branch 'main' into mes/beta-badges
ParkreinerSep 17, 2024
7e1ec68
chore: update body text for tooltip
ParkreinerSep 17, 2024
1b58e4d
chore: update dark preivew role to use sky palette
ParkreinerSep 17, 2024
ebc5397
chore: update color palettes for light/darkBlue themes
ParkreinerSep 17, 2024
a9b6897
chore: add beta badge to organizations subheader
ParkreinerSep 17, 2024
31e1fa7
chore: add beta badge to organizations settings page
ParkreinerSep 17, 2024
e16b140
chore: beef up font weight for form header
ParkreinerSep 17, 2024
3aeb3c0
fix: update text contrast for org menu list
ParkreinerSep 17, 2024
71323ac
chore: add beta badge to deployment dropdown
ParkreinerSep 17, 2024
da31c84
fix: run biome on imports
ParkreinerSep 17, 2024
c7308c3
chore: remove API for controlling FeatureBadge hover styling externally
ParkreinerSep 17, 2024
230aa1d
chore: add xs size for badge
ParkreinerSep 17, 2024
fb4b734
fix: update font weight for xs feature badges
ParkreinerSep 18, 2024
5a7bbd3
chore: add beta badges to all org headers
ParkreinerSep 18, 2024
9521f25
Merge branch 'main' into mes/beta-badges
ParkreinerSep 18, 2024
6638942
fix: turn badges and tooltips into separate concerns
ParkreinerSep 18, 2024
9a18e51
fix: update hover styling
ParkreinerSep 18, 2024
b093a99
docs: update wording on comment
ParkreinerSep 18, 2024
9647fc6
fix: apply formatting
ParkreinerSep 18, 2024
0197466
chore: rename FeatureBadge to FeatureStageBadge
ParkreinerSep 18, 2024
fb86964
refactor: redefine FeatureStageBadge
ParkreinerSep 20, 2024
0f64533
chore: update stories
ParkreinerSep 20, 2024
6a2cfcf
fix: add blur behavior to popover
ParkreinerSep 20, 2024
eb2b1c2
chore: revert theme colors
ParkreinerSep 20, 2024
0b46eca
chore: create featureStage branding namespace
ParkreinerSep 20, 2024
4302b3d
fix: make sure cleanup function is set up properly
ParkreinerSep 20, 2024
be8121e
docs: update wording on comment for clarity
ParkreinerSep 20, 2024
1e5d62b
Merge branch 'main' into mes/beta-badges
ParkreinerSep 20, 2024
a824404
Merge branch 'main' into mes/beta-badges
ParkreinerSep 20, 2024
39daf80
refactor: move styles down
ParkreinerSep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions.vscode/settings.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -120,6 +120,7 @@
"stretchr",
"STTY",
"stuntest",
"subpage",
"tailbroker",
"tailcfg",
"tailexchange",
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
import type { Meta, StoryObj } from "@storybook/react";
import { FeatureStageBadge } from "./FeatureStageBadge";

const meta: Meta<typeof FeatureStageBadge> = {
title: "components/FeatureStageBadge",
component: FeatureStageBadge,
args: {
contentType: "beta",
},
};

export default meta;
type Story = StoryObj<typeof FeatureStageBadge>;

export const MediumBeta: Story = {
args: {
size: "md",
},
};

export const SmallBeta: Story = {
args: {
size: "sm",
},
};

export const LargeBeta: Story = {
args: {
size: "lg",
},
};

export const MediumExperimental: Story = {
args: {
size: "md",
contentType: "experimental",
},
};
133 changes: 133 additions & 0 deletionssite/src/components/FeatureStageBadge/FeatureStageBadge.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
import type { Interpolation, Theme } from "@emotion/react";
import Link from "@mui/material/Link";
import { visuallyHidden } from "@mui/utils";
import { HelpTooltipContent } from "components/HelpTooltip/HelpTooltip";
import { Popover, PopoverTrigger } from "components/Popover/Popover";
import type { FC, HTMLAttributes, ReactNode } from "react";
import { docs } from "utils/docs";

/**
* 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 featureStageBadgeTypes = {
beta: "beta",
experimental: "experimental",
} as const satisfies Record<string, ReactNode>;

type FeatureStageBadgeProps = Readonly<
Omit<HTMLAttributes<HTMLSpanElement>, "children"> & {
contentType: keyof typeof featureStageBadgeTypes;
size?: "sm" | "md" | "lg";
}
>;

export const FeatureStageBadge: FC<FeatureStageBadgeProps> = ({
contentType,
size = "md",
...delegatedProps
}) => {
return (
<Popover mode="hover">
<PopoverTrigger>
{({ isOpen }) => (
<span
css={[
styles.badge,
size === "sm" && styles.badgeSmallText,
size === "lg" && styles.badgeLargeText,
isOpen && styles.badgeHover,
]}
{...delegatedProps}
>
<span style={visuallyHidden}> (This is a</span>
{featureStageBadgeTypes[contentType]}
<span style={visuallyHidden}> feature)</span>
</span>
)}
</PopoverTrigger>

<HelpTooltipContent
anchorOrigin={{ vertical: "bottom", horizontal: "center" }}
transformOrigin={{ vertical: "top", horizontal: "center" }}
>
<p css={styles.tooltipDescription}>
This feature has not yet reached general availability (GA).
</p>

<Link
href={docs("/contributing/feature-stages")}
target="_blank"
rel="noreferrer"
css={styles.tooltipLink}
>
Learn about feature stages
<span style={visuallyHidden}> (link opens in new tab)</span>
</Link>
</HelpTooltipContent>
</Popover>
);
};

const styles = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think we generally definestyles below the component

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",

// Base style assumes that medium badges will be the default
fontSize: "0.75rem",

cursor: "default",
flexShrink: 0,
padding: "4px 8px",
lineHeight: 1,
whiteSpace: "nowrap",
border: `1px solid ${theme.branding.featureStage.border}`,
color: theme.branding.featureStage.text,
backgroundColor: theme.branding.featureStage.background,
borderRadius: "6px",
transition:
"color 0.2s ease-in-out, border-color 0.2s ease-in-out, background-color 0.2s ease-in-out",
}),

badgeHover: (theme) => ({
color: theme.branding.featureStage.hover.text,
borderColor: theme.branding.featureStage.hover.border,
backgroundColor: theme.branding.featureStage.hover.background,
}),

badgeLargeText: {
fontSize: "1rem",
},

badgeSmallText: {
// Have to beef up font weight so that the letters still maintain the
// same relative thickness as all our other main UI text
fontWeight: 500,
fontSize: "0.625rem",
},

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<string, Interpolation<Theme>>;
2 changes: 1 addition & 1 deletionsite/src/components/Form/Form.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -170,7 +170,7 @@ const styles = {
formSectionInfoTitle: (theme) => ({
fontSize: 20,
color: theme.palette.text.primary,
fontWeight:400,
fontWeight:500,
margin: 0,
marginBottom: 8,
display: "flex",
Expand Down
78 changes: 59 additions & 19 deletionssite/src/components/Popover/Popover.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
import MuiPopover, {
type PopoverProps as MuiPopoverProps,
// biome-ignore lint/nursery/noRestrictedImports:Used asbase component
// biome-ignore lint/nursery/noRestrictedImports:This is thebase component that our custom popover is based on
Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I might have Biome configured wrong because my Biome plugin is flagging all of these comments as not doing anything

Accidentally removed this at first, but that's definitely a me problem, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I think it doesn't like that our biome.json config is in the site/ directory 🙄 I hate that extensions never bother to handle subdirectories well.

} from "@mui/material/Popover";
import {
type FC,
type HTMLAttributes,
type PointerEvent,
type PointerEventHandler,
type ReactElement,
type ReactNode,
type RefObject,
cloneElement,
createContext,
useContext,
useEffect,
useId,
useRef,
useState,
Expand All@@ -20,10 +23,13 @@ type TriggerMode = "hover" | "click";

type TriggerRef = RefObject<HTMLElement>;

type TriggerElement = ReactElement<{
ref: TriggerRef;
onClick?: () => void;
}>;
// Have to append ReactNode type to satisfy React's cloneElement function. It
// has absolutely no bearing on what happens at runtime
type TriggerElement = ReactNode &
ReactElement<{
ref: TriggerRef;
onClick?: () => void;
}>;

type PopoverContextValue = {
id: string;
Expand DownExpand Up@@ -61,6 +67,15 @@ export const Popover: FC<PopoverProps> = (props) => {
const [uncontrolledOpen, setUncontrolledOpen] = useState(false);
const triggerRef: TriggerRef = useRef(null);

// Helps makes sure that popovers close properly when the user switches to
// a different tab. This won't help with controlled instances of the
// component, but this is basically the most we can do from here
useEffect(() => {
const closeOnTabSwitch = () => setUncontrolledOpen(false);
window.addEventListener("blur", closeOnTabSwitch);
return () => window.removeEventListener("blur", closeOnTabSwitch);
}, []);

const value: PopoverContextValue = {
triggerRef,
id: `${hookId}-popover`,
Expand All@@ -86,30 +101,47 @@ export const usePopover = () => {
return context;
};

export const PopoverTrigger = (
props: HTMLAttributes<HTMLElement> & {
children: TriggerElement;
},
) => {
type PopoverTriggerRenderProps = Readonly<{
isOpen: boolean;
}>;

type PopoverTriggerProps = Readonly<
Omit<HTMLAttributes<HTMLElement>, "children"> & {
children:
| TriggerElement
| ((props: PopoverTriggerRenderProps) => TriggerElement);
}
>;

export const PopoverTrigger: FC<PopoverTriggerProps> = (props) => {
const popover = usePopover();
const { children, ...elementProps } = props;
const { children, onClick, onPointerEnter, onPointerLeave, ...elementProps } =
props;

const clickProps = {
onClick: () => {
onClick: (event: PointerEvent<HTMLElement>) => {
popover.setOpen(true);
onClick?.(event);
},
};

const hoverProps = {
onPointerEnter: () => {
onPointerEnter: (event: PointerEvent<HTMLElement>) => {
popover.setOpen(true);
onPointerEnter?.(event);
},
onPointerLeave: () => {
onPointerLeave: (event: PointerEvent<HTMLElement>) => {
popover.setOpen(false);
onPointerLeave?.(event);
},
};

return cloneElement(props.children, {
const evaluatedChildren =
typeof children === "function"
? children({ isOpen: popover.open })
: children;

return cloneElement(evaluatedChildren, {
...elementProps,
...(popover.mode === "click" ? clickProps : hoverProps),
"aria-haspopup": true,
Expand All@@ -130,6 +162,8 @@ export type PopoverContentProps = Omit<

export const PopoverContent: FC<PopoverContentProps> = ({
horizontal = "left",
onPointerEnter,
onPointerLeave,
...popoverProps
}) => {
const popover = usePopover();
Expand All@@ -152,7 +186,7 @@ export const PopoverContent: FC<PopoverContentProps> = ({
},
}}
{...horizontalProps(horizontal)}
{...modeProps(popover)}
{...modeProps(popover, onPointerEnter, onPointerLeave)}
{...popoverProps}
id={popover.id}
open={popover.open}
Expand All@@ -162,14 +196,20 @@ export const PopoverContent: FC<PopoverContentProps> = ({
);
};

const modeProps = (popover: PopoverContextValue) => {
const modeProps = (
popover: PopoverContextValue,
externalOnPointerEnter: PointerEventHandler<HTMLDivElement> | undefined,
externalOnPointerLeave: PointerEventHandler<HTMLDivElement> | undefined,
) => {
if (popover.mode === "hover") {
return {
onPointerEnter: () => {
onPointerEnter: (event: PointerEvent<HTMLDivElement>) => {
popover.setOpen(true);
externalOnPointerEnter?.(event);
},
onPointerLeave: () => {
onPointerLeave: (event: PointerEvent<HTMLDivElement>) => {
popover.setOpen(false);
externalOnPointerLeave?.(event);
},
};
}
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp