Skip to content

Pick union variants from chips when a list has two or more - #4371

Open
akira28 wants to merge 19 commits into
mainfrom
api-explorer/variant-chips
Open

akira28 wants to merge 19 commits into
mainfrom
api-explorer/variant-chips

Conversation

@akira28

@akira28 akira28 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

API Explorer variant lists with two or more variants now show one variant at a time, picked from a row of chips like the examples rail. From eleven variants the chips' "more" menu gets a filter. On the Elasticsearch and Kibana pages, 646 of 660 variant lists, on 237 pages, get chips; 13 of them get the filter, among them the 24 connector configurations.

Affects: API reference, Site UI

Prompt summary: The author asked to reuse the examples rail's chips, with their "more" menu and search, for long lists of union variants. Lists of 3–10 variants get chips, and lists of 11 or more get chips with a filter. After review, the author asked for chips from two variants, primitives included, so that every union reads the same way.

Why

A variant list with more than two variants folded behind "show type options" and then opened as one long column. A reader looking for one connector scrolled through 24 of them. The fold also rendered its hidden attribute HTML-encoded, as hidden="until-found". Browsers read that as plain hidden, so find-in-page never reached a folded list.

What

Chips pick the variant

A list with two or more variants renders a chip per variant above the variants and shows the first one. Clicking a chip, or an entry in the "+N more" menu, shows that variant. A chip drops the words every name in its list shares, so the connector secrets read bedrock, crowdstrike rather than bedrock_secrets, crowdstrike_secrets; the "more" menu uses the same short labels, and the full name stays as the tooltip on both. Each menu entry also names the discriminator value, such as type: eql, so the filter matches it too. The picked variant shows its fields at once, with no "show properties" step. Its heading drops the name the chip already shows and keeps only the discriminator value or the link to the type's own page. This includes a list such as TotalHits or number: a primitive variant keeps its type name, which is all its panel has to show. Only a lone variant renders without chips, so the "or" separator between variants is gone. An X[] / X pair is two object variants, so each side carries its own fields.

Hidden variants stay reachable

The other variants stay in the page with hidden="until-found", and their anchors are unchanged. Find-in-page reveals a match and switches the chip. A link to a field inside a variant that is not picked switches every chip list around it, on operation and schema pages. This includes the case where the browser already revealed the variant before the script ran. The Markdown export still lists every variant.

One chip row for examples and variants

The packing, the "more" menu, its filter and its keyboard handling move from api-examples-carousel.ts into chip-row.ts. Each caller passes the selectors of its own markup. The examples rail's tests pass unchanged. The menu now also moves back against its button when the filter shrinks it, on both.

The old fold is gone

The "show type options" markup, its two click handlers, its beforematch binding and its CSS are removed, along with ShouldCollapse and the union list's UseHiddenUntilFound.

Where to look

Every form a union takes in the API Explorer, on this PR's preview. A request body may start collapsed; open it to see the row.

Form Example Link
Primitive union, named in the type track_total_hits reads boolean | number open
Options row naming the members knn.filter: "One of: QueryContainer[] or QueryContainer" open
Single value or array, fields listed rescore: "A single Rescore or an array of them" open
Chips, an object and a primitive hits.total: TotalHits or number open
Chips, two objects mget response docs: GetResult or MultiGetError open
Chips with a primitive variant indices.query: string, QueryContainer, RoleTemplateQuery open
Chips with a discriminator value repository types, type: azure, type: gcs open
Chips on a request body, short labels rule types: Eql, Query, SavedQuery open
Chips with a filter (11 or more) 24 connector configurations: bedrock, email, … open
Link to a field in a variant not picked switches the chip to index_config open
Chips nested inside a picked variant entries of a trusted-apps exception item open
Array body whose items are a union "An array; each item is one of:" open
Single or array of a union more_like_this.like: "Like or Like[]; each Like is one of:" open
Required field sets incident: "Requires at least one of: correlation_id or externalId" open
Repeated union, linked error.caused_by: "Same fields as" an earlier listing open
Schema page union, chips with a filter Aggregate, 73 variants open

Verify

dotnet run --project tests/Elastic.ApiExplorer.Tests
# Render_TwoObjects_ShowChips, Render_OneObjectAndAPrimitive_ShowChips, Render_OneVariant_ShowsItWithoutChips
# Render_ThreeVariants_ShowsChipsAndOnlyTheFirstVariant
# Render_ElevenVariants_AddsAFilterToTheMenu
cd src/Elastic.Documentation.Site && npm run test
# api-union-chips.test.ts, api-docs-hash.test.ts, api-examples-carousel.test.ts
cd ../.. && dotnet run --project src/tooling/docs-builder -- serve
# /api/doc/kibana/operation/operation-post-actions-connector-id/ → Request → config
# /api/doc/kibana/operation/operation-post-actions-connector-id/#req-config-variant-index_config-index

