Skip to content

feat(autocomplete): add rich Autocomplete widget to Vue (phase 1)#7131

Draft
Haroenv wants to merge 4 commits into
feat/vue-chatfrom
feat/vue-autocomplete
Draft

feat(autocomplete): add rich Autocomplete widget to Vue (phase 1)#7131
Haroenv wants to merge 4 commits into
feat/vue-chatfrom
feat/vue-autocomplete

Conversation

@Haroenv

@Haroenv Haroenv commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Stacked on #7130 (Chat), and merges in #7126 (isolated index) which it hard-depends on. Merge order: #7127#7126#7130 → this (or retarget once the bases land).

What

Ports the graduated Autocomplete widget to vue-instantsearch, reusing the hook-based infra (createHooksStore + renderReactCompat) and the createAutocompletePropGetters runtime already validated in earlier PRs.

  • AisAutocomplete is a dispatcher that keeps the legacy headless API backward-compatible (per request): with a default slot it renders the renamed AutocompleteHeadless (connectAutocomplete slot component) unchanged; without a slot it renders the new rich widget.
  • AutocompleteWidget (rich): registers the searchbox companion, renders the required isolated <AisIndex> subtree (a <AisConfigure> per source), and renders the shared UI (search / panel / indices) via the prop getters. It bootstraps the isolated index's first search by setQuery-ing its helper — mirroring the JS widget (autocomplete.tsx L1434-1441); the isolated index doesn't search on init by itself. Supports indices + showQuerySuggestions in normal mode.

Vue-3 issues this surfaced & fixed (shared infra)

Getting this working on Vue 3 exposed real gaps (the widget stresses the adapter far more than the recommend/chat widgets):

  1. The react-compat adapter dropped class when a component rendered <el className={..} {...props}> with props.class = undefined — now keeps the className-derived value (Vue 2 already did).
  2. AisConfigure attrs must be stable — a fresh object each render re-registered the connector under Vue 3's stricter deep watcher (infinite loop → OOM).
  3. The dispatcher forwards $scopedSlots || $slots (Vue 2 vs Vue 3).
  4. The inner renders its shell unconditionally rather than gating on this.state.

Test plan

  • Component tests: dispatcher (rich default vs legacy slot), rich core (render, focus, type→results), legacy headless behavior.
  • Full vue-instantsearch suite green on Vue 2 (589) and Vue 3 (583); oxlint + prettier clean.

Scope / follow-ups (phase 2)

  • Detached mode, feeds mode, recent-search storage, prompt suggestions, AI mode (now that Vue has Chat), and lazy activation.
  • The shared common suite stays skipped until its vue widgetParams variant is extended to carry indices/showQuerySuggestions/templates.

Draft, stacked.

🤖 Generated with Claude Code

Haroenv and others added 3 commits July 22, 2026 11:54
Adds an `isolated` prop to `<ais-index>` (AisIndex), threaded through to the
index widget's `isolated` option. `indexName` is no longer `required` because
isolated indices don't need one — the underlying index widget validates that
either `indexName` or `isolated` is provided.

Vue never shipped the experimental `EXPERIMENTAL_isolated`, so only the stable
`isolated` prop is exposed; no deprecated alias is needed here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ports the graduated Autocomplete widget to vue-instantsearch, reusing the
hook-based UI infrastructure (createHooksStore + renderReactCompat) and the
already-validated `createAutocompletePropGetters` runtime.

- `AisAutocomplete` is a dispatcher preserving backward compatibility: with a
  default slot it renders the (renamed) legacy headless `connectAutocomplete`
  slot component unchanged; otherwise it renders the new rich widget.
- `AutocompleteWidget` (rich): registers the searchbox companion, renders the
  required isolated `<AisIndex>` subtree (`<AisConfigure>` per source) and the
  shared UI (search/panel/indices) via the prop getters. Bootstraps the
  isolated index's first search by `setQuery`-ing its helper (mirrors the JS
  widget) — the isolated index doesn't search on init by itself. Supports
  `indices` + `showQuerySuggestions` in normal mode.

Vue-3 fixes surfaced by this widget (all in shared vue-compat/infra):
- The Vue 3 react-compat adapter dropped `class` when a component rendered
  `<el className={..} {...props}>` with `props.class = undefined`; keep the
  className-derived value (matches Vue 2).
- Pass stable (cached) `AisConfigure` attrs — a fresh object each render
  re-registered the connector under Vue 3's deep watcher (infinite loop / OOM).
