Skip to content

Commit f61bece

Browse files
committed
(cellcard) Handle cell card hierarchy behaviour
1 parent 7e2cb7a commit f61bece

7 files changed

Lines changed: 318 additions & 108 deletions

File tree

src/components/CellCards/CellCard/CellCard.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ const CellCard = ({ cell, data, group, termSlug, discussionHref, onNavigateToCel
6363
cell={cell}
6464
hierarchy={data.hierarchy}
6565
rootLabel={data.entry?.rootClass?.split(":").pop() || "root"}
66+
// Same resolver the graph uses: a cell stays in the card, anything else opens its own page.
67+
onNavigate={onNavigateToRef}
6668
actions={commentFor(HIERARCHY_TITLE)}
6769
/>,
6870
<AnatomicalContext
Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { useEffect, useState } from "react";
2-
import { loadOntology, findCell } from "../services/ontologyGridService";
1+
import { useEffect, useMemo, useState } from "react";
2+
import { loadOntology, peekOntology, findCell } from "../services/ontologyGridService";
33
import { ONTOLOGY_CATALOG, DEFAULT_ONTOLOGY_SLUG } from "../config/gridConfig";
44

55
/**
@@ -11,41 +11,64 @@ import { ONTOLOGY_CATALOG, DEFAULT_ONTOLOGY_SLUG } from "../config/gridConfig";
1111
* arriving from a grid tile costs no network and no reparse, while a cold deep-link still
1212
* works on its own.
1313
*
14+
* A parse already in that cache is resolved *during render* (`peekOntology`) rather than in the
15+
* effect. Going through the effect would report `loading` for one frame on every term change,
16+
* and that frame swaps the card for its skeleton — unmounting the widgets and losing their
17+
* state. The Ontology Hierarchy widget has to stay stationary while the rest of the card changes
18+
* around it (spec §3.2), which it cannot do if it is remounted on the way.
19+
*
1420
* The card's data never comes from the InterLex term API — Precision cells are npokb-only and
1521
* that endpoint 404s on them. `ontologySlug` is the *context* ontology (the `?ontology=` param
1622
* written by the grid), which is a different thing from `DataContext.activeOntology`: the
1723
* latter is an edit target, not a data source.
1824
*/
1925
const useCellTerm = (termSlug, ontologySlug = DEFAULT_ONTOLOGY_SLUG) => {
20-
const [state, setState] = useState({ cell: null, data: null, loading: true, error: null });
26+
const slug = ontologySlug && ONTOLOGY_CATALOG[ontologySlug] ? ontologySlug : DEFAULT_ONTOLOGY_SLUG;
27+
// Carries the slug it was resolved against, so a switch of context ontology cannot be served
28+
// the previous ontology's cell for the render before the effect runs.
29+
const [state, setState] = useState({ slug, cell: null, data: null, loading: true, error: null });
30+
const cached = peekOntology(slug);
2131

2232
useEffect(() => {
23-
const slug = ontologySlug && ONTOLOGY_CATALOG[ontologySlug] ? ontologySlug : DEFAULT_ONTOLOGY_SLUG;
2433
if (!termSlug) {
25-
setState({ cell: null, data: null, loading: false, error: null });
34+
setState({ slug, cell: null, data: null, loading: false, error: null });
2635
return undefined;
2736
}
37+
// Already parsed: resolved synchronously below, so there is nothing to load or to flag.
38+
if (peekOntology(slug)) return undefined;
2839

2940
let active = true;
30-
setState((prev) => ({ ...prev, loading: true, error: null }));
41+
setState({ slug, cell: null, data: null, loading: true, error: null });
3142

3243
loadOntology(slug)
3344
.then((data) => {
3445
if (!active) return;
3546
// A term that is not in this ontology is not an error — the tab simply has nothing to
3647
// show, and the caller renders/hides accordingly.
37-
setState({ cell: findCell(data, termSlug) || null, data, loading: false, error: null });
48+
setState({ slug, cell: findCell(data, termSlug) || null, data, loading: false, error: null });
3849
})
3950
.catch((error) => {
40-
if (active) setState({ cell: null, data: null, loading: false, error });
51+
if (active) setState({ slug, cell: null, data: null, loading: false, error });
4152
});
4253

4354
return () => {
4455
active = false;
4556
};
46-
}, [termSlug, ontologySlug]);
57+
}, [termSlug, slug]);
4758

48-
return state;
59+
return useMemo(() => {
60+
if (cached) {
61+
return {
62+
cell: termSlug ? findCell(cached, termSlug) || null : null,
63+
data: cached,
64+
loading: false,
65+
error: null,
66+
};
67+
}
68+
// State left over from another ontology says nothing about this one; the effect is loading it.
69+
if (state.slug !== slug) return { cell: null, data: null, loading: true, error: null };
70+
return { cell: state.cell, data: state.data, loading: state.loading, error: state.error };
71+
}, [cached, termSlug, slug, state]);
4972
};
5073

5174
export default useCellTerm;

0 commit comments

Comments
 (0)