diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json
index c874df8427..e55f97ca72 100644
--- a/.codesandbox/ci.json
+++ b/.codesandbox/ci.json
@@ -1,6 +1,6 @@
{
"installCommand": "install:csb",
- "sandboxes": ["/examples/react/basic-typescript", "/examples/solid/basic-typescript", "/examples/vue/basic"],
+ "sandboxes": ["/examples/react/basic-typescript", "/examples/solid/basic-typescript", "/examples/svelte/basic", "/examples/vue/basic"],
"packages": ["packages/**"],
"node": "16"
}
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000000..6da4f0816a
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1 @@
+/packages/svelte-query/.svelte-kit
diff --git a/babel.config.js b/babel.config.js
index dc23100fd4..93f1b3251b 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -33,7 +33,7 @@ module.exports = {
].filter(Boolean),
overrides: [
{
- exclude: ['./packages/solid-query/**', './packages/vue-query/**'],
+ exclude: ['./packages/solid-query/**', './packages/svelte-query/**', './packages/vue-query/**'],
presets: ['@babel/react'],
},
{
diff --git a/docs/config.json b/docs/config.json
index 48282e3527..86118baba5 100644
--- a/docs/config.json
+++ b/docs/config.json
@@ -688,7 +688,7 @@
"label": "Getting Started",
"children": [
{
- "label": "Coming Soon",
+ "label": "Overview",
"to": "svelte/overview"
}
]
diff --git a/docs/svelte/overview.md b/docs/svelte/overview.md
index a7581f1c92..aeb846850d 100644
--- a/docs/svelte/overview.md
+++ b/docs/svelte/overview.md
@@ -1,8 +1,69 @@
---
id: overview
-title: Svelte Query (Coming Soon)
+title: Svelte Query
---
-> ⚠️ This module has not yet been developed. It requires an adapter similar to `react-query` to work. We estimate the amount of code to do this is low-to-moderate, but does require familiarity with the Svelte framework. If you would like to contribute this adapter, please open a PR!
+The `@tanstack/svelte-query` package offers a 1st-class API for using TanStack Query via Svelte.
-The `@tanstack/svelte-query` package offers a 1st-class API for using TanStack Query via Svelte. However, all of the primitives you receive from this API are core APIs that are shared across all of the TanStack Adapters including the Query Client, query results, query subscriptions, etc.
+## Example
+
+Include the QueryClientProvider near the root of your project:
+
+```svelte
+
+
+
Loading...
+ {:else if $query.isError} +Error: {$query.error.message}
+ {:else if $query.isSuccess} + {#each $query.data as todo} +{todo.title}
+ {/each} + {/if} ++ This example is best experienced on your own machine, where you can open + multiple tabs to the same localhost server and see your changes propagate + between the two. +
+ + +{$post.data.body}
+{$post.data.body}
+Population: {planet.population}
++ In this example, new items can be created using a mutation. The new item will + be optimistically added to the list in hopes that the server accepts the item. + If it does, the list is refetched with the true items from the list. Every now + and then, the mutation may fail though. When that happens, the previous list + of items is restored and the list is again refetched from the server. +
+ + + +{#if $todos.isLoading} + Loading... +{/if} +{#if $todos.error} + An error has occurred: + {$todos.error.message} +{/if} +{#if $todos.isSuccess} ++ The "staleTime" and "cacheTime" durations have been altered in this example to + show how query stale-ness and query caching work on a granular level +
+{$query.data.description}
+ 👀 {$query.data.subscribers_count}{' '} + ✨ {$query.data.stargazers_count}{' '} + 🍴 {$query.data.forks_count} +Using the Star Wars API
++ (Built by @Brent_m_Clark + ) +
++ In this demo you will be able to see how React Query is a significant + improvement over redux, mobx, and any + other general-purpose state container. +
++ No reducers, thunks, or sagas. No ES6 models to maintain in order to tag + them as observable. +
++ Simply associate a key with your fetch call and let{' '} + React Query handle the rest. +
++ Check out the{' '} + Films + and + Characters + ! +
+Loading...
+{/if} + +{#if $query.status === 'error'} +Error :(
+{/if} + +{#if $query.status === 'success'} +Loading...
+{/if} + +{#if $query.status === 'error'} +Error :(
+{/if} + +{#if $query.status === 'success'} + {@const homeworldUrlParts = $query.data.homeworld.split('/').filter(Boolean)} + {@const homeworldId = homeworldUrlParts[homeworldUrlParts.length - 1]} +Feature | +Value | +
---|---|
Born | +{$query.data.birth_year} | +
Eyes | +{$query.data.eye_color} | +
Hair | +{$query.data.hair_color} | +
Height | +{$query.data.height} | +
Mass | +{$query.data.mass} | +
Homeworld | +
Loading...
+{/if} + +{#if $query.status === 'error'} +Error :(
+{/if} + +{#if $query.status === 'success'} +Loading...
+{/if} + +{#if $query.status === 'error'} +Error :(
+{/if} + +{#if $query.status === 'success'} +{$query.data.opening_crawl}
+Loading
+{:else if $query.isError} +Error
+{:else if $query.isSuccess} +Success
+{/if} diff --git a/packages/svelte-query/src/__tests__/createMutation.test.ts b/packages/svelte-query/src/__tests__/createMutation.test.ts new file mode 100644 index 0000000000..a129e5784e --- /dev/null +++ b/packages/svelte-query/src/__tests__/createMutation.test.ts @@ -0,0 +1,22 @@ +import { describe, it, expect, vi } from 'vitest' +import { fireEvent, render, screen } from '@testing-library/svelte' +import CreateMutation from './CreateMutation.svelte' +import { sleep } from './utils' + +describe('createMutation', () => { + it('Call mutate and check function runs', async () => { + const queryFn = vi.fn() + + render(CreateMutation, { + props: { + queryFn, + }, + }) + + fireEvent.click(screen.getByRole('button')) + + await sleep(200) + + expect(queryFn).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/svelte-query/src/__tests__/createQuery.test.ts b/packages/svelte-query/src/__tests__/createQuery.test.ts new file mode 100644 index 0000000000..191fea1b23 --- /dev/null +++ b/packages/svelte-query/src/__tests__/createQuery.test.ts @@ -0,0 +1,28 @@ +import { describe, it, expect } from 'vitest' +import { render, screen } from '@testing-library/svelte' +import CreateQuery from './CreateQuery.svelte' +import { sleep } from './utils' + +describe('createQuery', () => { + it('Render and wait for success', async () => { + render(CreateQuery, { + props: { + queryKey: ['test'], + queryFn: async () => { + await sleep(100) + return 'Success' + }, + }, + }) + + expect(screen.queryByText('Loading')).toBeInTheDocument() + expect(screen.queryByText('Error')).not.toBeInTheDocument() + expect(screen.queryByText('Success')).not.toBeInTheDocument() + + await sleep(200) + + expect(screen.queryByText('Success')).toBeInTheDocument() + expect(screen.queryByText('Loading')).not.toBeInTheDocument() + expect(screen.queryByText('Error')).not.toBeInTheDocument() + }) +}) diff --git a/packages/svelte-query/src/__tests__/utils.ts b/packages/svelte-query/src/__tests__/utils.ts new file mode 100644 index 0000000000..9b26cd3ab1 --- /dev/null +++ b/packages/svelte-query/src/__tests__/utils.ts @@ -0,0 +1,72 @@ +import { vi } from 'vitest' +import { act } from '@testing-library/svelte' +import { + QueryClient, + type QueryClientConfig, + type MutationOptions, +} from '../lib' + +export function createQueryClient(config?: QueryClientConfig): QueryClient { + vi.spyOn(console, 'error').mockImplementation(() => undefined) + return new QueryClient({ logger: mockLogger, ...config }) +} + +export function mockVisibilityState(value: DocumentVisibilityState) { + return vi.spyOn(document, 'visibilityState', 'get').mockReturnValue(value) +} + +export function mockNavigatorOnLine(value: boolean) { + return vi.spyOn(navigator, 'onLine', 'get').mockReturnValue(value) +} + +export const mockLogger = { + log: vi.fn(), + warn: vi.fn(), + error: vi.fn(), +} + +let queryKeyCount = 0 +export function queryKey(): Array