🤖 Written by Claude
Problem
genes/templates/genes/tags/gene_grid_tag.html contains ~2,170 lines of JS. Roughly a third of it is a hand-rolled reactive table: global arrays (enrichmentKits, geneLists, pathologyTestVersions, evidenceColumns, …) hold the state, the DOM is a second copy, and createRowColumns / createRowColumnsFromData / addGeneDataRows / setCell / gcEmptyRows / removeColumn reconcile the two by hand — dynamic columns keyed by CSS class, sorted row insertion, colspan bookkeeping, enrichment-kit sub-columns. Another ~150 lines is a hand-rolled table→CSV exporter. That plumbing is the hardest part to change safely and none of it is specific to genes.
Proposal
Port the grid to Tabulator (MIT, 6.x, no build step, coexists with jQuery). It is column-oriented with dynamic columns as a first-class API, which is exactly the shape of this feature:
table.addColumn() / column.delete() replaces all column reconciliation and colspan handling
- Column groups replace
addSubColumns and the headerRow special-casing for enrichment kits
index: "gene" + updateOrAddData() replaces the sorted merge-insert in addGeneDataRows; sorting is free
- Per-column
formatter — each column type (gene list, enrichment kit, gene annotation release, PanelApp, GenCC, lab classification counts, pathology test version) becomes a self-contained module: fetch → column def + formatter. Lazy cells render a spinner and row.update() when the existing batch fetch resolves (keep the ~100 lines of debounced batching)
titleFormatter for header icons / close / copy buttons; cellMouseEnter/cellMouseLeave for hover-to-add
table.download("csv") deletes the CSV exporter
- Frozen gene column + virtual DOM rendering — large PanelApp panels × many columns will render noticeably faster than the current full-DOM approach
rowFormatter for alias / unmatched row styling
Expected outcome: ~2,170 → ~800–1,000 lines, mostly domain logic organised per column type.
Considered and passed on
- DataTables (already in the project) — row-oriented; adding/removing columns needs destroy/re-init. Poor fit for this grid specifically.
- AG Grid Community — would work but heavier than needed.
- Alpine / petite-vue — would remove the reconciliation code but leaves column groups, sorting and CSV to hand-write, and there's no precedent for a reactive framework in the codebase.
- Server-render via htmx — the URL is already the column source of truth, but the editable gene-list / pathology-test interactions and batched lazy coverage fetches make this a larger redesign.
Prerequisite
Extracting the JS from the template into js/gene_grid.js (config via json_script, callbacks via a geneGridInit(config, callbacks) bootstrap) is being done first as a separate, behaviour-preserving change. The port should start from that file.
Suggested first step
Prototype with two column types — a gene list and an enrichment kit column group with lazy coverage — to confirm the fit before porting the rest.
🤖 Written by Claude
Problem
genes/templates/genes/tags/gene_grid_tag.htmlcontains ~2,170 lines of JS. Roughly a third of it is a hand-rolled reactive table: global arrays (enrichmentKits,geneLists,pathologyTestVersions,evidenceColumns, …) hold the state, the DOM is a second copy, andcreateRowColumns/createRowColumnsFromData/addGeneDataRows/setCell/gcEmptyRows/removeColumnreconcile the two by hand — dynamic columns keyed by CSS class, sorted row insertion, colspan bookkeeping, enrichment-kit sub-columns. Another ~150 lines is a hand-rolled table→CSV exporter. That plumbing is the hardest part to change safely and none of it is specific to genes.Proposal
Port the grid to Tabulator (MIT, 6.x, no build step, coexists with jQuery). It is column-oriented with dynamic columns as a first-class API, which is exactly the shape of this feature:
table.addColumn()/column.delete()replaces all column reconciliation and colspan handlingaddSubColumnsand theheaderRowspecial-casing for enrichment kitsindex: "gene"+updateOrAddData()replaces the sorted merge-insert inaddGeneDataRows; sorting is freeformatter— each column type (gene list, enrichment kit, gene annotation release, PanelApp, GenCC, lab classification counts, pathology test version) becomes a self-contained module: fetch → column def + formatter. Lazy cells render a spinner androw.update()when the existing batch fetch resolves (keep the ~100 lines of debounced batching)titleFormatterfor header icons / close / copy buttons;cellMouseEnter/cellMouseLeavefor hover-to-addtable.download("csv")deletes the CSV exporterrowFormatterfor alias / unmatched row stylingExpected outcome: ~2,170 → ~800–1,000 lines, mostly domain logic organised per column type.
Considered and passed on
Prerequisite
Extracting the JS from the template into
js/gene_grid.js(config viajson_script, callbacks via ageneGridInit(config, callbacks)bootstrap) is being done first as a separate, behaviour-preserving change. The port should start from that file.Suggested first step
Prototype with two column types — a gene list and an enrichment kit column group with lazy coverage — to confirm the fit before porting the rest.