Skip to content

Handle footnotes, grouped rows, and multi-value cells in filterable tables - #4354

Open
alexandra5000 wants to merge 4 commits into
mainfrom
fix/filterable-table-edge-cases
Open

alexandra5000 wants to merge 4 commits into
mainfrom
fix/filterable-table-edge-cases

Conversation

@alexandra5000

@alexandra5000 alexandra5000 commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

The :filterable: option now derives and matches dropdown values correctly on tables with footnotes, grouped section rows, or cells that list several values. A plain table is unaffected.

Affects: Site UI, Authoring

Trying the merged :filterable: option on real tables — the EDOT components list and the AWS Firehose data-type table — turned up cases where the auto-generated filters misread the content.

Why

The filter reads each cell's text to build its dropdowns and to match rows, and three kinds of real content break that. A footnote reference is part of the cell text, so a footnoted Core reads as Core 1 and becomes its own option, and selecting Core drops the footnoted rows. A grouped table's section-header rows (first cell a heading, the rest empty) count as data rows, so the total is wrong, and they disappear the moment a filter runs. A cell that lists several comma-separated values never equals any single option, so a row that sends both Logs and Metrics is dropped when the reader picks Metrics.

What

Footnote markers no longer leak into values

A cell's value is now read with footnote references (.footnote-ref) removed. A footnoted Core contributes Core, so the component list offers Core, Deprecated, and Extended rather than seven near-duplicates, and filtering to Core keeps the footnoted components.

Section-header rows are structure, not data

A row whose first cell is filled and whose other cells are empty is treated as a heading: excluded from the count, and shown only while its group still has a visible row. If every row looks like a heading — which happens when a lazily rendered column is still empty — none is treated as one, and the observer re-reads once the column fills.

A multi-value cell contributes each value

A cell is split on commas, so Logs, Metrics offers Logs and Metrics separately, and a row matches a filter when any of its values matches. A service that sends both is no longer dropped by a single-value filter.

Verify

cd src/Elastic.Documentation.Site && npm run test -- FilterableTable
# ignores footnote markers… / splits a comma-separated cell… / excludes section-header rows…

Then serve a grouped table and confirm the count and the dropdown options:

dotnet run --project src/tooling/docs-builder -- serve --path <elastic-agent>/docs
# open /reference/edot-collector/components

Out of scope: the comma split treats every comma as a value separator, so an atomic low-cardinality value that contains a comma would be split wrongly. No such column exists in the tables seen so far; flagged in #4282 for a follow-up if one turns up.

🤖 Generated with Claude Code

…ables

Three edge cases surfaced when the :filterable: option was tried on real
tables (the EDOT components list and the AWS Firehose data-type table):

- A footnote reference is part of a cell's text, so a footnoted "Core"
  scraped as "Core 1" and split into its own dropdown option. Footnote
  markers are now stripped before a cell's value is read.
- A grouped table uses rows whose first cell is a heading and whose other
  cells are empty. Those rows inflated the count and vanished under a
  filter. They are now excluded from the count and kept visible only while
  their group has a matching row. If every row looks like a heading, none
  is, so a late-rendering column does not misclassify the whole table.
- A cell listing several comma-separated values ("Logs, Metrics") never
  matched a single dropdown option. Such a cell now contributes each value,
  and a row matches a filter for any of them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Docs preview (local build)

Handbook preview: https://docs-v3-preview.elastic.dev/elastic/docs-builder/pull/4354/

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for a functional regression: multi-value columns can lose their facet under the current key-column heuristic.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@alexandra5000
alexandra5000 marked this pull request as draft October 7, 2026 12:42
Tokenizing comma-separated cells made the key-column guard compare a
token count against the row count, so a category column whose tokens are
shared across rows (for example three rows of "Logs, Metrics" /
"Metrics, Traces" / "Traces, Logs") was mistaken for a unique-key column
and lost its dropdown. A column now counts as a key column only when
every row holds exactly one value and those values are all unique.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: the new section-header heuristic currently misclassifies legitimate sparse data rows as structural headers, which can remove real rows from counts/facets and hide them during filtering.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

The first version classed any row with a filled first cell and empty rest
as a section header, which also matched a sparse data row whose trailing
cells are blank - removing it from the count and hiding it under a filter.
A section heading is now recognised only when its sole filled cell is
emphasized (`|***Receivers***|||||`), the convention these grouped tables
use. A plain sparse data row stays data. The failure direction is safe: a
non-emphasized heading is merely counted as data, never the reverse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes for a remaining correctness issue in footnote value extraction that can still produce mismatched facet values for valid nested-markup cells.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

cellText skipped only direct-child footnote-ref nodes, so a footnote
written inside emphasis (`**Core[^1]**`, rendered as
`<strong>Core<a class="footnote-ref">…</a></strong>`) left the digit in
the value, giving "Core1". It now walks the cell and skips a footnote-ref
subtree at any depth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
// heading as data only miscounts by one, while the reverse would drop a
// real row from the count and hide it under a filter.
const emphasis = first.querySelector('strong, em, b, i')
return emphasis?.textContent?.trim() === heading

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[LOW] Emphasized sparse data rows can still be treated as section headers

The current heuristic still classifies <tr><td><strong>alpha</strong></td><td></td></tr> as a section row (emphasis?.textContent?.trim() === heading), so that row is removed from this.rows and can disappear from both counts and search/facet results.

This is a real behavior change vs. pre-grouping logic: a sparse-but-data row with emphasized first-cell text becomes unfilterable because readRows() excludes it as structure and applyFilters() only toggles it as a heading.

Could we tighten this with a structural marker emitted by rendering (or another non-presentational discriminator), so emphasis alone does not decide row semantics?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, so technically this is true, the filter is guessing which rows are section headings.

Markdown has no real way to mark a row as a heading, so the filter guesses from looks: first cell bold, other cells empty means it will be treated as a heading. That works today, but a real data row that happens to be bold-and-blank would be mistaken for a heading and vanish when someone filters, although that is rare and to my knowledge is not a real table case in the docs anywhere.

The proper fix would be of course to give authors a way to mark a section heading in the {table} directive and have the filter read that instead. But TBH I don't know if we really want to do that. Anyway that's a feature, not a bug fix. Noted on the main tracking issue, #4282, for any bots reading it in the future (if there is any).

This branch was successfully deployed

2 active deployments
docs-preview — ec4c8ed9 Deployed Oct 7, 2026 by github-actions[bot]
assembler-preview — ec4c8ed9 Deployed Oct 7, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant