Skip to content

Commit

Permalink
docs: Add sorting stand-alone guide
Browse files Browse the repository at this point in the history
  • Loading branch information
ntucker committed Jul 4, 2024
1 parent 9422581 commit 3959086
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 109 deletions.
111 changes: 2 additions & 109 deletions docs/rest/api/Query.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ sidebar_label: schema.Query

import { RestEndpoint } from '@data-client/rest';
import HooksPlayground from '@site/src/components/HooksPlayground';
import SortDemo from '../shared/\_SortDemo.mdx';

# schema.Query

Expand All @@ -34,115 +35,7 @@ response for use with [useQuery](/docs/api/useQuery)

### Maintaining sort after creates {#sorting}

import { postFixtures,getInitialInterceptorData } from '@site/src/fixtures/posts-collection';

Here we have an API that sorts based on the `orderBy` field. By wrapping our [Collection](./Collection.md)
in a `Query` that sorts, we can ensure we maintain the correct order after [pushing](./RestEndpoint.md#push)
new posts.

Our example code starts sorting by `title`. Try adding some posts and see them inserted in the correct sort
order.

<HooksPlayground fixtures={postFixtures} getInitialInterceptorData={getInitialInterceptorData} row>

```ts title="getPosts" {20-27}
import { Entity, RestEndpoint } from '@data-client/rest';

class Post extends Entity {
id = '';
title = '';
group = '';
author = '';

pk() {
return this.id;
}
}
export const getPosts = new RestEndpoint({
path: '/:group/posts',
searchParams: {} as { orderBy?: string; author?: string },
schema: new schema.Query(
new schema.Collection([Post], {
nonFilterArgumentKeys: /orderBy/,
}),
(posts, { orderBy } = {}) => {
if (orderBy) {
return [...posts].sort((a, b) =>
a[orderBy].localeCompare(b[orderBy]),
);
}
return posts;
},
),
});
```

```tsx title="NewPost" collapsed
import { useLoading } from '@data-client/react';
import { getPosts } from './getPosts';

export default function NewPost({ user }: { user: string }) {
const ctrl = useController();

const [handlePress, loading] = useLoading(async e => {
if (e.key === 'Enter') {
const title = e.currentTarget.value;
e.currentTarget.value = '';
await ctrl.fetch(getPosts.push, {group: 'react'}, {
title,
author: user,
});
}
});

return (
<div>
<input type="text" onKeyDown={handlePress} />{loading ? ' ...' : ''}
</div>
);
}
```

```tsx title="PostList" collapsed
import { useSuspense } from '@data-client/react';
import { getPosts } from './getPosts';
import NewPost from './NewPost';

export default function PostList({
user,
}) {
const posts = useSuspense(getPosts, { author: user, orderBy: 'title', group: 'react' });
return (
<div>
{posts.map(post => (
<div key={post.pk()}>{post.title}</div>
))}
<NewPost user={user} />
</div>
);
}
```

```tsx title="UserList" collapsed
import PostList from './PostList';

function UserList() {
const users = ['bob', 'clara']
return (
<div>
{users.map(user => (
<section key={user}>
<h3>{user}</h3>
<PostList user={user} />
</section>
))}
</div>
);
}
render(<UserList />);
```

</HooksPlayground>
<SortDemo />

### Aggregates

Expand Down
17 changes: 17 additions & 0 deletions docs/rest/guides/sorting-client-side.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
title: Efficient client-side sorting in React
sidebar_label: Sorting (client-side)
---

import SortDemo from '../shared/\_SortDemo.mdx';

# Client Side Sorting

Here we have an API that sorts based on the `orderBy` field. By wrapping our [Collection](../api/Collection.md)
in a [Query](../api/Query.md) that sorts, we can ensure we maintain the correct order after [pushing](../api/RestEndpoint.md#push)
new posts.

Our example code starts sorting by `title`. Try adding some posts and see them inserted in the correct sort
order.

<SortDemo defaultTab="PostList" />
119 changes: 119 additions & 0 deletions docs/rest/shared/_SortDemo.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import HooksPlayground from '@site/src/components/HooksPlayground';
import {
postFixtures,
getInitialInterceptorData,
} from '@site/src/fixtures/posts-collection';

<HooksPlayground fixtures={postFixtures} getInitialInterceptorData={getInitialInterceptorData} row defaultTab={props.defaultTab}>

```ts title="getPosts" {20-27}
import { Entity, RestEndpoint } from '@data-client/rest';

class Post extends Entity {
id = '';
title = '';
group = '';
author = '';

pk() {
return this.id;
}
}
export const getPosts = new RestEndpoint({
path: '/:group/posts',
searchParams: {} as { orderBy?: string; author?: string },
schema: new schema.Query(
new schema.Collection([Post], {
nonFilterArgumentKeys: /orderBy/,
}),
(posts, { orderBy } = {}) => {
if (orderBy) {
return [...posts].sort((a, b) =>
a[orderBy].localeCompare(b[orderBy]),
);
}
return posts;
},
),
});
```

```tsx title="NewPost" collapsed
import { useLoading } from '@data-client/react';
import { getPosts } from './getPosts';

export default function NewPost({ author }: Props) {
const ctrl = useController();

const [handlePress, loading] = useLoading(async e => {
if (e.key === 'Enter') {
const title = e.currentTarget.value;
e.currentTarget.value = '';
await ctrl.fetch(
getPosts.push,
{ group: 'react' },
{
title,
author,
},
);
}
});

return (
<div>
<input type="text" onKeyDown={handlePress} />
{loading ? ' ...' : ''}
</div>
);
}
interface Props {
author: string;
}
```

```tsx title="PostList" collapsed {8}
import { useSuspense } from '@data-client/react';
import { getPosts } from './getPosts';
import NewPost from './NewPost';

export default function PostList({ author }: Props) {
const posts = useSuspense(getPosts, {
author,
orderBy: 'title',
group: 'react',
});
return (
<div>
{posts.map(post => (
<div key={post.pk()}>{post.title}</div>
))}
<NewPost author={author} />
</div>
);
}
interface Props {
author: string;
}
```

```tsx title="UserList" collapsed
import PostList from './PostList';

function UserList() {
const users = ['bob', 'clara'];
return (
<div>
{users.map(user => (
<section key={user}>
<h3>{user}</h3>
<PostList author={user} />
</section>
))}
</div>
);
}
render(<UserList />);
```

</HooksPlayground>
4 changes: 4 additions & 0 deletions website/sidebars-rest.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ module.exports = {
collapsed: false,
label: 'Data Guides',
items: [
{
type: 'doc',
id: 'guides/sorting-client-side',
},
{
type: 'doc',
id: 'guides/relational-data',
Expand Down

0 comments on commit 3959086

Please sign in to comment.