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

fix(site): standardize headers for Admin Settings page#16911

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 20 commits intomainfrommes/headers-fix-b
Apr 1, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
20 commits
Select commitHold shift + click to select a range
de69ee3
fix: migrate deployment workspace proxies page to new header style
ParkreinerMar 11, 2025
a1da1c1
fix: update other proxy page to use proper render key values
ParkreinerMar 11, 2025
e5d581b
fix: make secondary styling more obvious
ParkreinerMar 11, 2025
9b4928b
refactor: move SettingsHeader to Tailwind
ParkreinerMar 11, 2025
a35a9ae
fix: resolve header level bugs
ParkreinerMar 11, 2025
9872c5d
fix: constrain max width
ParkreinerMar 11, 2025
1fe0022
fix: apply formatting
ParkreinerMar 13, 2025
7d0903a
fix: update HTML semantics
ParkreinerMar 13, 2025
a512bf5
fix: flush semantics fix
ParkreinerMar 13, 2025
734f59d
fix: update leading
ParkreinerMar 13, 2025
49b9dfe
fix: resolve styling edge case for beta badge
ParkreinerMar 13, 2025
840d6df
fix: set correct font size
ParkreinerMar 13, 2025
f169c4e
fix: remove redundant class
ParkreinerMar 17, 2025
2325982
Merge branch 'main' into mes/headers-fix-b
ParkreinerMar 28, 2025
99c2dbc
refactor: switch to CVA
ParkreinerMar 28, 2025
50eddd0
refactor: update API design of SettingsHeader
ParkreinerMar 28, 2025
2b77784
chore: add first-ever stories for SettingsHeader
ParkreinerMar 28, 2025
e2523cb
fix: make header more responsive
ParkreinerMar 28, 2025
7f53666
fix: format
ParkreinerMar 28, 2025
daf6da9
fix: update beta badge styling for Notifications page
ParkreinerMar 28, 2025
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
83 changes: 83 additions & 0 deletionssite/src/components/SettingsHeader/SettingsHeader.stories.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
import type { Meta, StoryObj } from "@storybook/react";
import { docs } from "utils/docs";
import {
SettingsHeader,
SettingsHeaderDescription,
SettingsHeaderDocsLink,
SettingsHeaderTitle,
} from "./SettingsHeader";

const meta: Meta<typeof SettingsHeader> = {
title: "components/SettingsHeader",
component: SettingsHeader,
};

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

export const PrimaryHeaderOnly: Story = {
args: {
children: <SettingsHeaderTitle>This is a header</SettingsHeaderTitle>,
},
};

export const PrimaryHeaderWithDescription: Story = {
args: {
children: (
<>
<SettingsHeaderTitle>Another primary header</SettingsHeaderTitle>
<SettingsHeaderDescription>
This description can be any ReactNode. This provides more options for
composition.
</SettingsHeaderDescription>
</>
),
},
};

export const PrimaryHeaderWithDescriptionAndDocsLink: Story = {
args: {
children: (
<>
<SettingsHeaderTitle>Another primary header</SettingsHeaderTitle>
<SettingsHeaderDescription>
This description can be any ReactNode. This provides more options for
composition.
</SettingsHeaderDescription>
</>
),
actions: <SettingsHeaderDocsLink href={docs("/admin/external-auth")} />,
},
};

export const SecondaryHeaderWithDescription: Story = {
args: {
children: (
<>
<SettingsHeaderTitle level="h6" hierarchy="secondary">
This is a secondary header.
</SettingsHeaderTitle>
<SettingsHeaderDescription>
The header's styling is completely independent of its semantics. Both
can be adjusted independently to help avoid invalid HTML.
</SettingsHeaderDescription>
</>
),
},
};

export const SecondaryHeaderWithDescriptionAndDocsLink: Story = {
args: {
children: (
<>
<SettingsHeaderTitle level="h3" hierarchy="secondary">
Another secondary header
</SettingsHeaderTitle>
<SettingsHeaderDescription>
Nothing to add, really.
</SettingsHeaderDescription>
</>
),
actions: <SettingsHeaderDocsLink href={docs("/admin/external-auth")} />,
},
};
161 changes: 97 additions & 64 deletionssite/src/components/SettingsHeader/SettingsHeader.tsx
View file
Open in desktop
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add a storybook for it covering the new variants?

