Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
Form,
FormControl,
FormField,
Input,
Input_Shadcn_,
} from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import { z } from 'zod'

Expand Down Expand Up @@ -149,15 +149,25 @@ export const CreateNewProjectDialog = ({
control={form.control}
name="password"
render={({ field }) => (
<FormItemLayout>
<FormItemLayout
label="Database password"
description={
<PasswordStrengthBar
passwordStrengthScore={passwordStrengthScore as PasswordStrengthScore}
password={field.value}
passwordStrengthMessage={passwordStrengthMessage}
generateStrongPassword={generatePassword}
/>
}
>
<FormControl>
<Input
id="db-password"
label="Database password"
type="password"
placeholder="Type in a strong password"
value={field.value}
copy={field.value?.length > 0}
reveal
onChange={(e) => {
const value = e.target.value
field.onChange(value)
Expand All @@ -166,14 +176,6 @@ export const CreateNewProjectDialog = ({
setPasswordStrengthMessage('')
} else checkPasswordStrength(value)
}}
descriptionText={
<PasswordStrengthBar
passwordStrengthScore={passwordStrengthScore as PasswordStrengthScore}
password={field.value}
passwordStrengthMessage={passwordStrengthMessage}
generateStrongPassword={generatePassword}
/>
}
/>
</FormControl>
</FormItemLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import { AlertCircle, Search } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import {
Card,
Input,
InputGroup,
InputGroupAddon,
InputGroupInput,
ShadowScrollArea,
Table,
TableBody,
Expand Down Expand Up @@ -79,16 +81,19 @@ export const Extensions = () => {
return (
<>
<div className="mb-4">
<Input
inputRef={searchInputRef}
size="tiny"
placeholder="Search for an extension"
value={filterString}
onChange={(e) => setFilterString(e.target.value)}
onKeyDown={onSearchInputEscape(filterString, setFilterString)}
className="w-52"
icon={<Search />}
/>
<InputGroup className="w-52">
<InputGroupInput
ref={searchInputRef}
size="tiny"
placeholder="Search for an extension"
value={filterString}
onChange={(e) => setFilterString(e.target.value)}
onKeyDown={onSearchInputEscape(filterString, setFilterString)}
/>
<InputGroupAddon>
<Search />
</InputGroupAddon>
</InputGroup>
</div>

{isPermissionsLoaded && !canUpdateExtensions && (
Expand All @@ -112,8 +117,8 @@ export const Extensions = () => {
<TableHead key="description">Description</TableHead>
<TableHead key="used-by">Used by</TableHead>
<TableHead key="links">Links</TableHead>
{/*
[Joshen] All these classes are just to make the last column sticky
{/*
[Joshen] All these classes are just to make the last column sticky
I reckon we can pull these out into the Table component where we can declare
sticky columns via props, but we can do that if we start to have more tables
in the dashboard with sticky columns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { includes, map as lodashMap, uniqBy } from 'lodash'
import { Search } from 'lucide-react'
import { parseAsBoolean, useQueryState } from 'nuqs'
import { useState } from 'react'
import { Input } from 'ui'
import { InputGroup, InputGroupAddon, InputGroupInput } from 'ui'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'

import { HooksListEmpty } from './HooksListEmpty'
Expand Down Expand Up @@ -47,14 +47,17 @@ export const HooksList = () => {
return (
<div className="w-full space-y-4">
<div className="flex items-center justify-between">
<Input
placeholder="Search for a webhook"
size="tiny"
icon={<Search />}
value={filterString}
className="w-52"
onChange={(e) => setFilterString(e.target.value)}
/>
<InputGroup className="w-52">
<InputGroupInput
size="tiny"
placeholder="Search for a webhook"
value={filterString}
onChange={(e) => setFilterString(e.target.value)}
/>
<InputGroupAddon>
<Search />
</InputGroupAddon>
</InputGroup>
<div className="flex items-center gap-x-2">
<DocsButton href={`${DOCS_URL}/guides/database/webhooks`} />
<ButtonTooltip
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,17 @@ import { DatabaseZap, Search } from 'lucide-react'
import { parseAsBoolean, parseAsJson, parseAsString, useQueryState } from 'nuqs'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'sonner'
import { Card, Input, Table, TableBody, TableHead, TableHeader, TableRow } from 'ui'
import {
Card,
InputGroup,
InputGroupAddon,
InputGroupInput,
Table,
TableBody,
TableHead,
TableHeader,
TableRow,
} from 'ui'
import { EmptyStatePresentational } from 'ui-patterns'
import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'

Expand Down Expand Up @@ -243,16 +253,19 @@ execute function function_name();`
onOpenChange={setSchemaSelectorOpen}
/>
</Shortcut>
<Input
inputRef={searchInputRef}
placeholder="Search for a trigger"
size="tiny"
icon={<Search />}
value={filterString}
className="w-full lg:w-52"
onChange={(e) => setFilterString(e.target.value)}
onKeyDown={onSearchInputEscape(filterString, setFilterString)}
/>
<InputGroup className="w-full lg:w-52">
<InputGroupInput
ref={searchInputRef}
size="tiny"
placeholder="Search for a trigger"
value={filterString}
onChange={(e) => setFilterString(e.target.value)}
onKeyDown={onSearchInputEscape(filterString, setFilterString)}
/>
<InputGroupAddon>
<Search />
</InputGroupAddon>
</InputGroup>
<ReportsSelectFilter
label="Table"
options={tables.map((type) => ({ label: type, value: type }))}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Expand } from 'lucide-react'
import { Button, cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui'

import { formatCellValue, isLargeValue } from './Results.utils'

interface ResultCellProps {
column: string
value: unknown
onContextMenu: (e: React.MouseEvent, column: string, value: unknown) => void
onExpand: (column: string, value: unknown) => void
}

export const ResultCell = ({ column, value, onContextMenu, onExpand }: ResultCellProps) => {
const showExpand = isLargeValue(value)

return (
<div
className={cn(
'group/cell relative flex items-center h-full font-mono text-xs w-full whitespace-pre',
value === null && 'text-foreground-lighter'
)}
onContextMenu={(e) => {
e.preventDefault()
onContextMenu(e, column, value)
}}
>
{formatCellValue(value)}
{showExpand && (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="default"
size="tiny"
className="absolute right-1 top-1/2 -translate-y-1/2 px-1 opacity-0 group-hover/cell:opacity-100 focus-visible:opacity-100"
icon={<Expand size={10} />}
aria-label="View full cell content"
onClick={(e) => {
e.stopPropagation()
onExpand(column, value)
}}
/>
</TooltipTrigger>
<TooltipContent side="left">View full cell content</TooltipContent>
</Tooltip>
)}
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Copy, Expand } from 'lucide-react'
import { useCallback, useMemo, useRef, useState } from 'react'
import DataGrid, { CalculatedColumn } from 'react-data-grid'
import {
cn,
ContextMenu_Shadcn_,
ContextMenuContent_Shadcn_,
ContextMenuItem_Shadcn_,
Expand All @@ -11,12 +10,12 @@ import {
} from 'ui'

import { CellDetailPanel } from './CellDetailPanel'
import { formatCellValue, formatClipboardValue } from './Results.utils'
import { ResultCell } from './ResultCell'
import { formatClipboardValue } from './Results.utils'
import { handleCellKeyDown } from '@/components/grid/SupabaseGrid.utils'

export const Results = ({ rows }: { rows: readonly any[] }) => {
const [expandCell, setExpandCell] = useState(false)
const [cellPosition, setCellPosition] = useState<{ column: any; row: any; rowIdx: number }>()
const [expandedCell, setExpandedCell] = useState<{ column: string; value: any } | null>(null)
const contextMenuCellRef = useRef<{ column: string; value: any } | null>(null)
const triggerRef = useRef<HTMLDivElement>(null)

Expand Down Expand Up @@ -72,23 +71,14 @@ export const Results = ({ rows }: { rows: readonly any[] }) => {
frozen: false,
sortable: false,
isLastFrozenColumn: false,
renderCell: ({ row }: { row: any }) => {
const cellValue = row[key]
return (
<div
className={cn(
'flex items-center h-full font-mono text-xs w-full whitespace-pre',
cellValue === null && 'text-foreground-lighter'
)}
onContextMenu={(e) => {
e.preventDefault()
handleContextMenu(e, key, cellValue)
}}
>
{formatCellValue(cellValue)}
</div>
)
},
renderCell: ({ row }: { row: any }) => (
<ResultCell
column={key}
value={row[key]}
onContextMenu={handleContextMenu}
onExpand={(column, value) => setExpandedCell({ column, value })}
/>
),
renderHeaderCell: () => columnRender(key),
}
}),
Expand Down Expand Up @@ -123,7 +113,10 @@ export const Results = ({ rows }: { rows: readonly any[] }) => {
</ContextMenuItem_Shadcn_>
<ContextMenuItem_Shadcn_
className="gap-x-2"
onSelect={() => setExpandCell(true)}
onSelect={() => {
const cell = contextMenuCellRef.current
if (cell) setExpandedCell({ column: cell.column, value: cell.value })
}}
onFocusCapture={(e) => e.stopPropagation()}
>
<Expand size={12} />
Expand All @@ -136,14 +129,13 @@ export const Results = ({ rows }: { rows: readonly any[] }) => {
rows={rows}
className="grow min-h-0 border-t-0"
rowClass={() => '[&>.rdg-cell]:items-center'}
onSelectedCellChange={setCellPosition}
onCellKeyDown={handleCellKeyDown}
/>
<CellDetailPanel
column={cellPosition?.column.name ?? ''}
value={cellPosition?.row?.[cellPosition.column.name]}
visible={expandCell}
onClose={() => setExpandCell(false)}
column={expandedCell?.column ?? ''}
value={expandedCell?.value}
visible={expandedCell !== null}
onClose={() => setExpandedCell(null)}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
convertResultsToMarkdown,
formatResults,
getResultsHeaders,
isLargeValue,
} from './Results.utils'

describe('Results.utils', () => {
Expand Down Expand Up @@ -102,6 +103,52 @@ describe('Results.utils', () => {
})
})

describe('isLargeValue', () => {
it('returns false for null', () => {
expect(isLargeValue(null)).toBe(false)
})

it('returns false for undefined', () => {
expect(isLargeValue(undefined)).toBe(false)
})

it('returns false for an empty string', () => {
expect(isLargeValue('')).toBe(false)
})

it('returns false for a short string under the threshold', () => {
expect(isLargeValue('hello')).toBe(false)
})

it('returns false for a string at the 60-char boundary', () => {
expect(isLargeValue('a'.repeat(60))).toBe(false)
})

it('returns true for a string just over the 60-char threshold', () => {
expect(isLargeValue('a'.repeat(61))).toBe(true)
})

it('returns true for a short string containing a newline', () => {
expect(isLargeValue('hello\nworld')).toBe(true)
})

it('returns true for an object', () => {
expect(isLargeValue({ a: 1 })).toBe(true)
})

it('returns true for an array', () => {
expect(isLargeValue([1, 2, 3])).toBe(true)
})

it('returns false for a number', () => {
expect(isLargeValue(42)).toBe(false)
})

it('returns false for a boolean', () => {
expect(isLargeValue(true)).toBe(false)
})
})

describe('convertResultsToCSV', () => {
it('should return undefined for empty results', () => {
expect(convertResultsToCSV([])).toBeUndefined()
Expand Down
Loading
Loading