From 76257f164bb86ae353b9abdec24c90d6b277c020 Mon Sep 17 00:00:00 2001 From: Danny White <3104761+dnywh@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:37:54 +1000 Subject: [PATCH] fix(studio): align storage row icon weight (#48480) ## What kind of change does this PR introduce? Bug fix stacked on #48478. ## What is the current behavior? Storage Explorer renders the custom closed-folder icon at `1.5` but leaves the Lucide open-folder icon at its default `2`. The duplicated file-picker implementation also uses a different set of stroke-width overrides. ## What is the new behavior? A shared `StorageRowIcon` renders loading, open and closed folder, image, audio, video and generic file icons at `1.5` across Storage Explorer, row editing and the bucket file picker. Test by comparing open and closed folders and file-type rows in Storage Explorer and the bucket file picker. | Before | After | | --- | --- | | CleanShot 2026-08-03 at 18 38
06@2x | CleanShot 2026-08-03 at 18 39
20@2x | | _Thicker image and file icon compared to folder icon_ | _Every icon has the same stroke thickness_ | ## Summary by CodeRabbit * **Improvements** * Standardized file, folder, media, and loading icons across storage views. * Improved visual consistency with unified icon sizing, styling, and stroke width. * **Tests** * Added coverage for loading, folder, media, and generic file icon states. --- .../BucketFilePickerRow.tsx | 48 +------------ .../StorageExplorer/FileExplorerRow.tsx | 49 +------------ .../FileExplorerRowEditing.tsx | 4 +- .../Storage/StorageRowIcon.test.tsx | 68 +++++++++++++++++++ .../interfaces/Storage/StorageRowIcon.tsx | 60 ++++++++++++++++ 5 files changed, 135 insertions(+), 94 deletions(-) create mode 100644 apps/studio/components/interfaces/Storage/StorageRowIcon.test.tsx create mode 100644 apps/studio/components/interfaces/Storage/StorageRowIcon.tsx diff --git a/apps/studio/components/interfaces/Storage/BucketFilePickerDialog/BucketFilePickerRow.tsx b/apps/studio/components/interfaces/Storage/BucketFilePickerDialog/BucketFilePickerRow.tsx index 5ec97e3b1bd..a43544d17cf 100644 --- a/apps/studio/components/interfaces/Storage/BucketFilePickerDialog/BucketFilePickerRow.tsx +++ b/apps/studio/components/interfaces/Storage/BucketFilePickerDialog/BucketFilePickerRow.tsx @@ -1,54 +1,12 @@ -import { FilesBucket as FilesBucketIcon } from 'icons' -import { AlertCircle, File, Film, FolderOpen, Image, LoaderCircle, Music } from 'lucide-react' +import { AlertCircle, LoaderCircle } from 'lucide-react' import type { CSSProperties, MouseEvent } from 'react' import { Checkbox, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { STORAGE_ROW_STATUS, STORAGE_ROW_TYPES, STORAGE_VIEWS } from '../Storage.constants' import { type StorageItem } from '../Storage.types' +import { StorageRowIcon } from '../StorageRowIcon' import { formatBytes } from '@/lib/helpers' -const RowIcon = ({ - view, - status, - fileType, - isOpened = false, - mimeType, -}: { - view: STORAGE_VIEWS - status: STORAGE_ROW_STATUS - fileType: string - isOpened?: boolean - mimeType: string | undefined -}) => { - if (view === STORAGE_VIEWS.LIST && status === STORAGE_ROW_STATUS.LOADING) { - return ( - - ) - } - - if (fileType === STORAGE_ROW_TYPES.FOLDER) { - return isOpened ? ( - - ) : ( - - ) - } - - if (mimeType?.includes('image')) { - return - } - - if (mimeType?.includes('audio')) { - return - } - - if (mimeType?.includes('video')) { - return - } - - return -} - interface BucketFilePickerRowProps { item: StorageItem view: STORAGE_VIEWS @@ -114,7 +72,7 @@ export const BucketFilePickerRow = ({ hidden: isSelected, })} > - { - if (view === STORAGE_VIEWS.LIST && status === STORAGE_ROW_STATUS.LOADING) { - return - } - - if (fileType === STORAGE_ROW_TYPES.FOLDER) { - return isOpened ? ( - - ) : ( - - ) - } - - if (mimeType?.includes('image')) { - return - } - - if (mimeType?.includes('audio')) { - return - } - - if (mimeType?.includes('video')) { - return - } - - return -} - interface FileExplorerRowProps { index: number item: StorageItem @@ -329,7 +284,7 @@ export const FileExplorerRow = ({ )} style={{ top: '2px' }} > -
- { + test.each(CASES)('renders $name at the canonical stroke width', (props) => { + const { container } = customRender( + + ) + + const icon = container.querySelector('svg') + + expect(icon).toHaveClass(props.expectedIcon) + expect(icon).toHaveAttribute('stroke-width', '1.5') + }) +}) diff --git a/apps/studio/components/interfaces/Storage/StorageRowIcon.tsx b/apps/studio/components/interfaces/Storage/StorageRowIcon.tsx new file mode 100644 index 00000000000..814a3b259c2 --- /dev/null +++ b/apps/studio/components/interfaces/Storage/StorageRowIcon.tsx @@ -0,0 +1,60 @@ +import { FilesBucket as FilesBucketIcon } from 'icons' +import { File, Film, FolderOpen, Image as ImageIcon, LoaderCircle, Music } from 'lucide-react' + +import { STORAGE_ROW_STATUS, STORAGE_ROW_TYPES, STORAGE_VIEWS } from './Storage.constants' + +const ICON_STROKE_WIDTH = 1.5 + +interface StorageRowIconProps { + view: STORAGE_VIEWS + status: STORAGE_ROW_STATUS + fileType: string + isOpened?: boolean + mimeType: string | undefined +} + +export const StorageRowIcon = ({ + view, + status, + fileType, + isOpened = false, + mimeType, +}: StorageRowIconProps) => { + if (view === STORAGE_VIEWS.LIST && status === STORAGE_ROW_STATUS.LOADING) { + return ( + + ) + } + + if (fileType === STORAGE_ROW_TYPES.FOLDER) { + return isOpened ? ( + + ) : ( + + ) + } + + if (mimeType?.includes('image')) { + return ( + + ) + } + + if (mimeType?.includes('audio')) { + return + } + + if (mimeType?.includes('video')) { + return + } + + return +}