diff --git a/apps/studio/components/grid/components/footer/Footer.tsx b/apps/studio/components/grid/components/footer/Footer.tsx
index 9a06d3ccd79..5ecd561dbd2 100644
--- a/apps/studio/components/grid/components/footer/Footer.tsx
+++ b/apps/studio/components/grid/components/footer/Footer.tsx
@@ -3,7 +3,7 @@ import { parseAsString, useQueryState } from 'nuqs'
import { Pagination } from './pagination/Pagination'
import { GridFooter } from '@/components/ui/GridFooter'
-import TwoOptionToggle from '@/components/ui/TwoOptionToggle'
+import { TwoOptionToggle } from '@/components/ui/TwoOptionToggle'
import { useTableEditorQuery } from '@/data/table-editor/table-editor-query'
import { isTableLike, isViewLike } from '@/data/table-editor/table-editor-types'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
diff --git a/apps/studio/components/grid/components/header/Header.tsx b/apps/studio/components/grid/components/header/Header.tsx
index 20086e61688..be4b6f661ce 100644
--- a/apps/studio/components/grid/components/header/Header.tsx
+++ b/apps/studio/components/grid/components/header/Header.tsx
@@ -1,12 +1,10 @@
-import { PermissionAction } from '@supabase/shared-types/out/constants'
import { keepPreviousData } from '@tanstack/react-query'
import { useParams } from 'common'
-import { ArrowUp, ChevronDown, FileText, Trash } from 'lucide-react'
+import { ChevronDown, Trash } from 'lucide-react'
import { ReactNode, useState } from 'react'
import { toast } from 'sonner'
import {
Button,
- cn,
copyToClipboard,
DropdownMenu,
DropdownMenuContent,
@@ -31,12 +29,8 @@ import {
} from '@/components/layouts/TableEditorLayout/ExportAllRows'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import { Shortcut } from '@/components/ui/Shortcut'
-import { ShortcutBadge } from '@/components/ui/ShortcutBadge'
import { useTableRowsCountQuery } from '@/data/table-rows/table-rows-count-query'
import { useTableRowsQuery } from '@/data/table-rows/table-rows-query'
-import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
-import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
-import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { RoleImpersonationState } from '@/lib/role-impersonation'
import {
@@ -44,7 +38,6 @@ import {
useSubscribeToImpersonatedRole,
} from '@/state/role-impersonation-state'
import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
-import { useShortcut } from '@/state/shortcuts/useShortcut'
import { useTableEditorStateSnapshot } from '@/state/table-editor'
import { useTableEditorTableStateSnapshot } from '@/state/table-editor-table'
@@ -60,11 +53,10 @@ export const Header = ({ customHeader, isRefetching, tableQueriesEnabled = true
useSyncFiltersToUrl()
const snap = useTableEditorTableStateSnapshot()
- const showInsertButton = snap.selectedRows.size === 0
return (
-
+
{customHeader ? (
{customHeader}
) : snap.selectedRows.size > 0 ? (
@@ -72,165 +64,21 @@ export const Header = ({ customHeader, isRefetching, tableQueriesEnabled = true
) : (
-
+
)}
-
+
{!customHeader && snap.selectedRows.size === 0 && (
)}
- {showInsertButton && }
)
}
-const InsertButton = () => {
- const { ref: projectRef } = useParams()
- const { data: org } = useSelectedOrganizationQuery()
-
- const snap = useTableEditorTableStateSnapshot()
- const tableEditorSnap = useTableEditorStateSnapshot()
- const { can: canCreateColumns } = useAsyncCheckPermissions(
- PermissionAction.TENANT_SQL_ADMIN_WRITE,
- 'columns'
- )
- const { mutate: sendEvent } = useSendEventMutation()
-
- const onAddRow =
- snap.editable && (snap.table.columns ?? []).length > 0 ? tableEditorSnap.onAddRow : undefined
- const onAddColumn = snap.editable ? tableEditorSnap.onAddColumn : undefined
- const onImportData = snap.editable ? tableEditorSnap.onImportData : undefined
-
- const canAddNew = onAddRow !== undefined || onAddColumn !== undefined
-
- useShortcut(SHORTCUT_IDS.TABLE_EDITOR_INSERT_ROW, () => onAddRow?.(), {
- registerInCommandMenu: true,
- enabled: onAddRow !== undefined && canAddNew && canCreateColumns,
- })
- useShortcut(SHORTCUT_IDS.TABLE_EDITOR_INSERT_COLUMN, () => onAddColumn?.(), {
- registerInCommandMenu: true,
- enabled: onAddColumn !== undefined && canAddNew && canCreateColumns,
- })
- useShortcut(SHORTCUT_IDS.TABLE_EDITOR_IMPORT_CSV, () => onImportData?.(), {
- registerInCommandMenu: true,
- enabled: onImportData !== undefined && canAddNew && canCreateColumns,
- })
-
- if (!canAddNew || !canCreateColumns) return null
-
- return (
-
-
- }
- >
- Insert
-
-
-
- {[
- ...(onAddRow !== undefined
- ? [
-
-
-
-
Insert row
-
Insert a new row into {snap.table.name}
-
-
- ,
- ]
- : []),
- ...(onAddColumn !== undefined
- ? [
-
-
-
-
Insert column
-
- Insert a new column into {snap.table.name}
-
-
-
- ,
- ]
- : []),
- ...(onImportData !== undefined
- ? [
- {
- onImportData()
- sendEvent({
- action: 'import_data_button_clicked',
- properties: { tableType: 'Existing Table' },
- groups: {
- project: projectRef ?? 'Unknown',
- organization: org?.slug ?? 'Unknown',
- },
- })
- }}
- >
-
-
-
Import data from CSV
-
Insert new rows from a CSV
-
-
- ,
- ]
- : []),
- ]}
-
-
- )
-}
-
type RowHeaderProps = {
tableQueriesEnabled?: boolean
}
diff --git a/apps/studio/components/grid/components/header/filter/FilterPopoverNew.tsx b/apps/studio/components/grid/components/header/filter/FilterPopoverNew.tsx
index db262c64b04..604db76167a 100644
--- a/apps/studio/components/grid/components/header/filter/FilterPopoverNew.tsx
+++ b/apps/studio/components/grid/components/header/filter/FilterPopoverNew.tsx
@@ -210,7 +210,7 @@ export const FilterPopoverNew = ({ isRefetching = false }: FilterPopoverProps) =
actions={actions}
isLoading={isGenerating}
variant="pill"
- className="bg-transparent border-0 overflow-visible px-1.5"
+ className="bg-transparent border-0 overflow-visible px-1.5 [&>div>div>div>input]:!text-xs"
icon={icon}
/>
diff --git a/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx b/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx
index 7bedec7cfd1..06369681231 100644
--- a/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx
+++ b/apps/studio/components/interfaces/Auth/EmailTemplates/TemplateEditor.tsx
@@ -25,7 +25,7 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { SpamValidation } from './SpamValidation'
import { PreventNavigationOnUnsavedChanges } from '@/components/ui-patterns/Dialogs/PreventNavigationOnUnsavedChanges'
import CodeEditor from '@/components/ui/CodeEditor/CodeEditor'
-import TwoOptionToggle from '@/components/ui/TwoOptionToggle'
+import { TwoOptionToggle } from '@/components/ui/TwoOptionToggle'
import { useAuthConfigQuery } from '@/data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from '@/data/auth/auth-config-update-mutation'
import { useValidateSpamMutation, ValidateSpamResponse } from '@/data/auth/validate-spam-mutation'
@@ -279,7 +279,7 @@ export const TemplateEditor = ({ template }: TemplateEditorProps) => {
width={60}
options={['preview', 'source']}
activeOption={activeView}
- onClickOption={(option: 'source' | 'preview') => setActiveView(option)}
+ onClickOption={(option) => setActiveView(option as 'source' | 'preview')}
borderOverride="border-muted"
/>
diff --git a/apps/studio/components/interfaces/QueryInsights/QueryInsightsTable/QueryInsightsTable.tsx b/apps/studio/components/interfaces/QueryInsights/QueryInsightsTable/QueryInsightsTable.tsx
index c3a06a7a301..fbabf2a23a6 100644
--- a/apps/studio/components/interfaces/QueryInsights/QueryInsightsTable/QueryInsightsTable.tsx
+++ b/apps/studio/components/interfaces/QueryInsights/QueryInsightsTable/QueryInsightsTable.tsx
@@ -27,7 +27,7 @@ import type { QueryPlanRow } from '@/components/interfaces/ExplainVisualizer/Exp
import { FilterPill } from '@/components/interfaces/QueryPerformance/components/FilterPill'
import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
import { FilterPopover } from '@/components/ui/FilterPopover'
-import TwoOptionToggle from '@/components/ui/TwoOptionToggle'
+import { TwoOptionToggle } from '@/components/ui/TwoOptionToggle'
import { useExecuteSqlMutation } from '@/data/sql/execute-sql-mutation'
import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
@@ -341,7 +341,7 @@ export const QueryInsightsTable = ({
options={['explorer', 'triage']}
activeOption={mode}
borderOverride="border"
- onClickOption={setMode}
+ onClickOption={(mode) => setMode(mode as Mode)}
/>
{appNameFilter.length > 0 ? (
setView(value as 'view' | 'md')}
/>
)}
diff --git a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
index 733de5bfd7b..9006734ec7f 100644
--- a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx
@@ -1,6 +1,5 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { useParams } from 'common'
-import { Lightbulb, Lock, MousePointer2, PlusCircle, Unlock } from 'lucide-react'
+import { Lock, PlusCircle, Unlock } from 'lucide-react'
import Link from 'next/link'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { useState } from 'react'
@@ -18,16 +17,17 @@ import {
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { RoleImpersonationPopover } from '../RoleImpersonationSelector/RoleImpersonationPopover'
-import ViewEntityAutofixSecurityModal from './ViewEntityAutofixSecurityModal'
+import { IndexAdvisorPopover } from './IndexAdvisorPopover'
+import { InsertButton } from './InsertButton'
+import { RealtimeToggle } from './RealtimeToggle'
+import { SecurityDefinerViewPopover } from './SecurityDefinerViewPopover'
+import { ViewEntityAutofixSecurityModal } from './ViewEntityAutofixSecurityModal'
import { RefreshButton } from '@/components/grid/components/header/RefreshButton'
import { useTableIndexAdvisor } from '@/components/grid/context/TableIndexAdvisorContext'
-import { EnableIndexAdvisorButton } from '@/components/interfaces/QueryPerformance/IndexAdvisor/EnableIndexAdvisorButton'
import { getEntityLintDetails } from '@/components/interfaces/TableGridEditor/TableEntity.utils'
import { APIDocsButton } from '@/components/ui/APIDocsButton'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
import { useDatabasePoliciesQuery } from '@/data/database-policies/database-policies-query'
-import { useDatabasePublicationsQuery } from '@/data/database-publications/database-publications-query'
-import { useDatabasePublicationUpdateMutation } from '@/data/database-publications/database-publications-update-mutation'
import { useProjectLintsQuery } from '@/data/lint/lint-query'
import {
Entity,
@@ -51,9 +51,8 @@ export interface GridHeaderActionsProps {
}
export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProps) => {
- const { ref } = useParams()
- const { data: project } = useSelectedProjectQuery()
const track = useTrack()
+ const { data: project } = useSelectedProjectQuery()
const [showWarning, setShowWarning] = useQueryState(
'showWarning',
@@ -84,7 +83,6 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
},
})
- const [showEnableRealtime, setShowEnableRealtime] = useState(false)
const [rlsConfirmModalOpen, setRlsConfirmModalOpen] = useState(false)
const [isAutofixViewSecurityModalOpen, setIsAutofixViewSecurityModalOpen] = useState(false)
@@ -100,32 +98,6 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
(policy) => policy.schema === table.schema && policy.table === table.name
)
- const { data: publications } = useDatabasePublicationsQuery({
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- })
- const realtimePublication = (publications ?? []).find(
- (publication) => publication.name === 'supabase_realtime'
- )
- const realtimeEnabledTables = realtimePublication?.tables ?? []
- const isRealtimeEnabled = realtimeEnabledTables.some((t) => t.id === table?.id)
-
- const { mutate: updatePublications, isPending: isTogglingRealtime } =
- useDatabasePublicationUpdateMutation({
- onSuccess: () => {
- setShowEnableRealtime(false)
-
- track(isRealtimeEnabled ? 'table_realtime_disabled' : 'table_realtime_enabled', {
- method: 'ui',
- schema_name: table.schema,
- table_name: table.name,
- })
- },
- onError: (error) => {
- toast.error(`Failed to toggle realtime for ${table.name}: ${error.message}`)
- },
- })
-
const { can: canSqlWriteTables, isLoading: isLoadingPermissions } = useAsyncCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
'tables'
@@ -163,29 +135,6 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
table.schema
)
- const toggleRealtime = async () => {
- if (!project || !realtimePublication) return
-
- const exists = realtimeEnabledTables.some((x) => x.id === table.id)
- const tables = !exists
- ? [`${table.schema}.${table.name}`].concat(
- realtimeEnabledTables.map((t) => `${t.schema}.${t.name}`)
- )
- : realtimeEnabledTables.filter((x) => x.id !== table.id).map((x) => `${x.schema}.${x.name}`)
-
- track('realtime_toggle_table_clicked', {
- newState: exists ? 'disabled' : 'enabled',
- origin: 'tableGridHeader',
- })
-
- updatePublications({
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- id: realtimePublication.id,
- tables,
- })
- }
-
const closeConfirmModal = () => {
setRlsConfirmModalOpen(false)
}
@@ -319,139 +268,17 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
) : null
) : null}
- {isTable && isIndexAdvisorAvailable && !isIndexAdvisorEnabled && (
-
-
- }>
- Index Advisor
-
-
-
-
- Index Advisor
-
-
-
- Index Advisor recommends indexes to improve query performance on this table.
-
-
- Enable Index Advisor to get recommendations based on your actual query patterns.
-
-
-
-
-
-
-
- )}
-
- {realtimeEnabled && (
-
- }
- onClick={() => setShowEnableRealtime(true)}
- className={cn(isRealtimeEnabled && 'w-7 h-7 p-0 text-brand hover:text-brand-hover')}
- tooltip={{
- content: {
- side: 'bottom',
- text: isRealtimeEnabled
- ? 'Click to disable realtime for this table'
- : 'Click to enable realtime for this table',
- },
- }}
- >
- {!isRealtimeEnabled && 'Enable Realtime'}
-
- )}
-
{isView && viewHasLints && (
-
-
- }>
- Security Definer view
-
-
-
-
- Secure your View
-
-
-
- This view is defined with the Security Definer property, giving it permissions
- of the view's creator (Postgres), rather than the permissions of the querying
- user.
-
-
-
- Since this view is in the public schema, it is accessible via your project's
- APIs.
-
-
-
- {
- setIsAutofixViewSecurityModalOpen(true)
- }}
- >
- Autofix
-
-
-
- Learn more
-
-
-
-
-
-
+ {
+ setIsAutofixViewSecurityModalOpen(true)
+ }}
+ />
)}
{isMaterializedView && materializedViewHasLints && (
-
-
- }>
- Security Definer view
-
-
-
-
- Secure your View
-
-
-
- This view is defined with the Security Definer property, giving it permissions
- of the view's creator (Postgres), rather than the permissions of the querying
- user.
-
-
-
- Since this view is in the public schema, it is accessible via your project's
- APIs.
-
-
-
-
-
- Learn more
-
-
-
-
-
-
+
)}
{isForeignTable && table.schema === 'public' && (
@@ -489,38 +316,19 @@ export const GridHeaderActions = ({ table, isRefetching }: GridHeaderActionsProp
+ {isTable && isIndexAdvisorAvailable && !isIndexAdvisorEnabled && }
+
+ {isTable && realtimeEnabled && }
+
{doesHaveAutoGeneratedAPIDocs && (
)}
+
+ {showHeaderActions && }
)}
- setShowEnableRealtime(false)}
- onConfirm={() => toggleRealtime()}
- >
-
-
- Once realtime has been {isRealtimeEnabled ? 'disabled' : 'enabled'}, the table will{' '}
- {isRealtimeEnabled ? 'no longer ' : ''}broadcast any changes to authorized subscribers.
-
- {!isRealtimeEnabled && (
-
- You may also select which events to broadcast to subscribers on the{' '}
-
- database publications
- {' '}
- settings.
-
- )}
-
-
{
+ return (
+
+
+ }>
+ Index Advisor
+
+
+
+
+ Index Advisor
+
+
+
Index Advisor recommends indexes to improve query performance on this table.
+
Enable Index Advisor to get recommendations based on your actual query patterns.
+
+
+
+
+
+
+ )
+}
diff --git a/apps/studio/components/interfaces/TableGridEditor/InsertButton.tsx b/apps/studio/components/interfaces/TableGridEditor/InsertButton.tsx
new file mode 100644
index 00000000000..ca1451a062d
--- /dev/null
+++ b/apps/studio/components/interfaces/TableGridEditor/InsertButton.tsx
@@ -0,0 +1,156 @@
+import { PermissionAction } from '@supabase/shared-types/out/constants'
+import { useParams } from 'common'
+import { ArrowUp, ChevronDown, FileText } from 'lucide-react'
+import {
+ Button,
+ cn,
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from 'ui'
+
+import { ShortcutBadge } from '@/components/ui/ShortcutBadge'
+import { useSendEventMutation } from '@/data/telemetry/send-event-mutation'
+import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
+import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
+import { SHORTCUT_IDS } from '@/state/shortcuts/registry'
+import { useShortcut } from '@/state/shortcuts/useShortcut'
+import { useTableEditorStateSnapshot } from '@/state/table-editor'
+import { useTableEditorTableStateSnapshot } from '@/state/table-editor-table'
+
+export const InsertButton = () => {
+ const { ref: projectRef } = useParams()
+ const { data: org } = useSelectedOrganizationQuery()
+
+ const snap = useTableEditorTableStateSnapshot()
+ const tableEditorSnap = useTableEditorStateSnapshot()
+ const { can: canCreateColumns } = useAsyncCheckPermissions(
+ PermissionAction.TENANT_SQL_ADMIN_WRITE,
+ 'columns'
+ )
+ const { mutate: sendEvent } = useSendEventMutation()
+
+ const onAddRow =
+ snap.editable && (snap.table.columns ?? []).length > 0 ? tableEditorSnap.onAddRow : undefined
+ const onAddColumn = snap.editable ? tableEditorSnap.onAddColumn : undefined
+ const onImportData = snap.editable ? tableEditorSnap.onImportData : undefined
+
+ const canAddNew = onAddRow !== undefined || onAddColumn !== undefined
+
+ useShortcut(SHORTCUT_IDS.TABLE_EDITOR_INSERT_ROW, () => onAddRow?.(), {
+ registerInCommandMenu: true,
+ enabled: onAddRow !== undefined && canAddNew && canCreateColumns,
+ })
+ useShortcut(SHORTCUT_IDS.TABLE_EDITOR_INSERT_COLUMN, () => onAddColumn?.(), {
+ registerInCommandMenu: true,
+ enabled: onAddColumn !== undefined && canAddNew && canCreateColumns,
+ })
+ useShortcut(SHORTCUT_IDS.TABLE_EDITOR_IMPORT_CSV, () => onImportData?.(), {
+ registerInCommandMenu: true,
+ enabled: onImportData !== undefined && canAddNew && canCreateColumns,
+ })
+
+ if (!canAddNew || !canCreateColumns) return null
+
+ return (
+
+
+ }
+ >
+ Insert
+
+
+
+ {[
+ ...(onAddRow !== undefined
+ ? [
+
+
+ Insert row
+
+ ,
+ ]
+ : []),
+ ...(onAddColumn !== undefined
+ ? [
+
+
+ Insert column
+
+ ,
+ ]
+ : []),
+ ...(onImportData !== undefined
+ ? [
+ {
+ onImportData()
+ sendEvent({
+ action: 'import_data_button_clicked',
+ properties: { tableType: 'Existing Table' },
+ groups: {
+ project: projectRef ?? 'Unknown',
+ organization: org?.slug ?? 'Unknown',
+ },
+ })
+ }}
+ >
+
+ Import data from CSV
+
+ ,
+ ]
+ : []),
+ ]}
+
+
+ )
+}
diff --git a/apps/studio/components/interfaces/TableGridEditor/RealtimeToggle.tsx b/apps/studio/components/interfaces/TableGridEditor/RealtimeToggle.tsx
new file mode 100644
index 00000000000..9cb77a95149
--- /dev/null
+++ b/apps/studio/components/interfaces/TableGridEditor/RealtimeToggle.tsx
@@ -0,0 +1,141 @@
+import { useParams } from 'common'
+import { Realtime } from 'icons'
+import { useState } from 'react'
+import { toast } from 'sonner'
+import {
+ Button,
+ cn,
+ Dialog,
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogSection,
+ DialogSectionSeparator,
+ DialogTitle,
+ DialogTrigger,
+} from 'ui'
+
+import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
+import { InlineLink } from '@/components/ui/InlineLink'
+import { useDatabasePublicationsQuery } from '@/data/database-publications/database-publications-query'
+import { useDatabasePublicationUpdateMutation } from '@/data/database-publications/database-publications-update-mutation'
+import { Entity } from '@/data/table-editor/table-editor-types'
+import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
+import { useTrack } from '@/lib/telemetry/track'
+
+export const RealtimeToggle = ({ table }: { table: Entity }) => {
+ const track = useTrack()
+ const { ref } = useParams()
+ const { data: project } = useSelectedProjectQuery()
+
+ const [open, setOpen] = useState(false)
+
+ const { data: publications } = useDatabasePublicationsQuery({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ })
+ const realtimePublication = (publications ?? []).find(
+ (publication) => publication.name === 'supabase_realtime'
+ )
+ const realtimeEnabledTables = realtimePublication?.tables ?? []
+ const isRealtimeEnabled = realtimeEnabledTables.some((t) => t.id === table?.id)
+
+ const { mutate: updatePublications, isPending: isTogglingRealtime } =
+ useDatabasePublicationUpdateMutation({
+ onSuccess: () => {
+ setOpen(false)
+
+ track(isRealtimeEnabled ? 'table_realtime_disabled' : 'table_realtime_enabled', {
+ method: 'ui',
+ schema_name: table.schema,
+ table_name: table.name,
+ })
+ },
+ onError: (error) => {
+ toast.error(`Failed to toggle realtime for ${table.name}: ${error.message}`)
+ },
+ })
+
+ const toggleRealtime = async () => {
+ if (!project || !realtimePublication) return
+
+ const exists = realtimeEnabledTables.some((x) => x.id === table.id)
+ const tables = !exists
+ ? [`${table.schema}.${table.name}`].concat(
+ realtimeEnabledTables.map((t) => `${t.schema}.${t.name}`)
+ )
+ : realtimeEnabledTables.filter((x) => x.id !== table.id).map((x) => `${x.schema}.${x.name}`)
+
+ track('realtime_toggle_table_clicked', {
+ newState: exists ? 'disabled' : 'enabled',
+ origin: 'tableGridHeader',
+ })
+
+ updatePublications({
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ id: realtimePublication.id,
+ tables,
+ })
+ }
+
+ return (
+
+
+
+ }
+ className={cn('w-7 h-7 p-0', isRealtimeEnabled && 'text-brand hover:text-brand-hover')}
+ tooltip={{
+ content: {
+ side: 'bottom',
+ text: isRealtimeEnabled
+ ? 'Disable Realtime for this table'
+ : 'Enable Realtime for this table',
+ },
+ }}
+ />
+
+
+
+
+ {isRealtimeEnabled ? 'Disable' : 'Enable'} realtime for {table.name}
+
+
+
+
+
+
+ Once realtime has been {isRealtimeEnabled ? 'disabled' : 'enabled'}, the table will{' '}
+ {isRealtimeEnabled ? 'no longer ' : ''}broadcast any changes to authorized
+ subscribers.
+
+ {!isRealtimeEnabled && (
+
+ You may also select which events to broadcast to subscribers on the{' '}
+
+ database publications
+ {' '}
+ settings.
+
+ )}
+
+
+
+ setOpen(false)}>
+ Cancel
+
+
+ {isRealtimeEnabled ? 'Disable' : 'Enable'} realtime
+
+
+
+
+ )
+}
diff --git a/apps/studio/components/interfaces/TableGridEditor/SecurityDefinerViewPopover.tsx b/apps/studio/components/interfaces/TableGridEditor/SecurityDefinerViewPopover.tsx
new file mode 100644
index 00000000000..d512c231dce
--- /dev/null
+++ b/apps/studio/components/interfaces/TableGridEditor/SecurityDefinerViewPopover.tsx
@@ -0,0 +1,56 @@
+import { useParams } from 'common'
+import { Unlock } from 'lucide-react'
+import Link from 'next/link'
+import { Button, Popover_Shadcn_, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_ } from 'ui'
+
+import { type Lint } from '@/data/lint/lint-query'
+
+export const SecurityDefinerViewPopover = ({
+ lint,
+ onAutofix,
+}: {
+ lint: Lint | null
+ onAutofix?: () => void
+}) => {
+ const { ref } = useParams()
+
+ return (
+
+
+ }>
+ Security Definer view
+
+
+
+
+ Secure your view
+
+
+
+ This view is defined with the Security Definer property, giving it permissions of the
+ view's creator (Postgres), rather than the permissions of the querying user.
+
+
+
Since this view is in the public schema, it is accessible via your project's APIs.
+
+
+ {!!onAutofix && (
+
+ Autofix
+
+ )}
+
+
+ Learn more
+
+
+
+
+
+
+ )
+}
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx
index 68320878f56..cc906632951 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx
@@ -10,7 +10,7 @@ import { isValueTruncated } from '../RowEditor.utils'
import { DrilldownViewer } from './DrilldownViewer/DrilldownViewer'
import { JsonCodeEditor } from './JsonCodeEditor'
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
-import TwoOptionToggle from '@/components/ui/TwoOptionToggle'
+import { TwoOptionToggle } from '@/components/ui/TwoOptionToggle'
import { useTableEditorQuery } from '@/data/table-editor/table-editor-query'
import { isTableLike } from '@/data/table-editor/table-editor-types'
import { useGetCellValueMutation } from '@/data/table-rows/get-cell-value-mutation'
@@ -149,7 +149,7 @@ export const JsonEditor = ({
options={['view', 'edit']}
activeOption={view}
borderOverride="border-muted"
- onClickOption={setView}
+ onClickOption={(value) => setView(value as 'view' | 'edit')}
/>
)}
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/TextEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/TextEditor.tsx
index c82999c3a05..7ec00878a57 100644
--- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/TextEditor.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/TextEditor.tsx
@@ -10,7 +10,7 @@ import { Button, cn, SidePanel } from 'ui'
import { ActionBar } from '../ActionBar'
import { isValueTruncated } from './RowEditor.utils'
import { Markdown } from '@/components/interfaces/Markdown'
-import TwoOptionToggle from '@/components/ui/TwoOptionToggle'
+import { TwoOptionToggle } from '@/components/ui/TwoOptionToggle'
import { useTableEditorQuery } from '@/data/table-editor/table-editor-query'
import { isTableLike } from '@/data/table-editor/table-editor-types'
import { useGetCellValueMutation } from '@/data/table-rows/get-cell-value-mutation'
@@ -111,7 +111,7 @@ export const TextEditor = ({
options={['view', 'edit']}
activeOption={view}
borderOverride="border-muted"
- onClickOption={setView}
+ onClickOption={(value) => setView(value as 'view' | 'edit')}
/>
)}
diff --git a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
index 6f78afc56ce..e427f59bcbb 100644
--- a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
@@ -2,6 +2,7 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import Link from 'next/link'
import { useRouter } from 'next/router'
+import { parseAsString, useQueryState } from 'nuqs'
import { useCallback } from 'react'
import { Button } from 'ui'
import { Admonition, GenericSkeletonLoader } from 'ui-patterns'
@@ -22,7 +23,6 @@ import {
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useDashboardHistory } from '@/hooks/misc/useDashboardHistory'
import { useQuerySchemaState } from '@/hooks/misc/useSchemaQueryState'
-import { useUrlState } from '@/hooks/ui/useUrlState'
import { useIsProtectedSchema } from '@/hooks/useProtectedSchemas'
import { TableEditorTableStateContextProvider } from '@/state/table-editor-table'
import { createTabId, useTabsStateSnapshot } from '@/state/tabs'
@@ -48,7 +48,8 @@ export const TableGridEditor = ({
table: selectedTable,
})
- const [{ view: selectedView = 'data' }] = useUrlState()
+ const [selectedView] = useQueryState('view', parseAsString.withDefault('data'))
+
const { can: canEditTables } = useAsyncCheckPermissions(
PermissionAction.TENANT_SQL_ADMIN_WRITE,
'tables'
@@ -67,7 +68,7 @@ export const TableGridEditor = ({
`/project/${projectRef}/editor/${table.id}${!!selectedSchema ? `?schema=${selectedSchema}` : ''}`
)
},
- [projectRef, router]
+ [projectRef, router, selectedSchema]
)
const onTableDeleted = useCallback(async () => {
@@ -82,7 +83,7 @@ export const TableGridEditor = ({
onClearDashboardHistory: () => setLastVisitedTable(undefined),
})
}
- }, [router, selectedTable, tabs])
+ }, [router, selectedTable, setLastVisitedTable, tabs])
const { isSchemaLocked } = useIsProtectedSchema({ schema: selectedTable?.schema ?? '' })
diff --git a/apps/studio/components/interfaces/TableGridEditor/ViewEntityAutofixSecurityModal.tsx b/apps/studio/components/interfaces/TableGridEditor/ViewEntityAutofixSecurityModal.tsx
index 408f8e1545d..39ce5efe34c 100644
--- a/apps/studio/components/interfaces/TableGridEditor/ViewEntityAutofixSecurityModal.tsx
+++ b/apps/studio/components/interfaces/TableGridEditor/ViewEntityAutofixSecurityModal.tsx
@@ -1,7 +1,7 @@
import { useQueryClient } from '@tanstack/react-query'
import { toast } from 'sonner'
import { ScrollArea } from 'ui'
-import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
+import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
import { SimpleCodeBlock } from 'ui-patterns/SimpleCodeBlock'
@@ -17,11 +17,11 @@ interface ViewEntityAutofixSecurityModalProps {
setIsAutofixViewSecurityModalOpen: (isAutofixViewSecurityModalOpen: boolean) => void
}
-export default function ViewEntityAutofixSecurityModal({
+export const ViewEntityAutofixSecurityModal = ({
table,
isAutofixViewSecurityModalOpen,
setIsAutofixViewSecurityModalOpen,
-}: ViewEntityAutofixSecurityModalProps) {
+}: ViewEntityAutofixSecurityModalProps) => {
const { data: project } = useSelectedProjectQuery()
const queryClient = useQueryClient()
const {
diff --git a/apps/studio/components/ui/TwoOptionToggle.tsx b/apps/studio/components/ui/TwoOptionToggle.tsx
index 247f5ea9d9e..4358e9a24b9 100644
--- a/apps/studio/components/ui/TwoOptionToggle.tsx
+++ b/apps/studio/components/ui/TwoOptionToggle.tsx
@@ -1,14 +1,14 @@
import { cn } from 'ui'
interface TwoOptionToggleProps {
- options: any
+ options: string[]
width?: number
- activeOption: any
- onClickOption: any
+ activeOption: string
+ onClickOption: (value: string) => void
borderOverride: string
}
-const TwoOptionToggle = ({
+export const TwoOptionToggle = ({
options,
width = 50,
activeOption,
@@ -35,8 +35,8 @@ const TwoOptionToggle = ({
'z-0 inline-block rounded-sm h-full bg-overlay-hover shadow-sm transform',
'transition-all ease-in-out border border-strong'
)}
- >
- {options.map((option: any, index: number) => (
+ />
+ {options.map((option, index: number) => (
)
}
-
-export default TwoOptionToggle
diff --git a/apps/www/components/SurveyResults/SurveyChart.tsx b/apps/www/components/SurveyResults/SurveyChart.tsx
index 5089446bcee..02e50a42144 100644
--- a/apps/www/components/SurveyResults/SurveyChart.tsx
+++ b/apps/www/components/SurveyResults/SurveyChart.tsx
@@ -1,7 +1,10 @@
'use client'
-import { useEffect, useState, useRef, useCallback } from 'react'
+
import { createClient } from '@supabase/supabase-js'
+import CodeBlock from '~/components/CodeBlock/CodeBlock'
import { motion } from 'framer-motion'
+import { ChevronsUpDown } from 'lucide-react'
+import { useCallback, useEffect, useRef, useState } from 'react'
import {
Button,
DropdownMenu,
@@ -9,9 +12,8 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from 'ui'
-import { ChevronsUpDown } from 'lucide-react'
-import TwoOptionToggle from '../../../studio/components/ui/TwoOptionToggle'
-import CodeBlock from '~/components/CodeBlock/CodeBlock'
+
+import { TwoOptionToggle } from '../../../studio/components/ui/TwoOptionToggle'
// Separate Supabase client for survey project
const externalSupabase = createClient(
@@ -340,7 +342,7 @@ export function SurveyChart({
handleViewChange(value as 'sql' | 'chart')}
borderOverride="border-overlay"
/>
diff --git a/e2e/studio/features/queue-table-operations.spec.ts b/e2e/studio/features/queue-table-operations.spec.ts
index bbc357bd4f6..c191d781310 100644
--- a/e2e/studio/features/queue-table-operations.spec.ts
+++ b/e2e/studio/features/queue-table-operations.spec.ts
@@ -182,7 +182,7 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('new row value')
await page.getByTestId('action-bar-save-row').click()
@@ -224,12 +224,12 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('row one')
await page.getByTestId('action-bar-save-row').click()
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('row two')
await page.getByTestId('action-bar-save-row').click()
@@ -269,12 +269,12 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('keep this row')
await page.getByTestId('action-bar-save-row').click()
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('remove this row')
await page.getByTestId('action-bar-save-row').click()
@@ -316,7 +316,7 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('shortcut test')
await page.getByTestId('action-bar-save-row').click()
@@ -359,7 +359,7 @@ test.describe('Queue Table Operations', () => {
await expect(page.getByRole('gridcell', { name: 'existing row' })).toBeVisible()
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('undo this row')
await page.getByTestId('action-bar-save-row').click()
@@ -395,12 +395,12 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('first row')
await page.getByTestId('action-bar-save-row').click()
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('second row')
await page.getByTestId('action-bar-save-row').click()
@@ -608,7 +608,7 @@ test.describe('Queue Table Operations', () => {
await page.getByRole('menuitem', { name: 'Delete row' }).click()
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('new row')
await page.getByTestId('action-bar-save-row').click()
@@ -636,9 +636,7 @@ test.describe('Queue Table Operations', () => {
await using _ = await withSetupCleanup(
async () => {
- await createTable(tableName, columnName, [
- { name: 'existing row' },
- ])
+ await createTable(tableName, columnName, [{ name: 'existing row' }])
},
async () => {
await dropTable(tableName)
@@ -655,7 +653,7 @@ test.describe('Queue Table Operations', () => {
// Add a new row
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('new row')
await page.getByTestId('action-bar-save-row').click()
@@ -710,7 +708,7 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('pending in table 1')
await page.getByTestId('action-bar-save-row').click()
@@ -719,7 +717,7 @@ test.describe('Queue Table Operations', () => {
await page.getByRole('button', { name: `View ${tableName2}`, exact: true }).click()
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('pending in table 2')
await page.getByTestId('action-bar-save-row').click()
@@ -764,7 +762,7 @@ test.describe('Queue Table Operations', () => {
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('only in table 1')
await page.getByTestId('action-bar-save-row').click()
@@ -776,7 +774,7 @@ test.describe('Queue Table Operations', () => {
// Add a row in table 2
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('only in table 2')
await page.getByTestId('action-bar-save-row').click()
@@ -846,10 +844,10 @@ test.describe('Queue Table Operations', () => {
last_name text
)`
)
- await query(
- `INSERT INTO ${tableName} (first_name, last_name) VALUES ($1, $2)`,
- ['Alice', 'Smith']
- )
+ await query(`INSERT INTO ${tableName} (first_name, last_name) VALUES ($1, $2)`, [
+ 'Alice',
+ 'Smith',
+ ])
},
async () => {
await dropTable(tableName)
diff --git a/e2e/studio/features/table-editor.spec.ts b/e2e/studio/features/table-editor.spec.ts
index 1fc0bc481f3..66038d9219c 100644
--- a/e2e/studio/features/table-editor.spec.ts
+++ b/e2e/studio/features/table-editor.spec.ts
@@ -243,7 +243,7 @@ testRunner('table editor', () => {
// insert row with enum value
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByText('Insert a new row into').click()
+ await page.getByText('Insert row').click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'value1' }).click()
await page.getByTestId('action-bar-save-row').click()
@@ -252,7 +252,7 @@ testRunner('table editor', () => {
// insert row with another enum value
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByText('Insert a new row into').click()
+ await page.getByText('Insert row').click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'value2' }).click()
await page.getByTestId('action-bar-save-row').click()
@@ -290,7 +290,7 @@ testRunner('table editor', () => {
// create 3 rows
for (const value of ['789', '456', '123']) {
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId('pw_column-input').fill(value)
const insertPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
@@ -514,7 +514,7 @@ testRunner('table editor', () => {
for (const value of ['789', '456', '123']) {
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${colName}-input`).fill(value)
const insertPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
@@ -769,7 +769,7 @@ testRunner('table editor', () => {
// Insert first row with value 'first_row_value'
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${colName}-input`).fill('first_row_value')
const insertFirstPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
@@ -779,7 +779,7 @@ testRunner('table editor', () => {
// Insert second row with value 'second_row_value'
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${colName}-input`).fill('second_row_value')
const insertSecondPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
@@ -862,7 +862,7 @@ testRunner('table editor', () => {
// Insert a row with TRUE value via side panel
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'TRUE' }).click()
const insertTruePromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
@@ -878,7 +878,7 @@ testRunner('table editor', () => {
// Insert a row with FALSE value via side panel
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'FALSE' }).click()
const insertFalsePromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
@@ -983,7 +983,7 @@ testRunner('table editor', () => {
// Insert a row with TRUE value
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'TRUE' }).click()
const insertTruePromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
@@ -999,7 +999,7 @@ testRunner('table editor', () => {
// Insert a row with FALSE value
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByRole('combobox').click()
await page.getByRole('option', { name: 'FALSE' }).click()
const insertFalsePromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
@@ -1354,7 +1354,7 @@ testRunner('table editor', () => {
.toBe('229|229|t')
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId('name-input').fill('Dave')
const insertPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
@@ -1433,7 +1433,7 @@ testRunner('table editor', () => {
.toBe('229|229|t')
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId('name-input').fill('Dave')
const insertPromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=', {
method: 'POST',
@@ -1472,7 +1472,7 @@ testRunner('table editor', () => {
// Open side panel to insert a new row
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
await page.getByTestId(`${columnName}-input`).fill('immediate insert')
// Wait for the POST mutation to complete when saving
@@ -1769,7 +1769,7 @@ testRunner('table editor', () => {
// Open side panel to insert a new row
await page.getByTestId('table-editor-insert-new-row').click()
- await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
+ await page.getByRole('menuitem', { name: 'Insert row' }).click()
// Open the field actions dropdown and set the text column to NULL
await page.getByTestId(`${columnName}-field-actions`).click()