Skip to content

Commit 0ca3d5c

Browse files
atrakhConvex, Inc.
authored andcommitted
dashboard: add button to hide and show fields on the data page (#42526)
Adds the ability to toggle visibility of columns in the data page. This ui also supports re-ordering fields via a drag-and-droppable list. Also implements a constraint that we'll only try to render up to 25 fields to start, and will allow the user to manually select more fields to render GitOrigin-RevId: a9fc1165a4a9ce2ce8eef90d1d2219c770d10a81
1 parent 5548e01 commit 0ca3d5c

File tree

12 files changed

+664
-20
lines changed

12 files changed

+664
-20
lines changed

npm-packages/common/config/rush/pnpm-lock.yaml

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

npm-packages/dashboard-common/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"@headlessui/react": "1.7.19",
2828
"@heroicons/react": "2.2.0",
2929
"@monaco-editor/react": "4.7.0",
30+
"@radix-ui/react-switch": "~1.2.6",
3031
"@radix-ui/react-tooltip": "~1.2.0",
3132
"@tanstack/react-table": "~8.21.0",
3233
"acorn": "~8.8.1",

npm-packages/dashboard-common/src/features/data/components/DataContent.tsx

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@ import {
3737
DataToolbarSkeleton,
3838
} from "@common/features/data/components/DataToolbar/DataToolbar";
3939
import { EmptyDataContent } from "@common/features/data/components/EmptyData";
40-
import { useDataColumns } from "@common/features/data/components/Table/utils/useDataColumns";
40+
import {
41+
useDataColumns,
42+
useStoredHiddenColumns,
43+
useStoredColumnOrder,
44+
} from "@common/features/data/components/Table/utils/useDataColumns";
4145
import { useQueryFilteredTable } from "@common/features/data/components/Table/utils/useQueryFilteredTable";
4246
import { useSingleTableSchemaStatus } from "@common/features/data/components/TableSchema";
4347
import { DataFilters } from "@common/features/data/components/DataFilters/DataFilters";
@@ -130,6 +134,77 @@ export function DataContent({
130134
width: (ref.current?.getSize() || 1000) - 3,
131135
});
132136

137+
const [hiddenColumnsRaw, setHiddenColumnsRaw] =
138+
useStoredHiddenColumns(localStorageKey);
139+
140+
// Default to showing only 25 fields (including _id and _creationTime)
141+
const hiddenColumns = useMemo(() => {
142+
if (hiddenColumnsRaw !== undefined) {
143+
return hiddenColumnsRaw;
144+
}
145+
146+
// First time - hide fields beyond the first 25
147+
// Ensure _id and _creationTime are always visible
148+
const visibleFields: string[] = [];
149+
const allTableFields = [...tableFields];
150+
151+
// Add _id and _creationTime first if they exist
152+
if (allTableFields.includes("_id")) {
153+
visibleFields.push("_id");
154+
}
155+
if (allTableFields.includes("_creationTime")) {
156+
visibleFields.push("_creationTime");
157+
}
158+
159+
// Add remaining fields up to 25 total
160+
for (const field of allTableFields) {
161+
if (
162+
field !== "_id" &&
163+
field !== "_creationTime" &&
164+
visibleFields.length < 25
165+
) {
166+
visibleFields.push(field);
167+
}
168+
}
169+
170+
// Hide everything else
171+
return allTableFields.filter((field) => !visibleFields.includes(field));
172+
}, [hiddenColumnsRaw, tableFields]);
173+
174+
// Wrap the setter to handle undefined -> [] conversion for functional updates
175+
const setHiddenColumns = useCallback(
176+
(newHiddenColumns: string[] | ((prev: string[]) => string[])) => {
177+
if (typeof newHiddenColumns === "function") {
178+
setHiddenColumnsRaw((prev) => newHiddenColumns(prev || []));
179+
} else {
180+
setHiddenColumnsRaw(newHiddenColumns);
181+
}
182+
},
183+
[setHiddenColumnsRaw],
184+
);
185+
186+
// Column order management
187+
const [columnOrderRaw, setColumnOrderRaw] =
188+
useStoredColumnOrder(localStorageKey);
189+
190+
const columnOrder = useMemo(() => columnOrderRaw || [], [columnOrderRaw]);
191+
192+
// Wrap the setter to handle undefined -> [] conversion for functional updates
193+
const setColumnOrder = useCallback(
194+
(newColumnOrder: string[] | ((prev: string[]) => string[])) => {
195+
if (typeof newColumnOrder === "function") {
196+
setColumnOrderRaw((prev) => newColumnOrder(prev || []));
197+
} else {
198+
setColumnOrderRaw(newColumnOrder);
199+
}
200+
},
201+
[setColumnOrderRaw],
202+
);
203+
204+
// Use tableFields directly for the combobox instead of deriving from columns
205+
// to avoid circular dependencies
206+
const allFields = useMemo(() => ["*select", ...tableFields], [tableFields]);
207+
133208
const listRef = useRef<FixedSizeList>(null);
134209

135210
const scrollToTop = useCallback(() => listRef.current?.scrollToItem(0), []);
@@ -282,6 +357,11 @@ export function DataContent({
282357
hasFilters={hasFiltersAndAtLeastOneDocument}
283358
showFilters={showFilters}
284359
setShowFilters={setShowFilters}
360+
allFields={allFields}
361+
hiddenColumns={hiddenColumns}
362+
setHiddenColumns={setHiddenColumns}
363+
columnOrder={columnOrder}
364+
setColumnOrder={setColumnOrder}
285365
/>
286366
)}
287367

@@ -307,6 +387,7 @@ export function DataContent({
307387
/>
308388
)}
309389
<Table
390+
key={columnOrder.join(",")}
310391
activeSchema={activeSchema}
311392
listRef={listRef}
312393
loadMore={loadNextPage}
@@ -338,6 +419,8 @@ export function DataContent({
338419
setPopup={popupState.setPopup}
339420
deleteRows={deleteRows}
340421
defaultDocument={defaultDocument}
422+
hiddenColumns={hiddenColumns}
423+
onColumnOrderChange={setColumnOrder}
341424
onAddDraftFilter={(filter: Filter) => {
342425
setDraftFilters((prev) =>
343426
prev

npm-packages/dashboard-common/src/features/data/components/DataFilters/DataFilters.stories.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,10 @@ export const Default: Story = {
6262
numRowsLoaded: 0,
6363
hasFilters: true,
6464
showFilters: true,
65+
allFields: ["*select", "myColumn"],
66+
hiddenColumns: [],
67+
setHiddenColumns: fn(),
68+
columnOrder: [],
69+
setColumnOrder: fn(),
6570
},
6671
};

npm-packages/dashboard-common/src/features/data/components/DataFilters/DataFilters.tsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { api } from "system-udfs/convex/_generated/api";
4747
import { Index } from "@common/features/data/lib/api";
4848
import { IndexFilters, getDefaultIndex } from "./IndexFilters";
4949
import { clearFilters } from "./clearFilters";
50+
import { FieldSelector } from "./FieldSelector";
5051

5152
export function DataFilters({
5253
defaultDocument,
@@ -64,6 +65,11 @@ export function DataFilters({
6465
hasFilters,
6566
showFilters,
6667
setShowFilters,
68+
allFields,
69+
hiddenColumns,
70+
setHiddenColumns,
71+
columnOrder,
72+
setColumnOrder,
6773
}: {
6874
defaultDocument: GenericDocument;
6975
tableName: string;
@@ -80,6 +86,11 @@ export function DataFilters({
8086
hasFilters: boolean;
8187
showFilters: boolean;
8288
setShowFilters: React.Dispatch<React.SetStateAction<boolean>>;
89+
allFields: string[];
90+
hiddenColumns: string[];
91+
setHiddenColumns: (hiddenColumns: string[]) => void;
92+
columnOrder: string[];
93+
setColumnOrder: (columnOrder: string[]) => void;
8394
}) {
8495
const { selectedNent } = useNents();
8596
const indexes =
@@ -152,11 +163,11 @@ export function DataFilters({
152163
key={currentIdx}
153164
>
154165
<div className="flex flex-col">
155-
<div className="flex justify-between gap-2">
156-
<div className="flex items-center">
166+
<div className="scrollbar flex justify-between gap-4 overflow-x-auto">
167+
<div className="flex items-center gap-2">
157168
<div
158169
className={cn(
159-
"flex w-full rounded-lg border bg-background-secondary",
170+
"flex w-full min-w-fit overflow-hidden rounded-lg border bg-background-secondary",
160171
showFilters && "rounded-b-none border-b-0",
161172
)}
162173
>
@@ -202,6 +213,13 @@ export function DataFilters({
202213
open={showFilters}
203214
/>
204215
</div>
216+
<FieldSelector
217+
allFields={allFields}
218+
hiddenColumns={hiddenColumns}
219+
setHiddenColumns={setHiddenColumns}
220+
columnOrder={columnOrder}
221+
setColumnOrder={setColumnOrder}
222+
/>
205223
</div>
206224
<div className="flex gap-2">
207225
{numRowsWeKnowOf !== undefined && (

0 commit comments

Comments
 (0)