In a 1,322-page comparison against main, every text change is the "show type options" toggle giving way to chips, or a removed "or" separator.

🤖 Generated with Claude Code

Long variant lists, such as the 24 connector configurations, folded
behind "show type options" and then scrolled as one long column. Lists
with three or more variants now show one variant at a time, picked from
a row of chips like the examples rail. From eleven variants the "more"
menu gets a filter. The other variants stay in the page with
hidden="until-found", so find-in-page and links to their fields switch
the chip.

The chip row moves out of the examples carousel into `chip-row.ts`.
Both now use it, each with its own selectors, and the examples tests
pass unchanged. The old fold rendered its `hidden` attribute
HTML-encoded, so find-in-page never reached a folded list; it is gone.

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

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Docs preview (local build)

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

Clicking a chip showed the variant's name again below the chips, such
as `{} bedrock_config`, and then a "show properties" toggle. Picking a
chip is already the choice to read that variant. Its fields now show
straight away, and the heading keeps only what the chip does not say:
the discriminator value, or the link to the type's own page. Lists of
two variants keep their names and toggles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each variant before the picked one still took 8px: hidden="until-found"
keeps an element's box, and with it the variant's padding. The further
down the list a chip was, the lower its variant started. Hidden variants
now take no padding. Near the bottom of the page, a shorter variant also
made the browser clamp the scroll, which moved the chips under the
pointer. The variant list now keeps the height that holds the scroll
position.

Co-Authored-By: Claude Opus 5.5 <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 due to a runtime UI regression: variant-switch scroll compensation can leave a stale min-height on the variants container, causing persistent extra whitespace after bottom-of-page switches. Please fix that behavior, then this looks ready for another pass.


What is this? | From workflow: PR Review

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

const maxScroll = document.documentElement.scrollHeight - window.innerHeight
const shortfall = scrolled - maxScroll
if (shortfall <= 0) return
list.style.minHeight = `${list.offsetHeight + shortfall}px`

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.

[MEDIUM] Scroll compensation min-height is never released

keepScrollPosition increases list.style.minHeight when the page is near the bottom, but there is no path that clears that override on later switches. After one bottom-of-page switch from a taller variant to a shorter one, the union list can remain artificially tall and leave persistent blank space for the rest of the session.

A concrete way to reproduce is: scroll near the bottom, switch to a shorter variant (which sets minHeight), then continue switching variants or navigating within the same page — the stale minHeight remains applied.

Please clear stale compensation when it is not needed (for example, reset list.style.minHeight before recomputing and/or whenever shortfall <= 0).

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.

Fixed in 3d27df7. keepScrollPosition now clears min-height on every switch and sets it again only when that switch would make the browser clamp the scroll. In Chromium, near the page bottom, the switch to a shorter variant keeps 181px, and the next switch releases it. releases the height kept for the scroll position on the next switch in api-union-chips.test.ts covers it.

Chips showed each variant's schema name, so the 22 connector secrets
all ended in `_secrets` and the detection rules in `RuleUpdateProps`.
Chip labels now drop the words that every name in the list shares at
its start or end: `bedrock`, `crowdstrike`; `Eql`, `SavedQuery`.
Words split at `_`, `-`, `.` and capitals, and every label keeps a word
of its own. The full name stays as the chip's tooltip and in the "more"
menu, so the filter still matches it.

Co-Authored-By: Claude Opus 5.5 <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: there is still a correctness regression in union variant rendering where chip mode can produce a selectable but empty array-variant panel for X[]/X pairs in 3+ variant unions.


What is this? | From workflow: PR Review

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

// the label keeps only what the chip does not say (the discriminator value, or the link to the type's page).
var collapsible = variant.IsCollapsible && !Model.UseChips;
var folded = variant.UseHidden && !Model.UseChips;
var showName = !Model.UseChips || variant.PageUrl is { Length: > 0 };

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.

[MEDIUM] Chip mode can render array variants as an empty panel

In chip mode, showName is false unless PageUrl exists, so array variants in an X[]/X pair can lose their only visible content.

Concrete path:

  • ApiPropertyTreeBuilder.Variants.cs suppresses array-pair properties (showProperties false) and description (description = null) for the X[] side.
  • Here, chip mode also suppresses the label name when PageUrl is absent.
  • Array variants also have DiscriminatorLabel = null.

That leaves a selectable chip whose panel is blank for unions with 3+ variants that include an X[]/X pair (plus at least one other variant to trigger chips).

Please keep at least one visible payload for these variants in chip mode (for example, keep the variant name visible when there is no discriminator/page link, or avoid suppressing array-pair properties in chip mode).

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.

