diff --git a/specs/astro.config.mjs b/specs/astro.config.mjs index 233de8addd..4e950a8cb5 100644 --- a/specs/astro.config.mjs +++ b/specs/astro.config.mjs @@ -1,7 +1,18 @@ +import react from '@astrojs/react'; import { defineConfig } from 'astro/config'; export default defineConfig({ site: 'https://instantsearchjs.netlify.app/', base: '/specs', outDir: '../website/specs', + integrations: [react()], + vite: { + ssr: { + noExternal: [ + '@codesandbox/sandpack-react', + '@codesandbox/sandpack-themes', + '@codesandbox/sandpack-client', + ], + }, + }, }); diff --git a/specs/package.json b/specs/package.json index a8eaa6c36d..9615e6b5cf 100644 --- a/specs/package.json +++ b/specs/package.json @@ -18,9 +18,16 @@ "prepare": "rm -rf public && mkdir public && cp -r ../node_modules/instantsearch.css/themes public/themes" }, "devDependencies": { - "@types/node": "18.11.13", + "@types/node": "20.10.0", + "@codesandbox/sandpack-react": "2.19.1", + "@codesandbox/sandpack-themes": "2.0.21", + "dedent": "1.5.1", "astro": "1.6.14", + "@astrojs/react": "1.2.2", "instantsearch.css": "8.5.0", + "instantsearch.js": "4.74.0", + "react-instantsearch": "7.13.0", + "vue-instantsearch": "4.19.3", "sass": "1.56.2" } } diff --git a/specs/src/components/Sandbox.tsx b/specs/src/components/Sandbox.tsx new file mode 100644 index 0000000000..02c6c3ffbf --- /dev/null +++ b/specs/src/components/Sandbox.tsx @@ -0,0 +1,207 @@ +/* eslint-disable react/react-in-jsx-scope */ +import { Sandpack } from '@codesandbox/sandpack-react'; +import { githubLight } from '@codesandbox/sandpack-themes'; +import dedent from 'dedent'; + +const settings = { + js: { + html: /* HTML */ ` + +
+
+ +
+ `, + preamble: /* JS */ ` + import 'instantsearch.css/themes/satellite-min.css'; + import { liteClient as algoliasearch } from 'algoliasearch/lite'; + import instantsearch from 'instantsearch.js'; + import { history } from 'instantsearch.js/es/lib/routers'; + import { searchBox, hits } from 'instantsearch.js/es/widgets'; + import { createWidgets } from './widget.ts'; + + const search = instantsearch({ + indexName: 'instant_search', + searchClient: algoliasearch('latency', '6be0576ff61c053d5f9a3225e2a90f76'), + future: { + preserveSharedStateOnUnmount: true, + }, + routing: { + router: history({ + cleanUrlOnDispose: false, + }), + }, + }); + + search.addWidgets([ + ...createWidgets(() => + document.querySelector('#custom').appendChild(document.createElement('div')) + ), + searchBox({ + container: '#searchbox', + }), + hits({ + container: '#hits', + templates: { + item: (hit, { components }) => + components.Highlight({ attribute: 'name', hit }), + }, + }), + ]); + + search.start(); + `, + dependencies: { + algoliasearch: '5.1.1', + }, + filename: '/widget.ts', + }, + react: { + html: /* HTML */ ` + +
+ `, + preamble: /* TSX */ ` + import 'instantsearch.css/themes/satellite-min.css'; + import React from "react"; + import { createRoot } from "react-dom/client"; + import { liteClient as algoliasearch } from "algoliasearch/lite"; + import { history } from "instantsearch.js/es/lib/routers"; + import { InstantSearch, SearchBox, Hits, Highlight } from "react-instantsearch"; + import { widgets } from "./widget.tsx"; + + createRoot(document.getElementById('root')).render( + + {widgets} +
+ + +
+ ); + + function Hit({ hit }) { + return ; + } + `, + dependencies: { + react: '18.2.0', + 'react-dom': '18.2.0', + algoliasearch: '5.1.1', + }, + filename: '/widget.tsx', + }, + vue: { + html: /* HTML */ ` + +
+ `, + preamble: ` + import "instantsearch.css/themes/satellite-min.css"; + import Vue from "vue"; + import { liteClient as algoliasearch } from "algoliasearch/lite"; + import { history } from "instantsearch.js/es/lib/routers"; + import { AisInstantSearch, AisHits, AisSearchBox } from "vue-instantsearch/vue2/es"; + import Widget from "./Widget.vue"; + + Vue.config.productionTip = false; + + new Vue({ + render: (h) => + h( + AisInstantSearch, + { + props: { + searchClient: algoliasearch( + "latency", + "6be0576ff61c053d5f9a3225e2a90f76" + ), + indexName: "instant_search", + future: { + preserveSharedStateOnUnmount: true, + }, + routing: { + router: history({ + cleanUrlOnDispose: false, + }) + } + }, + }, + [h(Widget), h('hr'), h(AisSearchBox), h(AisHits)] + ), + }).$mount("#app"); + `, + dependencies: { + vue: '2.7.14', + algoliasearch: '5.1.1', + }, + filename: '/Widget.vue', + }, +}; + +export default function Sandbox({ + code, + flavor, + modules, +}: { + code: string; + flavor: 'react' | 'js' | 'vue'; + modules: { + files: Record; + dependencies: Record; + }; +}) { + const { preamble, html, filename, dependencies } = settings[flavor]; + return ( + + ); +} diff --git a/specs/src/components/WidgetContent.astro b/specs/src/components/WidgetContent.astro index 7aa7283e1e..d3cd2c61a7 100644 --- a/specs/src/components/WidgetContent.astro +++ b/specs/src/components/WidgetContent.astro @@ -2,6 +2,11 @@ import { Code } from 'astro/components'; import type { WidgetFrontmatter } from '../types'; import ThemeSelector from './ThemeSelector.astro'; +import Sandbox from './Sandbox.jsx'; +import { getSandpackCssText } from '@codesandbox/sandpack-react'; +import getFiles from '../getDependencyContent'; + +const modules = await getFiles(); type Props = { frontmatter: WidgetFrontmatter; @@ -23,7 +28,8 @@ const title = frontmatter.title; { frontmatter.info && (
-

{frontmatter.info}

+ {' '} +

{frontmatter.info}

{' '}
) } @@ -110,6 +116,30 @@ const title = frontmatter.title; ) } + { + frontmatter.examples ? ( + <> +

Usage

+