diff --git a/site/.eslintrc.yaml b/site/.eslintrc.yaml
index 9fc59fce26123..78da4dd93fdb9 100644
--- a/site/.eslintrc.yaml
+++ b/site/.eslintrc.yaml
@@ -144,6 +144,11 @@ rules:
"You should use the native HTML elements as span, p, h1, h2, h3..."
- name: "@mui/material/Box"
message: "You should use a
instead"
+ - name: "@mui/material/styles"
+ importNames: ["Interpolation", "Theme", "useTheme"]
+ message: "Import from @emotion/react instead."
+ - name: "lodash"
+ message: "Import from lodash/ instead."
no-unused-vars: "off"
"object-curly-spacing": "off"
react-hooks/exhaustive-deps: warn
diff --git a/site/src/components/FullPageLayout/Sidebar.tsx b/site/src/components/FullPageLayout/Sidebar.tsx
index 46f867e4619a6..a1e9817250775 100644
--- a/site/src/components/FullPageLayout/Sidebar.tsx
+++ b/site/src/components/FullPageLayout/Sidebar.tsx
@@ -1,9 +1,9 @@
-import { Interpolation, Theme, useTheme } from "@mui/material/styles";
-import { ComponentProps, HTMLAttributes } from "react";
-import { Link, LinkProps } from "react-router-dom";
+import { type Interpolation, type Theme, useTheme } from "@emotion/react";
+import { type ComponentProps, type FC, type HTMLAttributes } from "react";
+import { Link, type LinkProps } from "react-router-dom";
import { TopbarIconButton } from "./Topbar";
-export const Sidebar = (props: HTMLAttributes) => {
+export const Sidebar: FC> = (props) => {
const theme = useTheme();
return (
) => {
);
};
-export const SidebarLink = (props: LinkProps) => {
+export const SidebarLink: FC = (props) => {
return ;
};
-export const SidebarItem = (
- props: HTMLAttributes & { isActive?: boolean },
-) => {
- const { isActive, ...buttonProps } = props;
+interface SidebarItemProps extends HTMLAttributes {
+ isActive?: boolean;
+}
+
+export const SidebarItem: FC = ({
+ isActive,
+ ...buttonProps
+}) => {
const theme = useTheme();
return (
@@ -49,7 +53,7 @@ export const SidebarItem = (
);
};
-export const SidebarCaption = (props: HTMLAttributes) => {
+export const SidebarCaption: FC> = (props) => {
return (
) => {
);
};
-export const SidebarIconButton = (
- props: { isActive: boolean } & ComponentProps,
-) => {
- const theme = useTheme();
+interface SidebarIconButton extends ComponentProps {
+ isActive: boolean;
+}
+export const SidebarIconButton: FC = (props) => {
return (
@@ -112,4 +103,19 @@ const styles = {
backgroundColor: theme.palette.action.hover,
},
}),
+
+ activeSidebarIconButton: (theme) => ({
+ opacity: 1,
+ position: "relative",
+ "&::before": {
+ content: '""',
+ position: "absolute",
+ left: 0,
+ top: 0,
+ bottom: 0,
+ width: 2,
+ backgroundColor: theme.palette.primary.main,
+ height: "100%",
+ },
+ }),
} satisfies Record>;
diff --git a/site/src/components/FullPageLayout/Topbar.tsx b/site/src/components/FullPageLayout/Topbar.tsx
index 27457faa473e3..217cacdb4ae7c 100644
--- a/site/src/components/FullPageLayout/Topbar.tsx
+++ b/site/src/components/FullPageLayout/Topbar.tsx
@@ -1,7 +1,7 @@
import { css } from "@emotion/css";
import Button, { ButtonProps } from "@mui/material/Button";
import IconButton, { IconButtonProps } from "@mui/material/IconButton";
-import { useTheme } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
import { AvatarProps, ExternalAvatar } from "components/Avatar/Avatar";
import {
type FC,
diff --git a/site/src/components/Menu/Search.tsx b/site/src/components/Menu/Search.tsx
index 833f247fb3229..bb5cede3b5db8 100644
--- a/site/src/components/Menu/Search.tsx
+++ b/site/src/components/Menu/Search.tsx
@@ -1,9 +1,14 @@
import SearchOutlined from "@mui/icons-material/SearchOutlined";
// eslint-disable-next-line no-restricted-imports -- use it to have the component prop
-import Box, { BoxProps } from "@mui/material/Box";
-import { Interpolation, Theme, useTheme } from "@mui/material/styles";
+import Box, { type BoxProps } from "@mui/material/Box";
+import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import visuallyHidden from "@mui/utils/visuallyHidden";
-import { FC, HTMLAttributes, InputHTMLAttributes, forwardRef } from "react";
+import {
+ type FC,
+ type HTMLAttributes,
+ type InputHTMLAttributes,
+ forwardRef,
+} from "react";
export const Search = forwardRef(
({ children, ...boxProps }, ref) => {
diff --git a/site/src/hooks/usePaginatedQuery.ts b/site/src/hooks/usePaginatedQuery.ts
index 5be2b5e3b6261..bc1844e2e34f0 100644
--- a/site/src/hooks/usePaginatedQuery.ts
+++ b/site/src/hooks/usePaginatedQuery.ts
@@ -1,7 +1,7 @@
import { useEffect } from "react";
import { useEffectEvent } from "./hookPolyfills";
import { type SetURLSearchParams, useSearchParams } from "react-router-dom";
-import { clamp } from "lodash";
+import clamp from "lodash/clamp";
import {
type QueryFunctionContext,
diff --git a/site/src/pages/HealthPage/Content.tsx b/site/src/pages/HealthPage/Content.tsx
index a304205c58fe6..a55a5f6b515ed 100644
--- a/site/src/pages/HealthPage/Content.tsx
+++ b/site/src/pages/HealthPage/Content.tsx
@@ -14,7 +14,7 @@ import { css } from "@emotion/css";
import DoNotDisturbOnOutlined from "@mui/icons-material/DoNotDisturbOnOutlined";
import { HealthMessage, HealthSeverity } from "api/typesGenerated";
import Link from "@mui/material/Link";
-import { useTheme } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
const CONTENT_PADDING = 36;
diff --git a/site/src/pages/HealthPage/DERPPage.tsx b/site/src/pages/HealthPage/DERPPage.tsx
index c3771ad280b77..fc6c4e14f84d5 100644
--- a/site/src/pages/HealthPage/DERPPage.tsx
+++ b/site/src/pages/HealthPage/DERPPage.tsx
@@ -1,4 +1,16 @@
import { Link, useOutletContext } from "react-router-dom";
+import type {
+ HealthMessage,
+ HealthSeverity,
+ HealthcheckReport,
+} from "api/typesGenerated";
+import Button from "@mui/material/Button";
+import LocationOnOutlined from "@mui/icons-material/LocationOnOutlined";
+import { Alert } from "components/Alert/Alert";
+import { Helmet } from "react-helmet-async";
+import { pageTitle } from "utils/page";
+import { useTheme } from "@emotion/react";
+import { type FC } from "react";
import {
Header,
HeaderTitle,
@@ -9,19 +21,8 @@ import {
Logs,
HealthyDot,
} from "./Content";
-import {
- HealthMessage,
- HealthSeverity,
- HealthcheckReport,
-} from "api/typesGenerated";
-import Button from "@mui/material/Button";
-import LocationOnOutlined from "@mui/icons-material/LocationOnOutlined";
-import { healthyColor } from "./healthyColor";
-import { Alert } from "components/Alert/Alert";
-import { Helmet } from "react-helmet-async";
-import { pageTitle } from "utils/page";
-import { useTheme } from "@mui/material/styles";
import { DismissWarningButton } from "./DismissWarningButton";
+import { healthyColor } from "./healthyColor";
const flags = [
"UDP",
@@ -38,7 +39,7 @@ const flags = [
"PCP",
];
-export const DERPPage = () => {
+export const DERPPage: FC = () => {
const { derp } = useOutletContext();
const { netcheck, regions, netcheck_logs: logs } = derp;
const theme = useTheme();
diff --git a/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx b/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx
index b7e2b0f04d843..4788703ebb77b 100644
--- a/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx
+++ b/site/src/pages/HealthPage/ProvisionerDaemonsPage.tsx
@@ -9,23 +9,22 @@ import {
} from "./Content";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
-import { useTheme } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
import { DismissWarningButton } from "./DismissWarningButton";
import { Alert } from "components/Alert/Alert";
-import { HealthcheckReport } from "api/typesGenerated";
+import type { HealthcheckReport } from "api/typesGenerated";
import { createDayString } from "utils/createDayString";
-
+import { type FC } from "react";
import { useOutletContext } from "react-router-dom";
import Business from "@mui/icons-material/Business";
import Person from "@mui/icons-material/Person";
import SwapHoriz from "@mui/icons-material/SwapHoriz";
import Tooltip from "@mui/material/Tooltip";
import Sell from "@mui/icons-material/Sell";
-import { FC } from "react";
import CloseIcon from "@mui/icons-material/Close";
import IconButton from "@mui/material/IconButton";
-export const ProvisionerDaemonsPage = () => {
+export const ProvisionerDaemonsPage: FC = () => {
const healthStatus = useOutletContext();
const { provisioner_daemons: daemons } = healthStatus;
const theme = useTheme();
diff --git a/site/src/pages/HealthPage/WebsocketPage.tsx b/site/src/pages/HealthPage/WebsocketPage.tsx
index 150432c311646..45376b6c87549 100644
--- a/site/src/pages/HealthPage/WebsocketPage.tsx
+++ b/site/src/pages/HealthPage/WebsocketPage.tsx
@@ -10,7 +10,7 @@ import {
import { HealthcheckReport } from "api/typesGenerated";
import CodeOutlined from "@mui/icons-material/CodeOutlined";
import Tooltip from "@mui/material/Tooltip";
-import { useTheme } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
import { MONOSPACE_FONT_FAMILY } from "theme/constants";
import { Alert } from "components/Alert/Alert";
import { pageTitle } from "utils/page";
diff --git a/site/src/pages/HealthPage/WorkspaceProxyPage.tsx b/site/src/pages/HealthPage/WorkspaceProxyPage.tsx
index d3798052dc2e8..c2058cc364b7d 100644
--- a/site/src/pages/HealthPage/WorkspaceProxyPage.tsx
+++ b/site/src/pages/HealthPage/WorkspaceProxyPage.tsx
@@ -8,18 +8,19 @@ import {
Main,
Pill,
} from "./Content";
-import { HealthcheckReport } from "api/typesGenerated";
-import { useTheme } from "@mui/material/styles";
+import type { HealthcheckReport } from "api/typesGenerated";
+import { useTheme } from "@emotion/react";
import { createDayString } from "utils/createDayString";
import PublicOutlined from "@mui/icons-material/PublicOutlined";
import Tooltip from "@mui/material/Tooltip";
import TagOutlined from "@mui/icons-material/TagOutlined";
import { Alert } from "components/Alert/Alert";
+import { type FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { DismissWarningButton } from "./DismissWarningButton";
-export const WorkspaceProxyPage = () => {
+export const WorkspaceProxyPage: FC = () => {
const healthStatus = useOutletContext();
const { workspace_proxy } = healthStatus;
const { regions } = workspace_proxy.workspace_proxies;
diff --git a/site/src/pages/HealthPage/healthyColor.ts b/site/src/pages/HealthPage/healthyColor.ts
index d14bc808c0dca..97d69b08d559b 100644
--- a/site/src/pages/HealthPage/healthyColor.ts
+++ b/site/src/pages/HealthPage/healthyColor.ts
@@ -1,5 +1,5 @@
-import { Theme } from "@mui/material/styles";
-import { HealthSeverity } from "api/typesGenerated";
+import type { Theme } from "@emotion/react";
+import type { HealthSeverity } from "api/typesGenerated";
export const healthyColor = (theme: Theme, severity: HealthSeverity) => {
switch (severity) {
diff --git a/site/src/pages/WorkspacePage/ResourcesSidebar.tsx b/site/src/pages/WorkspacePage/ResourcesSidebar.tsx
index 4cf6d9c2ac971..c71d18f0d0374 100644
--- a/site/src/pages/WorkspacePage/ResourcesSidebar.tsx
+++ b/site/src/pages/WorkspacePage/ResourcesSidebar.tsx
@@ -1,7 +1,7 @@
-import { Interpolation, Theme } from "@emotion/react";
+import { type Interpolation, type Theme, useTheme } from "@emotion/react";
import Skeleton from "@mui/material/Skeleton";
-import { useTheme } from "@mui/material/styles";
-import { WorkspaceResource } from "api/typesGenerated";
+import { type FC } from "react";
+import type { WorkspaceResource } from "api/typesGenerated";
import {
Sidebar,
SidebarCaption,
@@ -16,9 +16,13 @@ type ResourcesSidebarProps = {
isSelected: (resource: WorkspaceResource) => boolean;
};
-export const ResourcesSidebar = (props: ResourcesSidebarProps) => {
+export const ResourcesSidebar: FC = ({
+ failed,
+ onChange,
+ isSelected,
+ resources,
+}) => {
const theme = useTheme();
- const { failed, onChange, isSelected, resources } = props;
return (
@@ -83,7 +87,7 @@ export const ResourcesSidebar = (props: ResourcesSidebarProps) => {
);
};
-export const ResourceSidebarItemSkeleton = () => {
+export const ResourceSidebarItemSkeleton: FC = () => {
return (
diff --git a/site/src/pages/WorkspacePage/ResourcesSidebarContent.tsx b/site/src/pages/WorkspacePage/ResourcesSidebarContent.tsx
index ebc43cf73dbaf..5db9494521b4f 100644
--- a/site/src/pages/WorkspacePage/ResourcesSidebarContent.tsx
+++ b/site/src/pages/WorkspacePage/ResourcesSidebarContent.tsx
@@ -1,4 +1,4 @@
-import { useTheme } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
import { Workspace } from "api/typesGenerated";
import { SidebarLink, SidebarCaption } from "components/FullPageLayout/Sidebar";
diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx
index 31efc004c2aa7..6b999fc093b8f 100644
--- a/site/src/pages/WorkspacePage/Workspace.tsx
+++ b/site/src/pages/WorkspacePage/Workspace.tsx
@@ -15,7 +15,7 @@ import { WorkspaceDeletedBanner } from "./WorkspaceDeletedBanner";
import { WorkspaceTopbar } from "./WorkspaceTopbar";
import { HistorySidebar } from "./HistorySidebar";
import HistoryOutlined from "@mui/icons-material/HistoryOutlined";
-import { useTheme } from "@mui/material/styles";
+import { useTheme } from "@emotion/react";
import { SidebarIconButton } from "components/FullPageLayout/Sidebar";
import HubOutlined from "@mui/icons-material/HubOutlined";
import { ResourcesSidebar } from "./ResourcesSidebar";
diff --git a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx
index 4613f6d489c84..1241ce769b417 100644
--- a/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx
+++ b/site/src/pages/WorkspacePage/WorkspaceScheduleControls.tsx
@@ -16,7 +16,6 @@ import IconButton from "@mui/material/IconButton";
import RemoveIcon from "@mui/icons-material/RemoveOutlined";
import AddIcon from "@mui/icons-material/AddOutlined";
import Tooltip from "@mui/material/Tooltip";
-import _ from "lodash";
import { getErrorMessage } from "api/errors";
import {
updateDeadline,
diff --git a/site/src/theme/index.ts b/site/src/theme/index.ts
index aa8283d779f69..49bd61cef3dae 100644
--- a/site/src/theme/index.ts
+++ b/site/src/theme/index.ts
@@ -1,3 +1,4 @@
+// eslint-disable-next-line no-restricted-imports -- 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 { NewTheme } from "./experimental";