feat: Airtable-style single-row toolbar for list views#447
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…-row layout Row 1: View tabs/switcher (optional) Row 2: Tool buttons (Hide Fields, Filter, Group, Sort, Color, Row Height) + expandable Search - Compact h-7 toolbar buttons with smaller icons (3.5x3.5) - Expandable search (collapsed by default, click to expand) - Placeholder buttons for future features (Hide Fields, Group, Color, Row Height) - Updated tests to match new expandable search UX Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot 你没搞懂,我需要的就是第二行好看的,第一行,丑的要死的直接把它删掉,哪有什么管理软件有两行工具栏的,你不会动脑筋思考吗? |
…tyle ListView toolbar - Removed filter/sort/search controls from ObjectView toolbar (delegated to ListView) - Removed unused imports (FilterUI, SortUI, Search, Input, RefreshCw) and searchQuery state - ObjectView toolbar now only renders named view tabs + create button when needed - Single clean Airtable toolbar row: Hide fields | Filter | Group | Sort | Color | Row height | Search Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
已修复,commit |
There was a problem hiding this comment.
Pull request overview
Redesigns list/object view toolbars to follow a more compact Airtable-style pattern by consolidating ListView controls into a compact toolbar and stripping ObjectView’s duplicate toolbar controls.
Changes:
- Reworked
ListViewtoolbar UI: compact tool buttons on the left + expandable Search on the right (with optional view tabs row). - Simplified
ObjectViewtoolbar to only show named view tabs / view switcher / create button, delegating filter/sort/search toListView. - Updated unit tests to reflect the new Search interaction and the removal of ObjectView search.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| packages/plugin-list/src/ListView.tsx | Implements new compact toolbar with tool buttons + expandable search UI. |
| packages/plugin-view/src/ObjectView.tsx | Removes duplicate toolbar controls from ObjectView and trims imports/UI. |
| packages/plugin-list/src/tests/ListView.test.tsx | Updates tests for “search button → expand → input” flow. |
| packages/plugin-view/src/tests/ObjectView.test.tsx | Updates expectations to assert ObjectView no longer renders search. |
Comments suppressed due to low confidence (1)
packages/plugin-view/src/tests/ObjectView.test.tsx:128
- These tests assert that ObjectView never renders a search box (even when
showSearchis provided). SinceshowSearchis still part of theObjectViewSchematype, this effectively enshrines a breaking behavior change. Please either (1) keep search support in ObjectView when used standalone and add a test coveringshowSearch: true, or (2) remove/deprecateshowSearchfrom the schema/types and update docs/tests accordingly.
it('should not render search box (delegated to ListView toolbar)', () => {
const schema: ObjectViewSchema = {
type: 'object-view',
objectName: 'contacts',
};
render(<ObjectView schema={schema} dataSource={mockDataSource} />);
expect(screen.queryByPlaceholderText(/search/i)).toBeNull();
});
it('should not render search box when showSearch is false', () => {
const schema: ObjectViewSchema = {
type: 'object-view',
objectName: 'contacts',
showSearch: false,
};
render(<ObjectView schema={schema} dataSource={mockDataSource} />);
expect(screen.queryByPlaceholderText(/search/i)).toBeNull();
});
| <Button | ||
| variant="ghost" | ||
| size="sm" | ||
| className="absolute right-0.5 top-1/2 -translate-y-1/2 h-5 w-5 p-0 hover:bg-muted-foreground/20" | ||
| onClick={() => { | ||
| handleSearchChange(''); | ||
| setSearchExpanded(false); | ||
| }} | ||
| > | ||
| <X className="h-3 w-3" /> | ||
| </Button> |
There was a problem hiding this comment.
The clear (X) button inside the expanded search input is icon-only and has no accessible name. Add an aria-label (e.g., "Clear search") or an sr-only text node so screen readers can identify the control.
| <Button | ||
| variant="ghost" | ||
| size="sm" | ||
| className="h-7 px-2 text-muted-foreground hover:text-primary text-xs" | ||
| onClick={() => setSearchExpanded(true)} | ||
| > | ||
| <Search className="h-3.5 w-3.5 mr-1.5" /> | ||
| <span className="hidden sm:inline">Search</span> | ||
| </Button> |
There was a problem hiding this comment.
The collapsed Search button hides its text label via hidden sm:inline, so on small screens it may end up with no accessible name (icon-only). Provide an aria-label/title or sr-only text so it remains discoverable and so tests can reliably query it by accessible name.
| // Render toolbar — only named view tabs; filter/sort/search is handled by ListView | ||
| const renderToolbar = () => { | ||
| const showSearchBox = schema.showSearch !== false; | ||
| const showCreateButton = schema.showCreate !== false && operations.create !== false; | ||
| const showRefreshButton = schema.showRefresh !== false; | ||
| const showViewSwitcherToggle = schema.showViewSwitcher === true; // Changed: default to false (hidden) | ||
|
|
||
| const namedViewTabs = renderNamedViewTabs(); | ||
|
|
||
| // Hide toolbar entirely if there is nothing to show | ||
| if (!namedViewTabs && !showViewSwitcherToggle && !showCreateButton && !toolbarAddon) return null; | ||
|
|
There was a problem hiding this comment.
After removing the toolbar controls (FilterUI/SortUI/Search/Refresh), there is still leftover filter/sort/search/refresh-related logic in this component (e.g., schema flags like showSearch/showFilters/showRefresh, and the Filter/Sort schema generation + refresh handler). That code path is now unused and is likely to trigger lint failures for unused variables, and it also makes the ObjectView API misleading. Either reintroduce controls when ObjectView is used standalone, or remove/deprecate the related schema flags and delete the unused logic to match the new toolbar responsibilities.
| const searchInput = screen.getByPlaceholderText(/find/i); | ||
| expect(searchInput).toBeInTheDocument(); | ||
| const searchButton = screen.getByRole('button', { name: /search/i }); | ||
| expect(searchButton).toBeInTheDocument(); |
There was a problem hiding this comment.
This test queries the Search control by accessible name, but the implementation currently derives that name from text that is visually hidden via Tailwind's hidden class (CSS not applied in jsdom). Once the button becomes icon-only on small screens, the accessible name may be empty unless an aria-label/sr-only label is added. Consider asserting against an explicit aria-label (and add it in the component) so the test reflects real accessibility behavior.
| expect(searchButton).toBeInTheDocument(); | |
| expect(searchButton).toBeInTheDocument(); | |
| // Ensure the search control exposes a stable accessible name via aria-label | |
| expect(searchButton).toHaveAttribute('aria-label', expect.stringMatching(/search/i)); |
| <Button | ||
| variant="ghost" | ||
| size="sm" | ||
| className="h-7 px-2 text-muted-foreground hover:text-primary text-xs" | ||
| disabled | ||
| > | ||
| <EyeOff className="h-3.5 w-3.5 mr-1.5" /> | ||
| <span className="hidden sm:inline">Hide fields</span> | ||
| </Button> |
There was a problem hiding this comment.
Several toolbar buttons rely on a label wrapped in hidden sm:inline, which is display:none on small screens. That removes the accessible name, leaving icon-only buttons without an aria label (e.g., Hide fields / Filter / Group / Sort / Color / Row height). Add an aria-label (and optionally title) or use an sr-only label so the buttons remain accessible when the visible text is hidden.

Redesigns the ListView toolbar to match Airtable's compact single-row toolbar pattern and removes the duplicate ObjectView toolbar.
Changes
Hide fields|Filter|Group|Sort|Color|Row height) left-aligned, expandableSearchright-alignedh-7ghost buttons withtext-xslabels and3.5x3.5iconsHide fields,Group,Color,Row heightrendered as disabled placeholders for future implementationFilterUI,SortUI,Search,Input,RefreshCw) and unusedsearchQuerystate from ObjectViewAffected packages
@object-ui/plugin-list—ListView.tsx@object-ui/plugin-view—ObjectView.tsx💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.