Fixed in 3d27df7. Behind chips each variant shows alone, so the X[] side of a pair no longer depends on the plain variant. When the list has chips, the builder keeps its fields and description, and the panel keeps its [] X heading so the fields read as each item's. A list of two variants still puts them on the plain variant only. BuildPropertyList_ArrayAndPlainPairBehindChips_BothShowTheFields covers both cases. No chip list on the current Elasticsearch and Kibana pages has such a pair, so no page changes from this today.

The height a chip list kept to hold the scroll position near the page
bottom was never released, so blank space could stay after later
switches. Every switch now starts from the list's own height.

An X[] / X pair listed one after the other puts the fields on the plain
variant only. Behind chips each variant shows alone, so the X[] chip
opened an empty panel. It now shows the fields and description too,
under its "[] X" heading.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +27 to +33
<button
type="button"
role="tab"
class="api-example-chip@(i == 0 ? " is-active" : "")"
data-chip="@variant.AnchorId"
aria-controls="@variant.AnchorId"
aria-selected="@(i == 0 ? "true" : "false")"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: P3 (Low)

Variant chips use tab semantics but lack the arrow-key and roving-focus behavior expected of tabs, making keyboard navigation inconsistent for keyboard and screen-reader users.

Generated by Libra. React with 👍 or 👎 to give feedback on this comment.

A row like `knn.filter` reads "One of: QueryContainer[] or
QueryContainer". The `QueryContainer` option was styled as a link but
was plain text, and `QueryContainer[]` was not even styled. Both now
link to the `QueryContainer` page in the same tab, as the type of a row
already does. The `[]` stays outside the link, so the link names the
type. The Markdown export links them too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
akira28 and others added 2 commits October 9, 2026 07:59
The ingest pipeline pages were almost all processor listings: 519 of
`ingest-put-pipeline`'s 519 rows, repeated on `ingest-simulate` and
`simulate-ingest`. `ProcessorContainer` now gets a page under Types,
Ingest, as QueryContainer and the aggregations do, and those rows link
to it. `ingest-put-pipeline` drops to 12 rows.

Which types get a page lived in three hardcoded places, keyed by display
name, so any schema whose short name was `Aggregate` linked to the
Elasticsearch page. `TypePages` now lists each page once with its schema
id, navigation category and map spelling, and rows link by `$ref` id.
The name-based link styling goes too: it styled `QueryContainer` as a
link on its own page and wherever it was not one. A type line built by
`Describe`, such as a response type, now carries a real link instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akira28 akira28 changed the title Pick union variants from chips and give ingest processors a page Pick union variants from chips when a list has three or more Oct 9, 2026
Base automatically changed from api-explorer/type-pages to main October 9, 2026 07:31
…chips

# Conflicts:
#	tests/Elastic.ApiExplorer.Tests/UnionVariantTests.cs
The chips read `bedrock`, `email`, but the "+N more" menu still listed
`bedrock_config`, `email_config`. Menu entries now use the same short
labels, with the full name as their tooltip, so the menu reads like the
row it extends. The filter still matches the short names.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment on lines +52 to +53
var leansOnPlain = variant.IsArray && hasBothVariants && variantsToRender.Count < ApiUnionVariants.ChipThreshold;
var showProperties = hasProperties && !leansOnPlain;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: P3 (Low)

In chip-mode X[]/X pairs, building the array variant first can register a shared nested shape and make the plain variant a “Same fields as” link, removing its descendant anchors and breaking existing field links.

Generated by Libra. React with 👍 or 👎 to give feedback on this comment.

Lists switched to chips at three variants, so two object variants still
listed both field sets one after the other while three showed one at a
time. A list now uses chips whenever it has two or more object variants;
a list with a single object, such as `TotalHits` or `number`, keeps
reading one after the other. An X[] / X pair is two object variants, so
the rule that left the array variant's fields to the plain one goes.

A primitive variant behind chips showed an empty panel, since its name
was on the chip and it has no fields. It now keeps its type name.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akira28 akira28 changed the title Pick union variants from chips when a list has three or more Pick union variants from chips when a list has two or more objects Oct 9, 2026
Comment on lines +31 to +35

[Test]
public async Task Render_PrimitiveVariantBehindChips_KeepsItsTypeName()
{
var html = await Render(3, objects: 2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Severity: P3 (Low)

The primitive-label regression test uses an array variant, so it would still pass if a non-array primitive lost its only visible type name.

Generated by Libra. React with 👍 or 👎 to give feedback on this comment.

Lists with a single object, such as `TotalHits` or `number`, still
listed their variants one after the other, so a union read one way or
the other depending on its members. Every list with two or more
variants now uses chips; a primitive variant shows its type name in its
panel. The "or" separator between listed variants goes with the last
lists that used it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@akira28 akira28 changed the title Pick union variants from chips when a list has two or more objects Pick union variants from chips when a list has two or more Oct 9, 2026

This branch was successfully deployed

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant