Skip to content

Commit 502c098

Browse files
authored
chore: migrate old <Checkbox /> usage to the new Shadcn component (supabase#45211)
## Problem We want to reduce the code we ship and maintain. ## Solution Migrate old `<Checkbox />` usage to the new Shadcn component. There's one visual change: no more green background or outline. ## Screenshots ### On `apps/www` Before: <img width="1095" height="772" alt="image" src="https://github.com/user-attachments/assets/b14a0477-d8ac-4ae5-a1c3-63657609e4e5" /> After: <img width="1085" height="766" alt="image" src="https://github.com/user-attachments/assets/f4c2156c-38e1-4834-b013-e39b8c3dd76e" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Unified checkbox appearance and behavior across the app for consistent toggles, labels, and interactions. * **Bug Fixes** * Fixed selection issues including shift-select edge cases and click-propagation in lists; added ARIA labels for improved accessibility. * **Chores** * Removed legacy checkbox implementation, associated styles, tests, and deprecated theme entries. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent f0746db commit 502c098

15 files changed

Lines changed: 261 additions & 549 deletions

File tree

apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerColumn.tsx

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ChevronsDown, ChevronsUp, Copy, Eye, FolderPlus, Upload } from 'lucide-
55
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
66
import { toast } from 'sonner'
77
import {
8-
Checkbox,
8+
Checkbox_Shadcn_ as Checkbox,
99
cn,
1010
ContextMenu_Shadcn_,
1111
ContextMenuContent_Shadcn_,
@@ -143,16 +143,6 @@ export const FileExplorerColumn = ({
143143
}
144144
}
145145

146-
const SelectAllCheckbox = () => (
147-
<Checkbox
148-
label=""
149-
className="-mt-0.5"
150-
checked={columnFiles.length !== 0 && selectedFilesFromColumn.length === columnFiles.length}
151-
disabled={columnFiles.length === 0}
152-
onChange={() => onSelectAllItemsInColumn(index)}
153-
/>
154-
)
155-
156146
const getItemKey = useCallback(
157147
(index: number) => {
158148
const item = columnItems[index]
@@ -218,10 +208,24 @@ export const FileExplorerColumn = ({
218208
>
219209
{columnFiles.length > 0 ? (
220210
<>
221-
<SelectAllCheckbox />
222-
<p className="text-sm text-foreground-light">
223-
Select all {columnFiles.length} files
224-
</p>
211+
<div className="flex items-center space-x-2">
212+
<Checkbox
213+
id="checkbox-select-all"
214+
className="-mt-0.5"
215+
checked={
216+
columnFiles.length !== 0 &&
217+
selectedFilesFromColumn.length === columnFiles.length
218+
}
219+
disabled={columnFiles.length === 0}
220+
onCheckedChange={() => onSelectAllItemsInColumn(index)}
221+
/>
222+
<label
223+
htmlFor="checkbox-select-all"
224+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
225+
>
226+
Select all {columnFiles.length} files
227+
</label>
228+
</div>
225229
</>
226230
) : (
227231
<p className="text-sm text-foreground-light">No files available for selection</p>
@@ -233,7 +237,24 @@ export const FileExplorerColumn = ({
233237
{view === STORAGE_VIEWS.LIST && (
234238
<div className="sticky top-0 py-2 z-10 flex min-w-min items-center border-b border-overlay bg-surface-100 px-2.5">
235239
<div className="flex w-[40%] min-w-[250px] items-center">
236-
<SelectAllCheckbox />
240+
<div className="relative w-[30px]" onClick={(event) => event.stopPropagation()}>
241+
<Checkbox
242+
id="checkbox-select-all"
243+
className="-mt-0.5"
244+
checked={
245+
columnFiles.length !== 0 &&
246+
selectedFilesFromColumn.length === columnFiles.length
247+
}
248+
disabled={columnFiles.length === 0}
249+
onCheckedChange={() => onSelectAllItemsInColumn(index)}
250+
/>
251+
<label
252+
htmlFor="checkbox-select-all"
253+
className="sr-only text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
254+
>
255+
Select all {columnFiles.length} files
256+
</label>
257+
</div>
237258
<p className="text-sm">Name</p>
238259
</div>
239260
<p className="w-[11%] min-w-[100px] text-sm">Size</p>

apps/studio/components/interfaces/Storage/StorageExplorer/FileExplorerRow.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from 'lucide-react'
1919
import type { CSSProperties } from 'react'
2020
import {
21-
Checkbox,
21+
Checkbox_Shadcn_ as Checkbox,
2222
cn,
2323
DropdownMenu,
2424
DropdownMenuContent,
@@ -333,15 +333,15 @@ export const FileExplorerRow = ({
333333
</div>
334334
)}
335335
<Checkbox
336-
label={''}
337-
className={`w-full ${item.type !== STORAGE_ROW_TYPES.FILE ? 'invisible' : ''} ${
336+
className={`${item.type !== STORAGE_ROW_TYPES.FILE ? 'invisible' : ''} ${
338337
isSelected ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
339338
}`}
340339
checked={isSelected}
341-
onChange={(event) => {
342-
event.stopPropagation()
343-
onCheckItem((event.nativeEvent as KeyboardEvent).shiftKey)
340+
// use onClick instead of onCheckedChange to handle shift-key selection
341+
onClick={(event) => {
342+
onCheckItem(event.nativeEvent.shiftKey)
344343
}}
344+
aria-label="Check to select this item"
345345
/>
346346
</div>
347347
<p title={item.name} className="truncate text-sm" style={{ width: nameWidth }}>

apps/studio/components/interfaces/Storage/StoragePolicies/StoragePoliciesEditor.tsx

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { noop } from 'lodash'
2-
import { Button, Checkbox, cn, Modal } from 'ui'
2+
import { Button, Checkbox_Shadcn_ as Checkbox, cn, Modal } from 'ui'
33

44
import { STORAGE_CLIENT_LIBRARY_MAPPINGS } from '../Storage.constants'
55
import { deriveAllowedClientLibraryMethods } from '../Storage.utils'
@@ -52,26 +52,58 @@ const PolicyAllowedOperations = ({ allowedOperations = [], onToggleOperation = (
5252
</div>
5353
<div className="md:w-2/3 flex flex-col gap-3">
5454
<div className="flex flex-wrap items-center gap-x-8 gap-y-4">
55-
<Checkbox
56-
label="SELECT"
57-
onChange={() => onToggleOperation('SELECT')}
58-
checked={allowedOperations.includes('SELECT')}
59-
/>
60-
<Checkbox
61-
label="INSERT"
62-
onChange={() => onToggleOperation('INSERT')}
63-
checked={allowedOperations.includes('INSERT')}
64-
/>
65-
<Checkbox
66-
label="UPDATE"
67-
onChange={() => onToggleOperation('UPDATE')}
68-
checked={allowedOperations.includes('UPDATE')}
69-
/>
70-
<Checkbox
71-
label="DELETE"
72-
onChange={() => onToggleOperation('DELETE')}
73-
checked={allowedOperations.includes('DELETE')}
74-
/>
55+
<div className="flex items-center space-x-2">
56+
<Checkbox
57+
id="checkbox-select"
58+
onCheckedChange={() => onToggleOperation('SELECT')}
59+
checked={allowedOperations.includes('SELECT')}
60+
/>
61+
<label
62+
htmlFor="checkbox-select"
63+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
64+
>
65+
SELECT
66+
</label>
67+
</div>
68+
<div className="flex items-center space-x-2">
69+
<Checkbox
70+
id="checkbox-insert"
71+
onCheckedChange={() => onToggleOperation('INSERT')}
72+
checked={allowedOperations.includes('INSERT')}
73+
/>
74+
<label
75+
htmlFor="checkbox-insert"
76+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
77+
>
78+
INSERT
79+
</label>
80+
</div>
81+
<div className="flex items-center space-x-2">
82+
<Checkbox
83+
id="checkbox-update"
84+
onCheckedChange={() => onToggleOperation('UPDATE')}
85+
checked={allowedOperations.includes('UPDATE')}
86+
/>
87+
<label
88+
htmlFor="checkbox-update"
89+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
90+
>
91+
UPDATE
92+
</label>
93+
</div>
94+
<div className="flex items-center space-x-2">
95+
<Checkbox
96+
id="checkbox-delete"
97+
onCheckedChange={() => onToggleOperation('DELETE')}
98+
checked={allowedOperations.includes('DELETE')}
99+
/>
100+
<label
101+
htmlFor="checkbox-delete"
102+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
103+
>
104+
DELETE
105+
</label>
106+
</div>
75107
</div>
76108
{hasUpdateOrDelete && (
77109
<p className="text-sm text-foreground-light mt-3 prose [&>code]:text-xs">

apps/studio/components/interfaces/TableGridEditor/DeleteConfirmationDialogs.tsx

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { ExternalLink } from 'lucide-react'
22
import Link from 'next/link'
33
import { toast } from 'sonner'
4-
import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, Button, Checkbox } from 'ui'
4+
import {
5+
Alert_Shadcn_,
6+
AlertDescription_Shadcn_,
7+
AlertTitle_Shadcn_,
8+
Button,
9+
Checkbox_Shadcn_ as Checkbox,
10+
} from 'ui'
511
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
612

713
import { useTableFilter } from '@/components/grid/hooks/useTableFilter'
@@ -216,12 +222,24 @@ const DeleteConfirmationDialogs = ({
216222
<p className="text-sm text-foreground-light">
217223
Are you sure you want to delete the selected column? This action cannot be undone.
218224
</p>
219-
<Checkbox
220-
label="Drop column with cascade?"
221-
description="Deletes the column and its dependent objects"
222-
checked={isDeleteWithCascade}
223-
onChange={() => snap.toggleConfirmationIsWithCascade()}
224-
/>
225+
<div className="items-top flex space-x-2">
226+
<Checkbox
227+
id="checkbox-cascade"
228+
checked={isDeleteWithCascade}
229+
onCheckedChange={() => snap.toggleConfirmationIsWithCascade()}
230+
/>
231+
<div className="grid gap-1.5 leading-none">
232+
<label
233+
htmlFor="checkbox-cascade"
234+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
235+
>
236+
Drop column with cascade?
237+
</label>
238+
<p className="text-sm text-foreground-muted">
239+
Deletes the column and its dependent objects
240+
</p>
241+
</div>
242+
</div>
225243
{isDeleteWithCascade && (
226244
<Alert_Shadcn_
227245
variant="warning"
@@ -265,12 +283,24 @@ const DeleteConfirmationDialogs = ({
265283
<p className="text-sm text-foreground-light">
266284
Are you sure you want to delete the selected table? This action cannot be undone.
267285
</p>
268-
<Checkbox
269-
label="Drop table with cascade?"
270-
description="Deletes the table and its dependent objects"
271-
checked={isDeleteWithCascade}
272-
onChange={() => snap.toggleConfirmationIsWithCascade(!isDeleteWithCascade)}
273-
/>
286+
<div className="items-top flex space-x-2">
287+
<Checkbox
288+
id="checkbox-cascade"
289+
checked={isDeleteWithCascade}
290+
onCheckedChange={() => snap.toggleConfirmationIsWithCascade(!isDeleteWithCascade)}
291+
/>
292+
<div className="grid gap-1.5 leading-none">
293+
<label
294+
htmlFor="checkbox-cascade"
295+
className="text-sm text-foreground-light leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
296+
>
297+
Drop table with cascade?
298+
</label>
299+
<p className="text-sm text-foreground-muted">
300+
Deletes the table and its dependent objects
301+
</p>
302+
</div>
303+
</div>
274304
{isDeleteWithCascade && (
275305
<Alert_Shadcn_ variant="warning">
276306
<AlertTitle_Shadcn_>

apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/Column.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { useState } from 'react'
55
import {
66
Badge,
77
Button,
8-
Checkbox,
98
Checkbox_Shadcn_,
109
cn,
1110
Command_Shadcn_,
@@ -284,10 +283,10 @@ export const Column = ({
284283
</div>
285284
</div>
286285
<div className="w-[10%]">
287-
<Checkbox
288-
label=""
286+
<Checkbox_Shadcn_
287+
aria-label="Check to make this column a primary key"
289288
checked={column.isPrimaryKey}
290-
onChange={() => {
289+
onCheckedChange={() => {
291290
const updatedValue = !column.isPrimaryKey
292291
onUpdateColumn({
293292
isPrimaryKey: updatedValue,

0 commit comments

Comments
 (0)