Compare commits

...
Author SHA1 Message Date
Reece 082bcc34c2 refactor(shared): extract FileDocIcon + CloudStorageIcons into @shared with stories
Move two zero-coupling icon components out of the editor into the shared SUI
library (frontend/shared/components), export them from the barrel, and add
Storybook stories. Editor consumers now import them from @shared instead of
@app. Both are fully self-contained (pure SVG, no app context, inline colours /
caller-supplied className), so the move is mechanical and nothing else changes.

First slice of an incremental extraction; it establishes the move + re-point +
Storybook pattern to repeat for further components.
2026-06-17 15:38:53 +01:00
9 changed files with 98 additions and 5 deletions
@@ -10,7 +10,7 @@ import { useFileActionIcons } from "@app/hooks/useFileActionIcons";
import { useAppConfig } from "@app/contexts/AppConfigContext";
import { useIsMobile } from "@app/hooks/useIsMobile";
import MobileUploadModal from "@app/components/shared/MobileUploadModal";
import { GoogleDriveIcon } from "@app/components/shared/CloudStorageIcons";
import { GoogleDriveIcon } from "@shared/components/CloudStorageIcons";
interface FileSourceButtonsProps {
horizontal?: boolean;
@@ -26,7 +26,7 @@ import {
useIndexedDBRevision,
} from "@app/contexts/IndexedDBContext";
import { accountService } from "@app/services/accountService";
import { GoogleDriveIcon } from "@app/components/shared/CloudStorageIcons";
import { GoogleDriveIcon } from "@shared/components/CloudStorageIcons";
import { Wordmark } from "@app/components/shared/Wordmark";
import type { StirlingFileStub } from "@app/types/fileContext";
import MenuIcon from "@mui/icons-material/Menu";
@@ -6,7 +6,7 @@ import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined";
import VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined";
import ShieldOutlinedIcon from "@mui/icons-material/ShieldOutlined";
import type { FileId } from "@app/types/file";
import { FileDocIcon } from "@app/components/shared/FileDocIcon";
import { FileDocIcon } from "@shared/components/FileDocIcon";
import { getFileDocVariant } from "@app/components/shared/filePreview/getFileTypeIcon";
import { useLazyThumbnail } from "@app/hooks/useLazyThumbnail";
import { IMAGE_EXTENSIONS } from "@app/utils/fileUtils";
@@ -1,6 +1,6 @@
import React from "react";
import { FileDocIcon } from "@app/components/shared/FileDocIcon";
import type { FileDocVariant } from "@app/components/shared/FileDocIcon";
import { FileDocIcon } from "@shared/components/FileDocIcon";
import type { FileDocVariant } from "@shared/components/FileDocIcon";
import type { StirlingFileStub } from "@app/types/fileContext";
import { detectFileExtension } from "@app/utils/fileUtils";
@@ -0,0 +1,41 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import {
GoogleDriveIcon,
OneDriveIcon,
DropboxIcon,
} from "@shared/components/CloudStorageIcons";
const meta: Meta<typeof GoogleDriveIcon> = {
title: "Primitives/CloudStorageIcons",
component: GoogleDriveIcon,
tags: ["autodocs"],
parameters: { layout: "centered" },
args: { colored: true },
argTypes: { colored: { control: "boolean" } },
};
export default meta;
type Story = StoryObj<typeof GoogleDriveIcon>;
export const GoogleDrive: Story = {};
/** All providers, brand-coloured vs. muted (inherits `currentColor`). */
export const AllProviders: Story = {
render: (args) => (
<div style={{ display: "flex", gap: 24, fontSize: 32 }}>
<GoogleDriveIcon width={32} height={32} {...args} />
<OneDriveIcon width={32} height={32} {...args} />
<DropboxIcon width={32} height={32} {...args} />
</div>
),
};
/** Uncoloured icons take the surrounding text colour via `currentColor`. */
export const MonochromeInheritsColor: Story = {
render: () => (
<div style={{ display: "flex", gap: 24, fontSize: 32, color: "#6d28d9" }}>
<GoogleDriveIcon width={32} height={32} />
<OneDriveIcon width={32} height={32} />
<DropboxIcon width={32} height={32} />
</div>
),
};
@@ -0,0 +1,48 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import {
FileDocIcon,
VARIANT_COLORS,
type FileDocVariant,
} from "@shared/components/FileDocIcon";
const VARIANTS = Object.keys(VARIANT_COLORS) as FileDocVariant[];
const meta: Meta<typeof FileDocIcon> = {
title: "Primitives/FileDocIcon",
component: FileDocIcon,
tags: ["autodocs"],
parameters: { layout: "centered" },
args: { variant: "pdf" },
argTypes: {
variant: { control: "inline-radio", options: VARIANTS },
color: { control: "color" },
},
};
export default meta;
type Story = StoryObj<typeof FileDocIcon>;
export const Pdf: Story = {};
/** Every file-type variant, each in its default brand colour. */
export const AllVariants: Story = {
render: () => (
<div style={{ display: "flex", gap: 20, alignItems: "center" }}>
{VARIANTS.map((variant) => (
<div
key={variant}
style={{ display: "grid", justifyItems: "center", gap: 6 }}
>
<FileDocIcon variant={variant} />
<small style={{ color: "var(--text-muted, #71717a)" }}>
{variant}
</small>
</div>
))}
</div>
),
};
/** The colour can be overridden to match any surface. */
export const CustomColour: Story = {
args: { variant: "generic", color: "#2563eb" },
};
+4
View File
@@ -39,3 +39,7 @@ export * from "@shared/components/Select";
export * from "@shared/components/Checkbox";
export * from "@shared/components/Radio";
export * from "@shared/components/Slider";
// Icons
export * from "@shared/components/FileDocIcon";
export * from "@shared/components/CloudStorageIcons";