Skip to content

Commit

Permalink
Pass
Browse files Browse the repository at this point in the history
  • Loading branch information
cdedreuille committed Jul 17, 2024
1 parent 7b798dc commit 59a3e26
Show file tree
Hide file tree
Showing 12 changed files with 119 additions and 127 deletions.
2 changes: 1 addition & 1 deletion apps/addon-catalog/app/[...addonName]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default async function AddonDetails({ params }: AddonDetailsProps) {
<Highlight withHTMLChildren={false}>
<div
/**
* These are copied from @repo/ui/src/components/mdx/...
* These are copied from \@repo/ui/src/components/mdx/...
*
* TODO: Tweak these styles
*/
Expand Down
32 changes: 20 additions & 12 deletions apps/addon-catalog/components/addon/addon-hero.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
'use client';

import Image from 'next/image';
import {
CheckIcon,
CopyIcon,
GithubIcon,
VerifiedIcon,
} from '@storybook/icons';
import humanFormat from 'human-format';
import { humanFormat } from 'human-format';
import copy from 'copy-to-clipboard';
import { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { StorybookIcon } from '@repo/ui';
import { type Addon } from '../../types';

export function AddonHero({ addon }: { addon: Addon }) {
const [state, setState] = useState(false);

const onClick = () => {
copy(`npx install ${addon.name}`);
copy(`npx install ${addon.name ?? ''}`);
setState(true);
setTimeout(() => {
setState(false);
Expand All @@ -27,22 +27,27 @@ export function AddonHero({ addon }: { addon: Addon }) {
return (
<div className="mb-12 flex justify-between border-b border-zinc-300 pb-12 dark:border-b-slate-700">
<div className="flex flex-col gap-8 md:flex-row">
{addon.icon ? <div
{addon.icon ? (
<div
style={{ backgroundImage: `url('${addon.icon}')` }}
className="h-20 w-20 bg-contain bg-center bg-no-repeat"
/> : null}
/>
) : null}
<div className="flex flex-col items-start">
<div className="flex items-center gap-2">
<h1 className="text-2xl font-bold">{addon.displayName}</h1>
{addon.verified &&
['official', 'integrators'].includes(addon.verified) &&
addon.status !== 'deprecated' ? <VerifiedIcon className="text-blue-500" /> : null}
['official', 'integrators'].includes(addon.verified) &&
addon.status !== 'deprecated' ? (
<VerifiedIcon className="text-blue-500" />
) : null}
</div>
<p className="mb-4">{addon.description}</p>
<div className="flex flex-col gap-6 md:flex-row md:items-center">
<button
className="relative flex cursor-pointer items-center gap-4 rounded bg-zinc-100 px-4 py-2 dark:bg-slate-800 dark:text-slate-300"
onClick={onClick}
type="button"
>
npm install {addon.name} <CopyIcon />
<AnimatePresence>
Expand All @@ -60,9 +65,10 @@ export function AddonHero({ addon }: { addon: Addon }) {
</AnimatePresence>
</button>
<a
href={addon.repositoryUrl || ''}
href={addon.repositoryUrl ?? ''}
target="_blank"
className="flex items-center gap-2 text-sm text-black transition-colors hover:text-blue-500 dark:text-slate-400" rel="noopener"
className="flex items-center gap-2 text-sm text-black transition-colors hover:text-blue-500 dark:text-slate-400"
rel="noopener"
>
<GithubIcon />
View on Github
Expand All @@ -73,19 +79,21 @@ export function AddonHero({ addon }: { addon: Addon }) {
<div className="hidden flex-col pr-8 lg:flex">
<div className="mb-4 flex flex-col">
<div className="text-3xl text-blue-400">
{humanFormat(addon.weeklyDownloads || 0, {
{humanFormat(addon.weeklyDownloads ?? 0, {
decimals: 0,
separator: '',
})}
</div>
<div className="text-md">Downloads per week</div>
</div>
{addon.verified && addon.verified === 'official' ? <div className="flex items-center gap-2 rounded bg-blue-900 px-2 py-1.5">
{addon.verified && addon.verified === 'official' ? (
<div className="flex items-center gap-2 rounded bg-blue-900 px-2 py-1.5">
<StorybookIcon size={16} />
<span className="text-xs font-bold text-white">
Made by Storybook
</span>
</div> : null}
</div>
) : null}
</div>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion apps/addon-catalog/components/addon/addon-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export function AddonSidebar({ addon }: { addon: AddonWithTagLinks }) {
))}
</ul>
</> : null}
{tags && tags.length ? <>
{tags?.length ? <>
<div className="mb-2 mt-6 flex items-center py-2 text-sm font-bold">
Tags
</div>
Expand Down
62 changes: 30 additions & 32 deletions apps/addon-catalog/lib/fetch-tag-details-data.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import { addonFragment, recipeFragment, validateResponse } from '@repo/utils';
import { type Tag } from '../types';
import { fetchAddonsQuery, gql } from './fetch-addons-query';

type TagValue = Pick<
Tag,
| 'name'
| 'displayName'
| 'description'
| 'icon'
| 'relatedTags'
| 'topIntegrations'
>
Tag,
| 'name'
| 'displayName'
| 'description'
| 'icon'
| 'relatedTags'
| 'topIntegrations'
>;

interface TagsData {
tags: TagValue[];
Expand All @@ -20,14 +21,13 @@ async function fetchTagsData({
}: {
isCategory?: boolean;
} = {}) {
try {
let value: TagValue[] = [];
async function fetchPartialData(skip = 0) {
const data = await fetchAddonsQuery<
TagsData,
{ isCategory: boolean; skip: number }
>(
gql`
let value: TagValue[] = [];
async function fetchPartialData(skip = 0) {
const data = await fetchAddonsQuery<
TagsData,
{ isCategory: boolean; skip: number }
>(
gql`
query Tags($isCategory: Boolean!, $skip: Int!) {
tags(isCategory: $isCategory, limit: 30, skip: $skip) {
name
Expand All @@ -54,26 +54,26 @@ async function fetchTagsData({
}
}
`,
{
variables: { isCategory: Boolean(isCategory), skip },
},
);
{
variables: { isCategory: Boolean(isCategory), skip },
},
);

validateResponse(() => data.tags);
validateResponse(() => data.tags);

const { tags } = data;
const { tags } = data;

value = [...value, ...tags];
value = [...value, ...tags];

if (tags.length > 0) await fetchPartialData(skip + tags.length);
if (tags.length > 0) await fetchPartialData(skip + tags.length);

return value;
}
return value;
}

try {
return await fetchPartialData();
} catch (error) {
// @ts-expect-error - Seems safe
throw new Error(`Failed to fetch tags data: ${error.message}`);
throw new Error(`Failed to fetch tags data: ${(error as Error).message}`);
}
}

Expand All @@ -83,9 +83,7 @@ export async function fetchTagDetailsData(name: string) {
const categoriesData = await fetchTagsData({ isCategory: true });
const tagsData = await fetchTagsData();

const tag = [...categoriesData, ...tagsData].find(
(tag) => tag.name === name,
);
const tag = [...categoriesData, ...tagsData].find((t) => t.name === name);

// if (!tag) throw new Error(`Tag not found: ${name}`);

Expand All @@ -96,6 +94,6 @@ export async function fetchTagDetailsData(name: string) {
isCategory: categoriesData.find((category) => category.name === name),
};
} catch (error) {
throw new Error(`Failed to fetch tags data: ${error}`);
throw new Error(`Failed to fetch tags data: ${(error as Error).message}`);
}
}
56 changes: 29 additions & 27 deletions apps/addon-catalog/lib/fetch-tags-data.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { validateResponse } from '@repo/utils';
import { fetchAddonsQuery, gql } from "./fetch-addons-query";
import { type Tag } from '../types';
import { fetchAddonsQuery, gql } from './fetch-addons-query';

type TagValue = Tag['name'];

Expand All @@ -12,39 +13,40 @@ export async function fetchTagsData({
}: {
isCategory?: boolean;
} = {}) {
try {
let value: TagValue[] = [];
async function fetchPartialData(skip = 0) {
const data = await fetchAddonsQuery<
TagsData,
{ isCategory: boolean; skip: number }
>(
gql`
query TagNames($isCategory: Boolean!, $skip: Int!) {
tags(isCategory: $isCategory, limit: 30, skip: $skip) {
name
}
let value: TagValue[] = []; // Moved outside to be accessible by fetchPartialData

// Define fetchPartialData at the root of fetchTagsData function body
async function fetchPartialData(skip = 0): Promise<TagValue[]> {
const data = await fetchAddonsQuery<
TagsData,
{ isCategory: boolean; skip: number }
>(
gql`
query TagNames($isCategory: Boolean!, $skip: Int!) {
tags(isCategory: $isCategory, limit: 30, skip: $skip) {
name
}
`,
{
variables: { isCategory: Boolean(isCategory), skip },
},
);
}
`,
{
variables: { isCategory: Boolean(isCategory), skip },
},
);

validateResponse(() => data?.tags);
validateResponse(() => data?.tags);

const { tags } = data;
const { tags } = data;

value = [...value, ...tags.map(({ name }) => name)];
value = [...value, ...tags.map(({ name }) => name)];

if (tags.length > 0) await fetchPartialData(skip + tags.length);
if (tags.length > 0) await fetchPartialData(skip + tags.length);

return value;
}
return value;
}

return await fetchPartialData();
try {
return await fetchPartialData(); // Call fetchPartialData at the end of fetchTagsData
} catch (error) {
// @ts-expect-error - Seems safe
throw new Error(`Failed to fetch addons data: ${error.message}`);
throw new Error(`Failed to fetch addons data: ${(error as Error).message}`);
}
}
3 changes: 3 additions & 0 deletions apps/addon-catalog/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@
"@next/third-parties": "^14.2.4",
"@radix-ui/react-slot": "^1.1.0",
"@repo/ui": "*",
"@repo/utils": "*",
"@storybook/icons": "^1.2.9",
"@types/mdx": "^2.0.13",
"copy-to-clipboard": "^3.3.3",
"framer-motion": "^11.2.12",
"graphql-request": "^7.1.0",
"human-format": "^1.2.0",
"next": "^14.2.4",
"next-plausible": "^3.12.0",
"next-themes": "^0.3.0",
"prismjs": "^1.29.0",
"react": "^18",
"react-dom": "^18",
Expand Down
23 changes: 13 additions & 10 deletions apps/addon-catalog/types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @ts-expect-error - TypeScript is unable to resolve the module
declare module 'rehype-urls';

type Appearance = 'official' | 'integrator' | 'community';
export type Appearance = 'official' | 'integrator' | 'community';

interface Addon {
export interface Addon {
type?: 'Addon';
name?: string;
authors?: User[] | null;
Expand All @@ -26,11 +27,13 @@ interface Addon {
yearlyDownloads?: number | null;
}

interface AddonWithTagLinks extends Omit<Addon, 'tags'> { tags: TagLinkType[] }
export interface AddonWithTagLinks extends Omit<Addon, 'tags'> {
tags: TagLinkType[];
}

interface Framework extends Pick<Tag, 'displayName' | 'icon' | 'name'> {}
export type Framework = Pick<Tag, 'displayName' | 'icon' | 'name'>;

interface Recipe {
export interface Recipe {
type: 'Recipe';
name: string;
accentColor: string | null;
Expand All @@ -50,9 +53,9 @@ interface Recipe {
yearlyViews: number | null;
}

type Status = 'default' | 'essential' | 'deprecated';
export type Status = 'default' | 'essential' | 'deprecated';

interface Tag {
export interface Tag {
type?: 'Tag';
// displayName is nullable in the GraphQL schema, but buildTagLinks implies it's always available
displayName?: string;
Expand All @@ -68,16 +71,16 @@ interface Tag {
link?: string;
}

interface User {
export interface User {
type?: 'User';
username: string;
email?: string | null;
gravatarUrl: string | null;
}

type Verified = 'integrators' | 'official';
export type Verified = 'integrators' | 'official';

interface TagLinkType {
export interface TagLinkType {
link: string;
name: string;
}
Loading

0 comments on commit 59a3e26

Please sign in to comment.