Improve Table Editor design and accessibility (#50797)

## Problem

Controls in the new table sheet's Columns section had inconsistent
styling, alignment and focus states. Some icon-only actions were faint
or lacked useful accessible names and tooltips, while related controls
used different dropdown treatments.

<img width="1428" height="250" alt="image"
src="https://github.com/user-attachments/assets/9d1ad80d-2de0-4736-8ddb-0260e10ddacc"
/>

## Solution

- Align the column headers and fields on the same grid, and keep the
foreign key action inside the name field in every state.
- Give the drag, foreign key, suggestions, extra options and remove
actions consistent button styling, sizing, hover and focus states, with
accessible names and tooltips.
- Move masking into Extra options and include it in the options count.
Remove the separators around the former inline controls.
- Use the design system's ComboboxTrigger for column types and align the
Default value select's radius with it. Update the suggestions dropdown
icon.
- Tidy related field labels and update the affected table editor tests.

The underlying column values and database behaviour are unchanged.

| Original before | Original after |
| --- | --- |
| <img width="1498" height="392" alt="CleanShot 2026-09-24 at 13 33
40@2x"
src="https://github.com/user-attachments/assets/56559e72-f289-44bc-999f-c1f3158c8f65"
/> | <img width="1496" height="416" alt="CleanShot 2026-09-24 at 13 32
42@2x"
src="https://github.com/user-attachments/assets/d05340fc-a460-4b5c-aaff-41127f692592"
/> |

## Review instructions

- Open **Table Editor → New table** and inspect the Columns section.
Check that Name, Type and Default value headers align with their fields.
- Tab through the row actions. Check their focus states, accessible
names and tooltips, including the foreign key control with and without a
relation.
- Drag a column to reorder it, open Extra options and toggle masking.
Check the options count and that the other settings still work.
- Compare the closed Type and Default value dropdowns, including an enum
default value.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Improvements**
* Updated the column editor layout and controls for naming,
sensitive-data settings, extra options, foreign-key editing, and column
removal, with clearer tooltips and more consistent interactions.
* Added accessible help tooltips for the Name and Default Value fields.
* Updated column-type and suggested-value controls, including clearer
guidance for choosing a suggested value.
  * Sensitive-data settings are now available in the extra-options menu.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Danny White <3104761+dnywh@users.noreply.github.com>
This commit is contained in:
Gildas GarciaandDanny White authored and GitHub committed 2026-09-24 12:37:30 +02:00
1 parent 0d3b73794b
commit ca28c945db
8 files changed
+371 -322

No files matched your search

@@ -58,8 +58,8 @@ test.describe('column editor complex types', () => {
'Type dropdown should show the bare base type, not the raw _int4 pg format'
).toBeVisible()
await expect(
columnEditor.getByRole('checkbox', { name: 'Define as Array' }),
'Define as Array should be checked for an existing array column'
columnEditor.getByRole('checkbox', { name: 'Define as array' }),
'Define as array should be checked for an existing array column'
).toBeChecked()
// Make an unrelated change and save - this must not corrupt the column's type.
+8 -8
View File
@@ -263,10 +263,10 @@ testRunner('table editor', () => {
await page.getByRole('button', { name: 'New table', exact: true }).click()
await page.getByTestId('table-name-input').fill(tableNameEnum)
await page.getByTestId('created_at-extra-options').click()
await page.getByText('Is Nullable').click()
await page.getByText('Is nullable').click()
await page.getByTestId('created_at-extra-options').click()
await page.getByRole('button', { name: 'Add column' }).click()
await page.getByLabel('Column name').nth(2).fill(columnNameEnum)
await page.getByLabel('Column name').nth(3).fill(columnNameEnum)
await page.getByRole('combobox').filter({ hasText: 'Choose a column type...' }).click()
await page.getByPlaceholder('Search types...').fill(enum_name)
// wait for response, then click
@@ -357,7 +357,7 @@ testRunner('table editor', () => {
.click()
await page.getByRole('menuitem', { name: 'Edit table' }).click()
await page.getByTestId('table-name-input').fill(tableNameUpdated)
await page.getByLabel('Column name').nth(2).fill(columnNameUpdated)
await page.getByLabel('Column name').nth(3).fill(columnNameUpdated)
const updateTablePromise = waitForApiResponse(page, 'pg-meta', ref, 'query?key=column-update', {
method: 'POST',
})
@@ -948,7 +948,7 @@ testRunner('table editor', () => {
// Add boolean column
await page.getByRole('button', { name: 'Add column' }).click()
await page.getByLabel('Column name').nth(3).fill(boolColName)
await page.getByLabel('Column name').nth(4).fill(boolColName)
await page.getByText('Choose a column type...').click()
await page.getByPlaceholder('Search types...').fill('bool')
await page.getByRole('option', { name: 'bool' }).first().click()
@@ -1064,7 +1064,7 @@ testRunner('table editor', () => {
// Add nullable boolean column
await page.getByRole('button', { name: 'Add column' }).click()
await page.getByLabel('Column name').nth(3).fill(boolColName)
await page.getByLabel('Column name').nth(4).fill(boolColName)
await page.getByText('Choose a column type...').click()
await page.getByPlaceholder('Search types...').fill('bool')
await page.getByRole('option', { name: 'bool' }).first().click()
@@ -1985,14 +1985,14 @@ testRunner('table editor', () => {
await page.getByRole('button', { name: 'New table' }).click()
await page.getByLabel('Name', { exact: true }).fill(tableName)
await page.getByRole('button', { name: 'Add column' }).click()
await page.getByLabel('Column name').nth(2).fill('pw_column')
await page.getByLabel('Column name').nth(3).fill('pw_column')
await page.getByRole('combobox').filter({ hasText: 'Choose a column type...' }).click()
await page.getByRole('option').filter({ hasText: 'int8' }).click()
await page.getByLabel('Column default value').nth(2).fill('invalid')
await page.getByLabel('Column default value').nth(3).fill('invalid')
await page.getByRole('button', { name: 'Save' }).click()
await expect(page.getByText('invalid input syntax')).toBeVisible()
await page.getByLabel('Column default value').nth(2).fill('10')
await page.getByLabel('Column default value').nth(3).fill('10')
await page.getByRole('button', { name: 'Save' }).click()
await expect(page.getByText(`Table ${tableName} is good to go!`)).toBeVisible()
await expect(page.getByRole('button', { name: `View ${tableName}`, exact: true })).toBeVisible()