Skip to content
38 changes: 20 additions & 18 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -440,24 +440,26 @@ The command palette is a first-class navigation surface. When you add a new user

#### Available route names

| Route name | URL pattern | Parameters |
| ------------------- | ------------------------------------------------- | -------------------------------- |
| `index` | `/` | — |
| `about` | `/about` | — |
| `compare` | `/compare` | — |
| `privacy` | `/privacy` | — |
| `search` | `/search` | — |
| `settings` | `/settings` | — |
| `package` | `/package/:org?/:name` | `org?`, `name` |
| `package-version` | `/package/:org?/:name/v/:version` | `org?`, `name`, `version` |
| `code` | `/package-code/:path+` | `path` (array) |
| `docs` | `/package-docs/:path+` | `path` (array) |
| `changelog` | `/package-changelog/:org?/:name` | `org?`, `name` |
| `changelog-version` | `/package-changelog/:org?/:name/v/:version` | `org?`, `name`, `version` |
| `timeline` | `/package-timeline/:org?/:packageName/v/:version` | `org?`, `packageName`, `version` |
| `org` | `/org/:org` | `org` |
| `~username` | `/~:username` | `username` |
| `~username-orgs` | `/~:username/orgs` | `username` |
| Route name | URL pattern | Parameters |
| ----------------------------- | ------------------------------------------------- | ------------------------------------ |
| `index` | `/` | — |
| `about` | `/about` | — |
| `compare` | `/compare` | — |
| `privacy` | `/privacy` | — |
| `search` | `/search` | — |
| `settings` | `/settings` | — |
| `package` | `/package/:org?/:name` | `org?`, `name` |
| `package-version` | `/package/:org?/:name/v/:version` | `org?`, `name`, `version` |
| `code` | `/package-code/:path+` | `path` (array) |
| `docs` | `/package-docs/:path+` | `path` (array) |
| `changelog` | `/package-changelog/:org?/:name` | `org?`, `name` |
| `changelog-version` | `/package-changelog/:org?/:name/v/:version` | `org?`, `name`, `version` |
| `timeline` | `/package-timeline/:org?/:packageName/v/:version` | `org?`, `packageName`, `version` |
| `org` | `/org/:org` | `org` |
| `~username` | `/~:username` | `username` |
| `~username-orgs` | `/~:username/orgs` | `username` |
| `collections-new` | `/collections/new` | — |
| `collections-identifier-rkey` | `/collections/:identifier/:rkey` | `identifier` (handle or DID), `rkey` |

### Cursor and navigation

Expand Down
19 changes: 19 additions & 0 deletions app/components/Collection/Card.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook-vue/nuxt'
import Card from './Card.vue'
import { mockCollectionSummary } from '../../storybook/mocks/handlers/collections'

const meta = {
component: Card,
tags: ['autodocs'],
args: { collection: mockCollectionSummary },
decorators: [() => ({ template: '<div class="max-w-sm"><story /></div>' })],
} satisfies Meta<typeof Card>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {}

export const WithoutDescription: Story = {
args: { collection: { ...mockCollectionSummary, description: undefined } },
}
40 changes: 40 additions & 0 deletions app/components/Collection/Card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<script setup lang="ts">
import type { CollectionSummary } from '#shared/types/social'

defineProps<{
collection: CollectionSummary
/** 3 by default, for lists under an h2 */
headingLevel?: 2 | 3
}>()
</script>

<template>
<NuxtLink
:to="{
name: 'collections-identifier-rkey',
params: { identifier: collection.handle, rkey: collection.rkey },
}"
class="block h-full"
>
<BaseCard class="flex h-full flex-col gap-2 min-w-0">
<component
:is="`h${headingLevel ?? 3}`"
class="font-mono text-base font-medium truncate min-w-0"
:title="collection.name"
>
{{ collection.name }}
</component>
<p class="font-mono text-sm text-fg-muted truncate">
{{ $t('collections.by', { handle: collection.handle }) }}
</p>
<p v-if="collection.description" class="text-sm text-fg-muted line-clamp-2">
{{ collection.description }}
</p>
<p class="mt-auto font-mono text-xs text-fg-subtle">
{{
$t('collections.item_count', { count: $n(collection.itemCount) }, collection.itemCount)
}}
</p>
</BaseCard>
</NuxtLink>
</template>
42 changes: 42 additions & 0 deletions app/components/Collection/Editor.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { Meta, StoryObj } from '@storybook-vue/nuxt'
import { expect, userEvent } from 'storybook/test'
import Editor from './Editor.vue'
import { mockCollection } from '../../storybook/mocks/handlers/collections'

const meta = {
component: Editor,
tags: ['autodocs'],
decorators: [() => ({ template: '<div class="max-w-2xl"><story /></div>' })],
} satisfies Meta<typeof Editor>

export default meta
type Story = StoryObj<typeof meta>

/** Creating a new collection. */
export const Empty: Story = {}

/** Editing an existing collection. */
export const Editing: Story = {
args: { initial: mockCollection },
}

/** The collection changed elsewhere while it was open. */
export const Conflict: Story = {
args: {
...Editing.args,
errorMessage:
'This collection was changed somewhere else since you opened it. Reload to get the latest version.',
},
}

/** Adding a package by name and reordering it to the top. */
export const AddAndReorder: Story = {
args: Editing.args,
play: async ({ canvas }) => {
await userEvent.type(canvas.getByRole('combobox', { name: /add a package/i }), 'zod{enter}')
// Only added items have this; text matching can hit a typeahead suggestion that then closes
const moveUp = await canvas.findByRole('button', { name: /move zod up/i })
await userEvent.click(moveUp)
await expect(canvas.getByRole('button', { name: /move zod down/i })).toBeVisible()
},
}
Loading
Loading