- The dispatcher forwards `$scopedSlots || $slots` (Vue 2 vs Vue 3).
- The inner renders its shell unconditionally (don't gate on `this.state`).

Phase 1 scope: indices + query suggestions, normal (non-detached) mode.
Detached mode, feeds, recent-search storage, prompt suggestions, AI mode and
lazy activation are follow-ups. Component tests cover the dispatcher (rich vs
legacy slot), the rich core (render, focus, type-to-results) and the headless
behavior. The shared common suite stays skipped pending an extension of its
`vue` widgetParams variant. Validated on Vue 2 and Vue 3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codacy-production

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 high

Alerts:

⚠ 1 issue (≤ 0 issues of at least high severity)

Results:
1 new issue

Category Results
Security 1 high

View in Codacy

🟢 Metrics 73 complexity

Metric Results
Complexity 73

View in Codacy

TIP This summary will be updated as you push new changes.

@pkg-pr-new

pkg-pr-new Bot commented Jul 22, 2026

Copy link
Copy Markdown
More templates

algoliasearch-helper

npm i https://pkg.pr.new/algolia/instantsearch/algoliasearch-helper@7131

instantsearch-ui-components

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch-ui-components@7131

instantsearch.css

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch.css@7131

instantsearch.js

npm i https://pkg.pr.new/algolia/instantsearch/instantsearch.js@7131

react-instantsearch

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch@7131

react-instantsearch-core

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-core@7131

react-instantsearch-nextjs

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-nextjs@7131

react-instantsearch-router-nextjs

npm i https://pkg.pr.new/algolia/instantsearch/react-instantsearch-router-nextjs@7131

vue-instantsearch

npm i https://pkg.pr.new/algolia/instantsearch/vue-instantsearch@7131

commit: 76901b5

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI 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.

Pull request overview

Adds the graduated Autocomplete widget to vue-instantsearch by introducing a dispatcher-based AisAutocomplete (legacy headless slot API vs new rich widget), plus supporting infrastructure changes needed for Vue 3 compatibility and isolated indices.

Changes:

  • Added a rich AutocompleteWidget implementation for Vue, backed by connectAutocomplete + shared instantsearch-ui-components Autocomplete UI factories.
  • Introduced an AisAutocomplete dispatcher component to preserve the legacy headless slot API while making the rich widget the default.
  • Updated Vue infra and Index API to support isolated indices and improve Vue 3 react-compat behavior; added/updated component tests and common-widgets wiring.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
packages/vue-instantsearch/src/widgets.js Re-points AisAutocomplete export to the new JS dispatcher entrypoint.
packages/vue-instantsearch/src/util/vue-compat/index-vue3.js Fixes Vue 3 react-compat prop mapping for className/class interactions.
packages/vue-instantsearch/src/util/tests/createServerRootMixin.test.js Updates SSR test to use the headless Autocomplete implementation directly.
packages/vue-instantsearch/src/components/Index.js Exposes isolated prop and makes indexName optional to support isolated indices.
packages/vue-instantsearch/src/components/AutocompleteWidget.js New rich Autocomplete widget (isolated <AisIndex> subtree + shared UI via prop getters).
packages/vue-instantsearch/src/components/AutocompleteHeadless.vue New legacy headless Autocomplete component (slot-driven connectAutocomplete).
packages/vue-instantsearch/src/components/Autocomplete.js New dispatcher component selecting headless vs rich implementation based on default slot presence.
packages/vue-instantsearch/src/components/tests/Index.js Adds coverage for the new isolated prop behavior.
packages/vue-instantsearch/src/components/tests/AutocompleteWidget.js Adds component tests for the rich Autocomplete widget.
packages/vue-instantsearch/src/components/tests/AutocompleteHeadless.js Adds tests + snapshot coverage for the legacy headless component.
packages/vue-instantsearch/src/components/tests/Autocomplete.js Adds dispatcher tests (rich default vs legacy slot behavior).
packages/vue-instantsearch/src/components/tests/snapshots/AutocompleteHeadless.js.snap Snapshot for the new headless component output.
packages/vue-instantsearch/src/tests/common-widgets.test.js Enables Autocomplete mount hook for common suite (suite itself remains skipped).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 90 to +94
if (name === 'className') {
data.class = value;
data.class = value || data.class;
} else if (name === 'class') {
// Shared components render `<el className={..} {...props}>` where props
// can carry a `class: undefined`; keep the className-derived value
Comment on lines +229 to +231
created() {
this.instanceKey = `ais-autocomplete-${(autocompleteInstanceId += 1)}`;
},
this.usePropGetters({
indices,
indicesConfig,
onRefine: (query) => refineAutocomplete(query),
Comment on lines +136 to +140
const inputProps = getInputProps();
const searchBoxContent = h(AutocompleteSearchUi, {
inputProps: Object.assign({}, inputProps, {
onChange: (event) => refineAutocomplete(event.currentTarget.value),
}),
Comment on lines +656 to +661
render: renderCompat((h) =>
h(AisInstantSearch, { props: instantSearchOptions }, [
h(AisAutocomplete, { props: widgetParams }),
h(GlobalErrorSwallower),
])
),
@Haroenv

Haroenv commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

CI notes

  • bundlesize: bumped the Vue thresholds for the added widget code (Autocomplete + isolated index are sizeable; markdown-to-jsx etc. already counted via the Chat base).
  • Codacy flags AutocompleteHeadless.js:30 as "jQuery .html() XSS". This is a false positive: it's @vue/test-utils' wrapper.html() in a test (not jQuery), and it's pre-existing code — this file is the renamed legacy Autocomplete.js test, unchanged.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants