Skip to content

feat(site): improve icon compatibility across themes #11457

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Next Next commit
feat: external icon settings
  • Loading branch information
aslilac committed Jan 5, 2024
commit 0d4ad6bbeb95109f91285df1c5de5f4939b603b8
2 changes: 1 addition & 1 deletion examples/templates/aws-devcontainer/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
display_name: AWS EC2 (Devcontainer)
description: Provision AWS EC2 VMs with a devcontainer as Coder workspaces
icon: ../../../site/static/icon/aws.png
icon: ../../../site/static/icon/aws.svg
maintainer_github: coder
verified: true
tags: [vm, linux, aws, persistent, devcontainer]
Expand Down
2 changes: 1 addition & 1 deletion examples/templates/aws-linux/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
display_name: AWS EC2 (Linux)
description: Provision AWS EC2 VMs as Coder workspaces
icon: ../../../site/static/icon/aws.png
icon: ../../../site/static/icon/aws.svg
maintainer_github: coder
verified: true
tags: [vm, linux, aws, persistent-vm]
Expand Down
2 changes: 1 addition & 1 deletion examples/templates/aws-windows/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
display_name: AWS EC2 (Windows)
description: Provision AWS EC2 VMs as Coder workspaces
icon: ../../../site/static/icon/aws.png
icon: ../../../site/static/icon/aws.svg
maintainer_github: coder
verified: true
tags: [vm, windows, aws]
Expand Down
8 changes: 1 addition & 7 deletions site/src/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import WorkspacesPage from "./pages/WorkspacesPage/WorkspacesPage";
import UserSettingsLayout from "./pages/UserSettingsPage/Layout";
import { TemplateSettingsLayout } from "./pages/TemplateSettingsPage/TemplateSettingsLayout";
import { WorkspaceSettingsLayout } from "./pages/WorkspaceSettingsPage/WorkspaceSettingsLayout";
import { ThemeOverride } from "contexts/ThemeProvider";
import themes from "theme";

// Lazy load pages
// - Pages that are secondary, not in the main navigation or not usually accessed
Expand Down Expand Up @@ -414,11 +412,7 @@ export const AppRouter: FC = () => {
/>
<Route
path="/:username/:workspace/terminal"
element={
<ThemeOverride theme={themes.dark}>
<TerminalPage />
</ThemeOverride>
}
element={<TerminalPage />}
/>
<Route path="/cli-auth" element={<CliAuthenticationPage />} />
<Route path="/icons" element={<IconsPage />} />
Expand Down
2 changes: 1 addition & 1 deletion site/src/__mocks__/react-markdown.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, PropsWithChildren } from "react";

const ReactMarkdown: FC<PropsWithChildren<unknown>> = ({ children }) => {
const ReactMarkdown: FC<PropsWithChildren> = ({ children }) => {
return <div data-testid="markdown">{children}</div>;
};

Expand Down
1 change: 1 addition & 0 deletions site/src/api/queries/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export const me = (): UseQueryOptions<User> & {
queryKey: meKey,
initialData: initialUserData,
queryFn: API.getAuthenticatedUser,
refetchOnWindowFocus: true,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export const Options: Story = {
name: "Third option",
value: "third_option",
description: "",
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
},
],
}),
Expand Down Expand Up @@ -138,7 +138,7 @@ Very big.

