From 5066ef708ab862f78fb30826f96c8ae4d82d86c2 Mon Sep 17 00:00:00 2001 From: robertkasza <167509084+robertkasza@users.noreply.github.com> Date: Wed, 12 Nov 2025 11:26:32 +0100 Subject: [PATCH] chore(dashboard): remove v2 ui components from datatable (#3568) --- dashboard/e2e/database/create-table.test.ts | 6 +- .../e2e/database/permissions-table.test.ts | 6 +- dashboard/react-table-config.d.ts | 1 - .../common/TimePicker/TimePickerInput.tsx | 2 +- .../FormActivityIndicator.tsx | 25 +- .../components/form/FormInput/FormInput.tsx | 85 +++- .../components/form/FormSelect/FormSelect.tsx | 91 ++++ .../src/components/form/FormSelect/index.ts | 1 + .../form/FormTextarea/FormTextarea.tsx | 98 ++++ .../src/components/form/FormTextarea/index.ts | 1 + .../AuthenticatedLayout.tsx | 9 +- .../src/components/layout/MainNav/NavTree.tsx | 4 +- .../ReadOnlyToggle/ReadOnlyToggle.tsx | 66 ++- .../ui/v2/icons/KeyIcon/KeyIcon.tsx | 32 -- .../components/ui/v2/icons/KeyIcon/index.ts | 1 - dashboard/src/components/ui/v3/button.tsx | 4 +- dashboard/src/components/ui/v3/checkbox.tsx | 6 +- dashboard/src/components/ui/v3/dialog.tsx | 17 +- .../src/components/ui/v3/inline-code.tsx | 4 +- dashboard/src/components/ui/v3/input.tsx | 7 +- dashboard/src/components/ui/v3/spinner.tsx | 5 +- dashboard/src/components/ui/v3/textarea.tsx | 2 +- .../SignInWithEmailAndPasswordForm.tsx | 2 + .../SignUpWithEmailAndPasswordForm.tsx | 9 +- .../components/SignUpWithSecurityKeyForm.tsx | 8 +- .../BillingDetails/BillingDetails.tsx | 4 +- .../projects/projects-grid/projects-grid.tsx | 3 +- .../BaseRecordForm/BaseRecordForm.tsx | 20 +- .../BaseTableForm/ColumnEditorTable.tsx | 5 +- .../CreateRecordForm/CreateRecordForm.tsx | 17 +- .../DataBrowserEmptyState.tsx | 13 +- .../DataBrowserGrid/DataBrowserGrid.tsx | 20 +- .../DataBrowserGridControls.tsx | 47 +- .../DataBrowserSidebar/DataBrowserSidebar.tsx | 445 +++++++----------- .../DataBrowserSidebar/TableActions.tsx | 111 +++++ .../DatabaseRecordInputGroup.tsx | 166 +++---- .../RuleGroupEditor/RuleGroupControls.tsx | 5 +- .../RuleGroupEditor/RuleValueInput.tsx | 2 +- .../useUpdateRecordWithToastMutation.ts | 14 +- .../dataGrid/types/dataBrowser/dataBrowser.ts | 1 + .../postgresqlConstants.ts | 7 +- .../components/DataGrid/DataGrid.test.tsx | 96 ++-- .../dataGrid/components/DataGrid/DataGrid.tsx | 18 +- .../components/DataGrid/useDataGrid.tsx | 40 +- .../components/DataGridBody/DataGridBody.tsx | 27 +- .../DataGridBooleanCell.tsx | 125 ++--- .../components/DataGridCell/DataGridCell.tsx | 73 ++- .../DataGridDateCell/DataGridDateCell.tsx | 77 +-- .../DataGridDecimalCell.tsx | 108 ----- .../components/DataGridDecimalCell/index.ts | 2 - .../DataGridHeader/DataGridHeader.tsx | 10 +- .../DataGridHeaderButton.tsx | 4 +- .../DataGridIntegerCell.tsx | 112 ----- .../components/DataGridIntegerCell/index.ts | 2 - .../DataGridNumericCell.tsx | 97 ++++ .../components/DataGridNumericCell/index.ts | 2 + .../DataGridPagination/DataGridPagination.tsx | 56 +-- .../DataGridPreviewCell.tsx | 249 ++++------ .../DataGridTextCell/DataGridTextCell.tsx | 91 +--- .../FilesDataGrid/FilesDataGrid.tsx | 5 +- .../FilesDataGridControls.tsx | 48 +- dashboard/src/pages/onboarding/index.tsx | 2 +- .../src/pages/orgs/[orgSlug]/billing.tsx | 2 +- .../src/pages/orgs/[orgSlug]/members.tsx | 2 +- .../[schemaSlug]/[tableSlug].tsx | 8 +- .../browser/[dataSourceSlug]/index.tsx | 8 +- .../pages/orgs/[orgSlug]/projects/index.tsx | 4 +- .../src/pages/orgs/[orgSlug]/settings.tsx | 2 +- dashboard/src/styles/globals.css | 41 +- dashboard/src/utils/toast/triggerToast.tsx | 31 +- dashboard/tailwind.config.js | 17 +- package.json | 1 + 72 files changed, 1369 insertions(+), 1363 deletions(-) create mode 100644 dashboard/src/components/form/FormSelect/FormSelect.tsx create mode 100644 dashboard/src/components/form/FormSelect/index.ts create mode 100644 dashboard/src/components/form/FormTextarea/FormTextarea.tsx create mode 100644 dashboard/src/components/form/FormTextarea/index.ts delete mode 100644 dashboard/src/components/ui/v2/icons/KeyIcon/KeyIcon.tsx delete mode 100644 dashboard/src/components/ui/v2/icons/KeyIcon/index.ts create mode 100644 dashboard/src/features/orgs/projects/database/dataGrid/components/DataBrowserSidebar/TableActions.tsx delete mode 100644 dashboard/src/features/orgs/projects/storage/dataGrid/components/DataGridDecimalCell/DataGridDecimalCell.tsx delete mode 100644 dashboard/src/features/orgs/projects/storage/dataGrid/components/DataGridDecimalCell/index.ts delete mode 100644 dashboard/src/features/orgs/projects/storage/dataGrid/components/DataGridIntegerCell/DataGridIntegerCell.tsx delete mode 100644 dashboard/src/features/orgs/projects/storage/dataGrid/components/DataGridIntegerCell/index.ts create mode 100644 dashboard/src/features/orgs/projects/storage/dataGrid/components/DataGridNumericCell/DataGridNumericCell.tsx create mode 100644 dashboard/src/features/orgs/projects/storage/dataGrid/components/DataGridNumericCell/index.ts diff --git a/dashboard/e2e/database/create-table.test.ts b/dashboard/e2e/database/create-table.test.ts index 8209ca61b..6631cc9ba 100644 --- a/dashboard/e2e/database/create-table.test.ts +++ b/dashboard/e2e/database/create-table.test.ts @@ -102,7 +102,7 @@ test('should create a table with nullable columns', async ({ page.getByRole('link', { name: tableName, exact: true }), ).toBeVisible(); await page - .locator(`li:has-text("${tableName}") #table-management-menu button`) + .locator(`li:has-text("${tableName}") #table-management-menu-${tableName}`) .click(); await page.getByText('Edit Table').click(); await expect(page.locator('h2:has-text("Edit Table")')).toBeVisible(); @@ -143,7 +143,7 @@ test('should create a table with an identity column', async ({ page.getByRole('link', { name: tableName, exact: true }), ).toBeVisible(); await page - .locator(`li:has-text("${tableName}") #table-management-menu button`) + .locator(`li:has-text("${tableName}") #table-management-menu-${tableName}`) .click(); await page.getByText('Edit Table').click(); await expect(page.locator('h2:has-text("Edit Table")')).toBeVisible(); @@ -267,7 +267,7 @@ test('should be able to create a table with a composite key', async ({ ).toBeVisible(); await page - .locator(`li:has-text("${tableName}") #table-management-menu button`) + .locator(`li:has-text("${tableName}") #table-management-menu-${tableName}`) .click(); await page.getByText('Edit Table').click(); await expect(page.locator('div[data-testid="id"]')).toBeVisible(); diff --git a/dashboard/e2e/database/permissions-table.test.ts b/dashboard/e2e/database/permissions-table.test.ts index 87a98598e..81d6951b2 100644 --- a/dashboard/e2e/database/permissions-table.test.ts +++ b/dashboard/e2e/database/permissions-table.test.ts @@ -41,7 +41,7 @@ test('should create a table with role permissions to select row', async ({ // Press three horizontal dots more options button next to the table name await page - .locator(`li:has-text("${tableName}") #table-management-menu button`) + .locator(`li:has-text("${tableName}") #table-management-menu-${tableName}`) .click(); await page.getByRole('menuitem', { name: /edit permissions/i }).click(); @@ -89,7 +89,7 @@ test('should create a table with role permissions and a custom check to select r // Press three horizontal dots more options button next to the table name await page - .locator(`li:has-text("${tableName}") #table-management-menu button`) + .locator(`li:has-text("${tableName}") #table-management-menu-${tableName}`) .click(); await page.getByRole('menuitem', { name: /edit permissions/i }).click(); @@ -114,7 +114,7 @@ test('should create a table with role permissions and a custom check to select r await page.getByText('Select variable...', { exact: true }).click(); - const variableSelector = await page.locator('input[role="combobox"]'); + const variableSelector = page.locator('input[role="combobox"]'); await variableSelector.fill('X-Hasura-User-Id'); diff --git a/dashboard/react-table-config.d.ts b/dashboard/react-table-config.d.ts index 7403b86fd..112ffa598 100644 --- a/dashboard/react-table-config.d.ts +++ b/dashboard/react-table-config.d.ts @@ -64,7 +64,6 @@ declare module 'react-table' { export interface Cell< D extends Record = Record, - V = any, > extends UseGroupByCellProps, UseRowStateCellProps {} diff --git a/dashboard/src/components/common/TimePicker/TimePickerInput.tsx b/dashboard/src/components/common/TimePicker/TimePickerInput.tsx index 3438f3667..0263460dc 100644 --- a/dashboard/src/components/common/TimePicker/TimePickerInput.tsx +++ b/dashboard/src/components/common/TimePicker/TimePickerInput.tsx @@ -123,7 +123,7 @@ const TimePickerInput = React.forwardRef< id={id || picker} name={name || picker} className={cn( - 'w-[48px] text-center font-mono text-base tabular-nums focus:bg-accent focus:text-accent-foreground [&::-webkit-inner-spin-button]:appearance-none', + 'w-[48px] text-center font-mono text-base tabular-nums focus:bg-accent-background focus:text-accent-foreground [&::-webkit-inner-spin-button]:appearance-none', className, )} value={value || calculatedValue} diff --git a/dashboard/src/components/form/FormActivityIndicator/FormActivityIndicator.tsx b/dashboard/src/components/form/FormActivityIndicator/FormActivityIndicator.tsx index 324fe2259..a5a1ad714 100644 --- a/dashboard/src/components/form/FormActivityIndicator/FormActivityIndicator.tsx +++ b/dashboard/src/components/form/FormActivityIndicator/FormActivityIndicator.tsx @@ -1,26 +1,25 @@ -import { ActivityIndicator } from '@/components/ui/v2/ActivityIndicator'; -import type { BoxProps } from '@/components/ui/v2/Box'; -import { Box } from '@/components/ui/v2/Box'; -import { twMerge } from 'tailwind-merge'; +import { Spinner } from '@/components/ui/v3/spinner'; +import { cn } from '@/lib/utils'; -export interface FormActivityIndicatorProps extends BoxProps {} +export interface FormActivityIndicatorProps { + className?: string; +} export default function FormActivityIndicator({ className, ...props }: FormActivityIndicatorProps) { return ( - - - + + Loading form... + + ); } diff --git a/dashboard/src/components/form/FormInput/FormInput.tsx b/dashboard/src/components/form/FormInput/FormInput.tsx index 1f39f102b..5efe6dc2c 100644 --- a/dashboard/src/components/form/FormInput/FormInput.tsx +++ b/dashboard/src/components/form/FormInput/FormInput.tsx @@ -1,12 +1,16 @@ import { FormControl, + FormDescription, FormField, FormItem, FormLabel, FormMessage, } from '@/components/ui/v3/form'; import { Input } from '@/components/ui/v3/input'; +import { cn } from '@/lib/utils'; +import { type ForwardedRef, forwardRef, type ReactNode } from 'react'; import type { Control, FieldPath, FieldValues } from 'react-hook-form'; +import { mergeRefs } from 'react-merge-refs'; const inputClasses = '!bg-transparent aria-[invalid=true]:border-red-500 aria-[invalid=true]:focus:border-red-500 aria-[invalid=true]:focus:ring-red-500'; @@ -17,43 +21,82 @@ interface FormInputProps< > { control: Control; name: TName; - label: string; + label: ReactNode; placeholder?: string; className?: string; type?: string; + inline?: boolean; + helperText?: string | null; } -function FormInput< +function InnerFormInput< TFieldValues extends FieldValues = FieldValues, TName extends FieldPath = FieldPath, ->({ - control, - name, - label, - placeholder, - className = '', - type = 'text', -}: FormInputProps) { +>( + { + control, + name, + label, + placeholder, + className = '', + type = 'text', + inline, + helperText, + }: FormInputProps, + ref: ForwardedRef, +) { return ( ( - - {label} - - - - + + + {label} + +
+ + + + {!!helperText && ( + + {helperText} + + )} + +
)} /> ); } +const FormInput = forwardRef(InnerFormInput) as < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>( + props: FormInputProps & { + ref?: ForwardedRef; + }, +) => ReturnType; + export default FormInput; diff --git a/dashboard/src/components/form/FormSelect/FormSelect.tsx b/dashboard/src/components/form/FormSelect/FormSelect.tsx new file mode 100644 index 000000000..9da4242bf --- /dev/null +++ b/dashboard/src/components/form/FormSelect/FormSelect.tsx @@ -0,0 +1,91 @@ +import { + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/v3/form'; +import { + Select, + SelectContent, + SelectTrigger, + SelectValue, +} from '@/components/ui/v3/select'; +import { cn } from '@/lib/utils'; +import type { PropsWithChildren, ReactNode } from 'react'; +import type { Control, FieldPath, FieldValues } from 'react-hook-form'; + +interface FormSelectProps< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> { + control: Control; + name: TName; + label: ReactNode; + placeholder?: string; + className?: string; + inline?: boolean; + helperText?: string | null; +} + +function FormSelect< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + control, + name, + label, + placeholder, + className = '', + inline, + helperText, + children, +}: PropsWithChildren>) { + return ( + { + const { onChange, ...selectProps } = field; + return ( + + + {label} + +
+ + {!!helperText && ( + + {helperText} + + )} + +
+
+ ); + }} + /> + ); +} + +export default FormSelect; diff --git a/dashboard/src/components/form/FormSelect/index.ts b/dashboard/src/components/form/FormSelect/index.ts new file mode 100644 index 000000000..cfedc469a --- /dev/null +++ b/dashboard/src/components/form/FormSelect/index.ts @@ -0,0 +1 @@ +export { default as FormSelect } from './FormSelect'; diff --git a/dashboard/src/components/form/FormTextarea/FormTextarea.tsx b/dashboard/src/components/form/FormTextarea/FormTextarea.tsx new file mode 100644 index 000000000..bd93c0952 --- /dev/null +++ b/dashboard/src/components/form/FormTextarea/FormTextarea.tsx @@ -0,0 +1,98 @@ +import { + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from '@/components/ui/v3/form'; +import { Textarea } from '@/components/ui/v3/textarea'; +import { cn } from '@/lib/utils'; +import { forwardRef, type ForwardedRef, type ReactNode } from 'react'; +import type { Control, FieldPath, FieldValues } from 'react-hook-form'; +import { mergeRefs } from 'react-merge-refs'; + +const inputClasses = + '!bg-transparent aria-[invalid=true]:border-red-500 aria-[invalid=true]:focus:border-red-500 aria-[invalid=true]:focus:ring-red-500'; + +interface FormTextareaProps< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> { + control: Control; + name: TName; + label: ReactNode; + placeholder?: string; + className?: string; + inline?: boolean; + helperText?: string | null; +} + +function InnerFormTextarea< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>( + { + control, + name, + label, + placeholder, + className = '', + inline, + helperText, + }: FormTextareaProps, + ref: ForwardedRef, +) { + return ( + ( + + + {label} + +
+ +