Parkreiner reacted with thumbs up emoji
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,107 @@
import{useTheme}from"@emotion/react";
import{typeVariantProps,cva}from"class-variance-authority";
import{Button}from"components/Button/Button";
import{Stack}from"components/Stack/Stack";
import{SquareArrowOutUpRightIcon}from"lucide-react";
importtype{FC,ReactNode}from"react";
importtype{FC,PropsWithChildren,ReactNode}from"react";
import{cn}from"utils/cn";

interfaceHeaderProps{
title:ReactNode;
description?:ReactNode;
secondary?:boolean;
docsHref?:string;
tooltip?:ReactNode;
}

exportconstSettingsHeader:FC<HeaderProps>=({
title,
description,
docsHref,
secondary,
tooltip,
typeSettingsHeaderProps=Readonly<
PropsWithChildren<{
actions?:ReactNode;
className?:string;
}>
>;
exportconstSettingsHeader:FC<SettingsHeaderProps>=({
children,
actions,
className,
})=>{
consttheme=useTheme();
return(
<hgroupclassName="flex flex-col justify-between items-start gap-2 pb-6 sm:flex-row">
{/*
* The text-sm class is only meant to adjust the font size of
* SettingsDescription, but we need to apply it here. That way,
* text-sm combines with the max-w-prose class and makes sure
* we have a predictable max width for the header + description by
* default.
*/}
<divclassName={cn("text-sm max-w-prose",className)}>{children}</div>
{actions}
</hgroup>
);
};

typeSettingsHeaderDocsLinkProps=Readonly<
PropsWithChildren<{href:string}>
>;
exportconstSettingsHeaderDocsLink:FC<SettingsHeaderDocsLinkProps>=({
href,
children="Read the docs",
})=>{
return(
<StackalignItems="baseline"direction="row"justifyContent="space-between">
<divcss={{maxWidth:420,marginBottom:24}}>
<Stackdirection="row"spacing={1}alignItems="center">
<h1
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This was a big accessibility/HTML validity issue. We were using this component multiple times on the same page, but only oneh1 is allowed to be on a page, total

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This is why I decoupled thesecondary boolean into a styling prop and a semantic prop. There may be cases when we need to change the heading level to make the HTML valid, even though the styling should be exactly the same

css={[
{
fontSize:32,
fontWeight:700,
display:"flex",
alignItems:"baseline",
lineHeight:"initial",
margin:0,
marginBottom:4,
gap:8,
},
secondary&&{
fontSize:24,
fontWeight:500,
},
]}
>
{title}
</h1>
{tooltip}
</Stack>
<ButtonasChildvariant="outline">
<ahref={href}target="_blank"rel="noreferrer">
<SquareArrowOutUpRightIcon/>
{children}
<spanclassName="sr-only"> (link opens in new tab)</span>
</a>
</Button>
);
};

{description&&(
<span
css={{
fontSize:14,
color:theme.palette.text.secondary,
lineHeight:"160%",
}}
>
{description}
</span>
)}
</div>
consttitleVariants=cva("m-0 pb-1 flex items-center gap-2 leading-tight",{
variants:{
hierarchy:{
primary:"text-3xl font-bold",
secondary:"text-2xl font-medium",
},
},
defaultVariants:{
hierarchy:"primary",
},
});
typeSettingsHeaderTitleProps=Readonly<
PropsWithChildren<
VariantProps<typeoftitleVariants>&{
level?: `h${1|2|3|4|5|6}`;
tooltip?:ReactNode;
className?:string;
}
>
>;
exportconstSettingsHeaderTitle:FC<SettingsHeaderTitleProps>=({
children,
tooltip,
className,
level="h1",
hierarchy="primary",
})=>{
// Explicitly not using Radix's Slot component, because we don't want to
// allow any arbitrary element to be composed into this. We specifically
// only want to allow the six HTML headers. Anything else will likely result
// in invalid markup
Comment on lines +78 to +81
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Added a comment to call out why the code isn't usingSlot

BrunoQuaresma reacted with thumbs up emoji
constTitle=level;
return(
<divclassName="flex flex-row gap-2 items-center">
<TitleclassName={cn(titleVariants({ hierarchy}),className)}>
{children}
</Title>
{tooltip}
</div>
);
};

{docsHref&&(
<ButtonasChildvariant="outline">
<ahref={docsHref}target="_blank"rel="noreferrer">
<SquareArrowOutUpRightIcon/>
Read the docs
</a>
</Button>
)}
</Stack>
typeSettingsHeaderDescriptionProps=Readonly<
PropsWithChildren<{
className?:string;
}>
>;
exportconstSettingsHeaderDescription:FC<SettingsHeaderDescriptionProps>=({
children,
className,
})=>{
return(
<pclassName={cn("m-0 text-content-secondary leading-relaxed",className)}>
{children}
</p>
);
};
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,11 @@ import {
} from "components/Badges/Badges";
import { Button } from "components/Button/Button";
import { PopoverPaywall } from "components/Paywall/PopoverPaywall";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import {
SettingsHeader,
SettingsHeaderDescription,
SettingsHeaderTitle,
} from "components/SettingsHeader/SettingsHeader";
import {
Popover,
PopoverContent,
Expand DownExpand Up@@ -54,10 +58,12 @@ export const AppearanceSettingsPageView: FC<

return (
<>
<SettingsHeader
title="Appearance"
description="Customize the look and feel of your Coder deployment."
/>
<SettingsHeader>
<SettingsHeaderTitle>Appearance</SettingsHeaderTitle>
<SettingsHeaderDescription>
Customize the look and feel of your Coder deployment.
</SettingsHeaderDescription>
</SettingsHeader>

<Badges>
<Popover mode="hover">
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,12 @@ import TableRow from "@mui/material/TableRow";
import type { DeploymentValues, ExternalAuthConfig } from "api/typesGenerated";
import { Alert } from "components/Alert/Alert";
import { PremiumBadge } from "components/Badges/Badges";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import {
SettingsHeader,
SettingsHeaderDescription,
SettingsHeaderDocsLink,
SettingsHeaderTitle,
} from "components/SettingsHeader/SettingsHeader";
import type { FC } from "react";
import { docs } from "utils/docs";

Expand All@@ -22,10 +27,14 @@ export const ExternalAuthSettingsPageView: FC<
return (
<>
<SettingsHeader
title="External Authentication"
description="Coder integrates with GitHub, GitLab, BitBucket, Azure Repos, and OpenID Connect to authenticate developers with external services."
docsHref={docs("/admin/external-auth")}
/>
actions={<SettingsHeaderDocsLink href={docs("/admin/external-auth")} />}
>
<SettingsHeaderTitle>External Authentication</SettingsHeaderTitle>
<SettingsHeaderDescription>
Coder integrates with GitHub, GitLab, BitBucket, Azure Repos, and
OpenID Connect to authenticate developers with external services.
</SettingsHeaderDescription>
</SettingsHeader>

<video
autoPlay
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -3,7 +3,11 @@ import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Button } from "components/Button/Button";
import { FileUpload } from "components/FileUpload/FileUpload";
import { displayError } from "components/GlobalSnackbar/utils";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import {
SettingsHeader,
SettingsHeaderDescription,
SettingsHeaderTitle,
} from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import { ChevronLeftIcon } from "lucide-react";
import type { FC } from "react";
Expand DownExpand Up@@ -50,10 +54,13 @@ export const AddNewLicensePageView: FC<AddNewLicenseProps> = ({
direction="row"
justifyContent="space-between"
>
<SettingsHeader
title="Add a license"
description="Get access to high availability, RBAC, quotas, and more."
/>
<SettingsHeader>
<SettingsHeaderTitle>Add a license</SettingsHeaderTitle>
<SettingsHeaderDescription>
Get access to high availability, RBAC, quotas, and more.
</SettingsHeaderDescription>
</SettingsHeader>

<Button asChild variant="outline">
<RouterLink to="/deployment/licenses">
<ChevronLeftIcon />
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,7 +8,11 @@ import Skeleton from "@mui/material/Skeleton";
import Tooltip from "@mui/material/Tooltip";
import type { GetLicensesResponse } from "api/api";
import type { UserStatusChangeCount } from "api/typesGenerated";
import { SettingsHeader } from "components/SettingsHeader/SettingsHeader";
import {
SettingsHeader,
SettingsHeaderDescription,
SettingsHeaderTitle,
} from "components/SettingsHeader/SettingsHeader";
import { Stack } from "components/Stack/Stack";
import { useWindowSize } from "hooks/useWindowSize";
import type { FC } from "react";
Expand DownExpand Up@@ -60,10 +64,12 @@ const LicensesSettingsPageView: FC<Props> = ({
direction="row"
justifyContent="space-between"
>
<SettingsHeader
title="Licenses"
description="Manage licenses to unlock Premium features."
/>
<SettingsHeader>
<SettingsHeaderTitle>Licenses</SettingsHeaderTitle>
<SettingsHeaderDescription>
Manage licenses to unlock Premium features.
</SettingsHeaderDescription>
</SettingsHeader>

<Stack direction="row" spacing={2}>
<Button
Expand Down
Loading
Loading

[8]ページ先頭

©2009-2025 Movatter.jp