> Wow, that description is straight up large. –Some guy, probably
`,
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
},
],
}),
Expand Down
17 changes: 17 additions & 0 deletions site/src/pages/IconsPage/IconsPage.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Meta, StoryObj } from "@storybook/react";
import { chromatic } from "testHelpers/chromatic";
import { IconsPage } from "./IconsPage";

const meta: Meta<typeof IconsPage> = {
title: "pages/IconsPage",
parameters: { chromatic },
component: IconsPage,
args: {},
};

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

const Example: Story = {};

export { Example as IconsPage };
24 changes: 17 additions & 7 deletions site/src/pages/IconsPage/IconsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ import {
} from "components/PageHeader/PageHeader";
import { Stack } from "components/Stack/Stack";
import icons from "theme/icons.json";
import {
defaultParametersForBuiltinIcons,
parseImageParameters,
} from "theme/externalImages";
import { pageTitle } from "utils/page";

const iconsWithoutSuffix = icons.map((icon) => icon.split(".")[0]);
Expand Down Expand Up @@ -163,13 +167,19 @@ export const IconsPage: FC = () => {
<img
alt={icon.url}
src={icon.url}
css={{
width: 60,
height: 60,
objectFit: "contain",
pointerEvents: "none",
padding: 12,
}}
css={[
{
width: 60,
height: 60,
objectFit: "contain",
pointerEvents: "none",
padding: 12,
},
parseImageParameters(
theme.externalImages,
defaultParametersForBuiltinIcons.get(icon.url) ?? "",
),
]}
/>
<figcaption
css={{
Expand Down
6 changes: 4 additions & 2 deletions site/src/pages/TerminalPage/TerminalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import {
PopoverContent,
PopoverTrigger,
} from "components/Popover/Popover";
import { ThemeOverride } from "contexts/ThemeProvider";
import themes from "theme";

export const Language = {
workspaceErrorMessagePrefix: "Unable to fetch workspace: ",
Expand Down Expand Up @@ -293,7 +295,7 @@ const TerminalPage: FC = () => {
]);

return (
<>
<ThemeOverride theme={themes.dark}>
<Helmet>
<title>
{workspace.data
Expand All @@ -314,7 +316,7 @@ const TerminalPage: FC = () => {
<BottomBar proxy={selectedProxy} latency={latency.latencyMS} />
)}
</div>
</>
</ThemeOverride>
);
};

Expand Down
4 changes: 2 additions & 2 deletions site/src/testHelpers/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2244,7 +2244,7 @@ export const MockTemplateExample: TypesGen.TemplateExample = {
description: "Get started with Linux development on AWS ECS.",
markdown:
"\n# aws-ecs\n\nThis is a sample template for running a Coder workspace on ECS. It assumes there\nis a pre-existing ECS cluster with EC2-based compute to host the workspace.\n\n## Architecture\n\nThis workspace is built using the following AWS resources:\n\n- Task definition - the container definition, includes the image, command, volume(s)\n- ECS service - manages the task definition\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n",
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
tags: ["aws", "cloud"],
};

Expand All @@ -2255,7 +2255,7 @@ export const MockTemplateExample2: TypesGen.TemplateExample = {
description: "Get started with Linux development on AWS EC2.",
markdown:
'\n# aws-linux\n\nTo get started, run `coder templates init`. When prompted, select this template.\nFollow the on-screen instructions to proceed.\n\n## Authentication\n\nThis template assumes that coderd is run in an environment that is authenticated\nwith AWS. For example, run `aws configure import` to import credentials on the\nsystem and user running coderd. For other ways to authenticate [consult the\nTerraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration).\n\n## Required permissions / policy\n\nThe following sample policy allows Coder to create EC2 instances and modify\ninstances provisioned by Coder:\n\n```json\n{\n "Version": "2012-10-17",\n "Statement": [\n {\n "Sid": "VisualEditor0",\n "Effect": "Allow",\n "Action": [\n "ec2:GetDefaultCreditSpecification",\n "ec2:DescribeIamInstanceProfileAssociations",\n "ec2:DescribeTags",\n "ec2:CreateTags",\n "ec2:RunInstances",\n "ec2:DescribeInstanceCreditSpecifications",\n "ec2:DescribeImages",\n "ec2:ModifyDefaultCreditSpecification",\n "ec2:DescribeVolumes"\n ],\n "Resource": "*"\n },\n {\n "Sid": "CoderResources",\n "Effect": "Allow",\n "Action": [\n "ec2:DescribeInstances",\n "ec2:DescribeInstanceAttribute",\n "ec2:UnmonitorInstances",\n "ec2:TerminateInstances",\n "ec2:StartInstances",\n "ec2:StopInstances",\n "ec2:DeleteTags",\n "ec2:MonitorInstances",\n "ec2:CreateTags",\n "ec2:RunInstances",\n "ec2:ModifyInstanceAttribute",\n "ec2:ModifyInstanceCreditSpecification"\n ],\n "Resource": "arn:aws:ec2:*:*:instance/*",\n "Condition": {\n "StringEquals": {\n "aws:ResourceTag/Coder_Provisioned": "true"\n }\n }\n }\n ]\n}\n```\n\n## code-server\n\n`code-server` is installed via the `startup_script` argument in the `coder_agent`\nresource block. The `coder_app` resource is defined to access `code-server` through\nthe dashboard UI over `localhost:13337`.\n',
icon: "/icon/aws.png",
icon: "/icon/aws.svg",
tags: ["aws", "cloud"],
};

Expand Down
2 changes: 2 additions & 0 deletions site/src/theme/dark/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
import { forDarkThemes } from "../externalImages";

export default {
...muiTheme,
experimental,
monaco,
externalImages: forDarkThemes,
};
2 changes: 2 additions & 0 deletions site/src/theme/darkBlue/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import experimental from "./experimental";
import monaco from "./monaco";
import muiTheme from "./mui";
import { forDarkThemes } from "../externalImages";

export default {
...muiTheme,
experimental,
monaco,
externalImages: forDarkThemes,
};
85 changes: 85 additions & 0 deletions site/src/theme/externalImages.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import {
forDarkThemes,
forLightThemes,
getExternalImageStylesFromUrl,
parseImageParameters,
} from "./externalImages";

describe("externalImage parameters", () => {
test("default parameters", () => {
// Correctly selects default
const widgetsStyles = getExternalImageStylesFromUrl(
forDarkThemes,
"/icon/widgets.svg",
);
expect(widgetsStyles).toBe(forDarkThemes.monochrome);

// Allows overrides
const overrideStyles = getExternalImageStylesFromUrl(
forDarkThemes,
"/icon/widgets.svg?fullcolor",
);
expect(overrideStyles).toBe(forDarkThemes.fullcolor);

// Not actually a built-in
const someoneElsesWidgetsStyles = getExternalImageStylesFromUrl(
forDarkThemes,
"https://example.com/icon/widgets.svg",
);
expect(someoneElsesWidgetsStyles).toBeUndefined();
});

test("blackWithColor brightness", () => {
const tryCase = (params: string) =>
parseImageParameters(forDarkThemes, params);

const withDecimalValue = tryCase("?blackWithColor&brightness=1.5");
expect(withDecimalValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(1.5)",
);

const withPercentageValue = tryCase("?blackWithColor&brightness=150%");
expect(withPercentageValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(150%)",
);

// Sketchy `brightness` value will be ignored.
const niceTry = tryCase(
"?blackWithColor&brightness=</style><script>alert('leet hacking');</script>",
);
expect(niceTry?.filter).toBe("invert(1) hue-rotate(180deg)");

const withLightTheme = parseImageParameters(
forLightThemes,
"?blackWithColor&brightness=1.5",
);
expect(withLightTheme).toBeUndefined();
});

test("whiteWithColor brightness", () => {
const tryCase = (params: string) =>
parseImageParameters(forLightThemes, params);

const withDecimalValue = tryCase("?whiteWithColor&brightness=1.5");
expect(withDecimalValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(1.5)",
);

const withPercentageValue = tryCase("?whiteWithColor&brightness=150%");
expect(withPercentageValue?.filter).toBe(
"invert(1) hue-rotate(180deg) brightness(150%)",
);

// Sketchy `brightness` value will be ignored.
const niceTry = tryCase(
"?whiteWithColor&brightness=</style><script>alert('leet hacking');</script>",
);
expect(niceTry?.filter).toBe("invert(1) hue-rotate(180deg)");

const withDarkTheme = parseImageParameters(
forDarkThemes,
"?whiteWithColor&brightness=1.5",
);
expect(withDarkTheme).toBeUndefined();
});
});
Loading