interaction in d3js with other chart/data

Hi, I have a chart that, when clicked, allows me to filter an external dataset that I display in an Input.Table. The interaction works fine, however, I need the chart to be displayed inside a div. I’m using Observable Framework, and this is all the code.

function grid_rank(data, {width}={}) {

  /* ---------- 1. derived sets & helpers ------------------- */
  const officials = Array.from(new Set(data.map(d => d.Official)));
  const statuses  = Array.from(new Set(data.map(d => d.Status)));      // P columns

  const maxByOfficial = Object.fromEntries(
    d3.groups(data, d => d.Official)
       .map(([ofc, arr]) => [ofc, d3.max(arr, d => d.Order)])
  );
  const globalMaxOrder = d3.max(data, d => d.Order);
  let clicked = null; 
  /* count proposals per (Official, Status) */
  const counts = d3.rollup(
    data,
    v => v.length,
    d => d.Official,
    d => d.Status
  ); // Map<Official, Map<Status, count>>

  /* avg Days per Status (across ALL officials) */
  const avgDays = Object.fromEntries(
    d3.groups(data, d => d.Status)
       .map(([st, arr]) => [st, d3.mean(arr, d => d.Days)])
  );
  
  /* target look-up */
  const targetMap = Object.fromEntries(
    tobe_ans_stages.map(d => [d.statusTime, d.target])
  );

  const headerColour = st => {
    const diff = (targetMap[st] ?? 0) - (avgDays[st] ?? 0);
    if (diff >= 2) return "#006BA2";           // green
    if (diff >= 0) return "#FFD700";           // yellow
    return "#a81414";                          // red
  };

  /* ---------- 2. layout & scales -------------------------- */
  const margin   = { top: 36, right: 30, bottom: 20, left: 160 };
  const rowH     = 50;
  const bandW    = 48;                         // width per status column
  const gap      = 28;                         // space between grids

  const height   = officials.length * rowH + margin.top + margin.bottom;

  const xStatus  = d3.scaleBand()
    .domain(statuses)
    .range([margin.left, margin.left + statuses.length * bandW])
    .paddingInner(0.15);

  const xOrder   = d3.scaleLinear()
    .domain([1, globalMaxOrder])
    .range([xStatus.range()[1] + gap, width - margin.right]);

  const yScale   = d3.scaleBand()
    .domain(officials)
    .range([margin.top, height - margin.bottom])
    .paddingInner(0.35);

  const sym = {
    "Under Review":       d3.symbolCircle,
    "Under Consultation": d3.symbolSquare,
    "Report Prep":        d3.symbolTriangle
  };

  /* ---------- 3. root SVG & tooltip ----------------------- */
   const div = d3.create("div")
    .style("overflow-x", "auto")
    .style("font-variant-numeric", "tabular-nums");

  const svg = div.append("svg")
    .attr("viewBox", [0, 0, width, height])
    .attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");

  const tooltip = d3.select("body").append("div")
    .style("position", "absolute")
    .style("pointer-events", "none")
    .style("visibility", "hidden")
    .style("background", "#fff")
    .style("border", "1px solid #ccc")
    .style("border-radius", "4px")
    .style("padding", "6px 8px")
    .style("font-size", "12px");
  
  const element = div.node();
  element.value = null;

  /* ---------- 4. grid lines ------------------------------- */
  /* status-grid verticals */
  statuses.forEach(st => {
    const x = xStatus(st);
    svg.append("line")
      .attr("x1", x).attr("x2", x)
      .attr("y1", margin.top).attr("y2", height - margin.bottom)
      .attr("stroke", "#000").attr("stroke-dasharray", "2,2")
      .attr("stroke-width", 1).attr("opacity", 0.35);
  });
  /* trailing boundary */
  svg.append("line")
     .attr("x1", xStatus.range()[1]).attr("x2", xStatus.range()[1])
     .attr("y1", margin.top).attr("y2", height - margin.bottom)
     .attr("stroke", "#000").attr("stroke-dasharray", "2,2")
     .attr("stroke-width", 1).attr("opacity", 0.35);

  /* order-grid verticals */
  for (let i = 1; i <= globalMaxOrder; ++i) {
    svg.append("line")
      .attr("x1", xOrder(i)).attr("x2", xOrder(i))
      .attr("y1", margin.top).attr("y2", height - margin.bottom)
      .attr("stroke", "#000").attr("stroke-dasharray", "2,2")
      .attr("stroke-width", 1).attr("opacity", 0.35);
  }

  /* horizontals + left label */
  officials.forEach(ofc => {
    const y = yScale(ofc);
    svg.append("line")
      .attr("x1", margin.left)
      .attr("x2", xOrder(globalMaxOrder))
      .attr("y1", y).attr("y2", y)
      .attr("stroke", "#000").attr("stroke-dasharray", "2,2")
      .attr("stroke-width", 1).attr("opacity", 0.35);

    svg.append("text")
      .attr("x", margin.left - 10).attr("y", y).attr("dy", "0.35em")
      .style("text-anchor", "end").style("font-size", 12)
      .text(`${ofc} (Pptas: ${maxByOfficial[ofc]})`);
  });

  /* ---------- 5. status-grid cell counts ----------------- */
  officials.forEach(ofc => {
    statuses.forEach(st => {
      const cnt = counts.get(ofc)?.get(st) ?? 0;
      svg.append("text")
        .attr("x", xStatus(st) + xStatus.bandwidth() / 2)
        .attr("y", yScale(ofc)).attr("dy", "0.35em")
        .style("text-anchor", "middle").style("font-size", 13)
        .text(cnt);
    });
  });

  /* ---------- 6. header shapes (colour by avg vs target) -- */
  statuses.forEach(st => {
    svg.append("path")
      .datum({ status: st })
      .attr("d", d3.symbol().type(sym[st]).size(240)())
      .attr("transform", `translate(${xStatus(st) + xStatus.bandwidth() / 2}, ${margin.top - 18})`)
      .attr("fill", headerColour(st))
      .attr("stroke", "black")
      .style("cursor", "pointer")      // visual cue
      .on("click", click)
  });



  /* ---------- 7. order-grid proposal shapes -------------- */
  const colourByThreshold = d => {
    const diff = d.Threshold - d.Days;
    if (diff >= 2) return "#006BA2";
    if (diff >= 0) return "#FFD700";
    return "#a81414";
  };

  data.forEach(d => {
    const cx = xOrder(d.Order);
    const cy = yScale(d.Official);
    svg.append("path")
      .datum(d)
      .attr("d", d3.symbol().type(sym[d.Status]).size(250)())
      .attr("transform", `translate(${cx}, ${cy})`)
      .attr("fill", colourByThreshold(d))
      .attr("stroke", "black")
      .style("cursor", "pointer") 
      .on("mouseover", (event, d) => {
        tooltip.style("visibility", "visible")
          .html(`PptNo: ${d.PptNo}<br/>Banker: ${d.Banker}<br/>Status: ${d.Status}<br/>Client: ${d.Client}`);
      })
      .on("mousemove", event => {
        tooltip.style("top", `${event.pageY + 10}px`)
               .style("left", `${event.pageX + 10}px`);
      })
      .on("mouseout", () => tooltip.style("visibility", "hidden"))
      .on("click", click)
  });

  function click(_, datum) {
    if (datum ) {

      clicked = datum;

      element.value = datum;
    } else {
      clicked = null;

      element.value = null;
    }
    element.dispatchEvent(new CustomEvent("input"));
  }
  /* ---------- 8. return ------------------------------- */
  //return svg.node();
  return element;
}
const sel_leader= view(Inputs.select(d3.group(data_grid, (d) => d.Banker),{width:7}));
const sel_office = view(Inputs.select(d3.group(data_grid, (d) => d.Official),{width:7}));
const selection_plot = view(grid_rank(data_grid,{width}));
display(Inputs.table( selection_plot === null ? data_grid : data_grid.filter((d) => d.Status === selection_plot.status)))

I tried to create the interaction using Mutable and d3.dispatch without any good results. My question is whether the interaction I created is correct or is there a better one, and how can I get the chart and table to display in a div?

Thanks in advance.