Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions components/buttons/GithubStarButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ExternalLink } from 'lucide-react';
import Link from 'next/link';
import React, { useEffect, useState } from 'react';
import axios from 'axios';
Comment on lines +1 to +4
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Lint/Prettier cleanup + icon refactor + safer fetch.

Addresses: import sort, extra blank lines, quotes, string concatenation, long lines, JSX quotes, trailing spaces, console usage, and replaces huge inline SVGs with lucide-react icons. Also adds timeout, Accept header, and unmount guard.

- import { ExternalLink } from 'lucide-react';
- import Link from 'next/link';
- import React, { useEffect, useState } from 'react';
- import axios from 'axios';
+import React, { useEffect, useState } from 'react';
+import axios from 'axios';
+import clsx from 'clsx';
+import { Github, Star } from 'lucide-react';

-const GithubStarBtn = ({ text, username, repo }: {
-    text: string;
-    username: string;
-    repo: string;
-}) => {
-
-    const [starCount, setStarCount] = useState(0);
+type GithubStarBtnProps = { text: string; username: string; repo: string };
+const GithubStarBtn = ({ text, username, repo }: GithubStarBtnProps) => {
+  const [starCount, setStarCount] = useState<number | null>(null);

-    const formatStarCount = (count: number) => {
-        if (count >= 1000) {
-            return (count / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
-        }
-        return count.toString();
-    };
+  const formatStarCount = (count: number) => {
+    if (count >= 1000) {
+      return `${(count / 1000).toFixed(1).replace(/\.0$/, '')}k`;
+    }
+    return count.toString();
+  };

-    useEffect(() => {
-        // Fetchingg star count from Github API...
-        const fetchStarCount = async () => {
-            try {
-                const response = await axios.get(`https://api.github.com/repos/${username}/${repo}`);
-                setStarCount(response.data.stargazers_count ?? <ExternalLink size={18} />);
-            } catch (error) {
-                console.error("Error fetching star count:", error);
-            }
-        };
-        fetchStarCount();
-    }, [username, repo]);
+  useEffect(() => {
+    let cancelled = false;
+    const fetchStarCount = async () => {
+      try {
+        const { data } = await axios.get(
+          `https://api.github.com/repos/${username}/${repo}`,
+          { headers: { Accept: 'application/vnd.github+json' }, timeout: 4000 }
+        );
+        if (!cancelled) {
+          setStarCount(typeof data?.stargazers_count === 'number' ? data.stargazers_count : null);
+        }
+      } catch {
+        if (!cancelled) setStarCount(null);
+      }
+    };
+    fetchStarCount();
+    return () => {
+      cancelled = true;
+    };
+  }, [username, repo]);

-    return (
-        <Link href={`https://github.com/${username}/${repo}`} target='_blank' rel='noreferrer noopener'>
-            <button className="flex ml-2 cursor-pointer space-x-1 overflow-hidden items-center text-sm font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-black text-white shadow hover:bg-black/90 h-9 px-4 py-2 max-w-52 whitespace-pre md:flex group relative w-full justify-center gap-2 rounded-md transition-all duration-300 ease-out hover:ring-2 hover:ring-black hover:ring-offset-2">
-                <span className="absolute right-0 -mt-12 h-32 w-8 translate-x-12 rotate-12 bg-white opacity-10 transition-all duration-1000 ease-out group-hover:-translate-x-40" />
-                <div className="flex items-center">
-                    <svg className="w-5 h-5 fill-current" viewBox="0 0 438.549 438.549">
-                        <path d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z" />
-                    </svg>
-                    <span className="ml-1 text-white mt-1">{text}</span>
-                </div>
-                <div className="mt-1 flex items-center text-sm md:flex">
-                    <svg className="w-4 h-4 text-gray-500 transition-all duration-300 group-hover:text-yellow-300" data-slot="icon" aria-hidden="true" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-                        <path clipRule="evenodd" d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.006 5.404.434c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.434 2.082-5.005Z" fillRule="evenodd" />
-                    </svg>
-                    <span className="inline-block tabular-nums tracking-wider font-display font-medium text-white"> {formatStarCount(starCount)} </span>
-                </div>
-            </button>
-        </Link>
-    );
+  const formatted = starCount == null ? '—' : formatStarCount(starCount);
+  return (
+    <a
+      href={`https://github.com/${username}/${repo}`}
+      target='_blank'
+      rel='noreferrer noopener'
+      aria-label={`${text} (${formatted} stars)`}
+      className={clsx(
+        'group relative ml-2 flex w-full max-w-52 items-center justify-center gap-2',
+        'h-9 cursor-pointer overflow-hidden rounded-md bg-black px-4 py-2 text-sm font-medium text-white shadow',
+        'hover:bg-black/90 transition-all duration-300 ease-out hover:ring-2 hover:ring-black hover:ring-offset-2',
+        'focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
+        'disabled:pointer-events-none disabled:opacity-50'
+      )}
+    >
+      <span
+        className={clsx(
+          'absolute right-0 -mt-12 h-32 w-8 translate-x-12 rotate-12 bg-white opacity-10',
+          'transition-all duration-1000 ease-out group-hover:-translate-x-40'
+        )}
+      />
+      <div className='flex items-center'>
+        <Github className='h-5 w-5' aria-hidden='true' />
+        <span className='ml-1 mt-1 text-white'>{text}</span>
+      </div>
+      <div className='mt-1 flex items-center text-sm'>
+        <Star className='h-4 w-4 text-gray-500 transition-all duration-300 group-hover:text-yellow-300' aria-hidden='true' />
+        <span className='inline-block font-display font-medium tabular-nums tracking-wider text-white' aria-live='polite' aria-atomic='true'>
+          {formatted}
+        </span>
+      </div>
+    </a>
+  );
 }
 
 export default GithubStarBtn;

Also applies to: 6-10, 14-19, 21-33, 35-51

🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 1-1: ESLint: Run autofix to sort imports (simple-import-sort/imports).

🤖 Prompt for AI Agents
In components/buttons/GithubStarButton.tsx around lines 1-4, 6-10, 14-19, 21-33
and 35-51, clean up formatting and refactor the fetch/icon usage: sort and
consolidate imports (use lucide-react ExternalLink and remove large inline
SVGs), remove extra blank lines and trailing spaces, normalize quotes (single
quotes in JS/TS, double quotes in JSX) and fix any string
concatenation/long-line issues (use template literals or wrap lines), eliminate
console.* calls, and make the GitHub request safer by adding a timeout and
Accept header to the axios call and guarding state updates with an unmount/abort
mechanism (AbortController or isMounted flag) so setState is not called after
unmount.


const GithubStarBtn = ({ text, username, repo }: {
text: string;
username: string;
repo: string;
}) => {

const [starCount, setStarCount] = useState(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

State type bug: mixing number with React element.

setStarCount(response.data.stargazers_count ?? <ExternalLink .../>) assigns a React node into a number state. This breaks types and later formatting. Use number | null and render a fallback in JSX instead.

Also applies to: 26-26

🤖 Prompt for AI Agents
In components/buttons/GithubStarButton.tsx around lines 12 and 26, the component
currently assigns a React node into a number state
(setStarCount(response.data.stargazers_count ?? <ExternalLink .../>)), causing a
type violation and rendering/formatting bugs; change the state type to number |
null (initialize to null or 0 as appropriate), ensure responses assign only
numbers (e.g., response.data.stargazers_count ?? null), and move the
ExternalLink fallback into the JSX render path so the UI displays the link when
starCount is null instead of storing a React element in state.


const formatStarCount = (count: number) => {
if (count >= 1000) {
return (count / 1000).toFixed(1).replace(/\.0$/, '') + 'k';
}
return count.toString();
};

useEffect(() => {
// Fetchingg star count from Github API...
const fetchStarCount = async () => {
try {
const response = await axios.get(`https://api.github.com/repos/${username}/${repo}`);
setStarCount(response.data.stargazers_count ?? <ExternalLink size={18} />);
} catch (error) {
console.error("Error fetching star count:", error);
}
};
fetchStarCount();
}, [username, repo]);

return (
<Link href={`https://github.com/${username}/${repo}`} target='_blank' rel='noreferrer noopener'>
<button className="flex ml-2 cursor-pointer space-x-1 overflow-hidden items-center text-sm font-medium focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 bg-black text-white shadow hover:bg-black/90 h-9 px-4 py-2 max-w-52 whitespace-pre md:flex group relative w-full justify-center gap-2 rounded-md transition-all duration-300 ease-out hover:ring-2 hover:ring-black hover:ring-offset-2">
<span className="absolute right-0 -mt-12 h-32 w-8 translate-x-12 rotate-12 bg-white opacity-10 transition-all duration-1000 ease-out group-hover:-translate-x-40" />
<div className="flex items-center">
<svg className="w-5 h-5 fill-current" viewBox="0 0 438.549 438.549">
<path d="M409.132 114.573c-19.608-33.596-46.205-60.194-79.798-79.8-33.598-19.607-70.277-29.408-110.063-29.408-39.781 0-76.472 9.804-110.063 29.408-33.596 19.605-60.192 46.204-79.8 79.8C9.803 148.168 0 184.854 0 224.63c0 47.78 13.94 90.745 41.827 128.906 27.884 38.164 63.906 64.572 108.063 79.227 5.14.954 8.945.283 11.419-1.996 2.475-2.282 3.711-5.14 3.711-8.562 0-.571-.049-5.708-.144-15.417a2549.81 2549.81 0 01-.144-25.406l-6.567 1.136c-4.187.767-9.469 1.092-15.846 1-6.374-.089-12.991-.757-19.842-1.999-6.854-1.231-13.229-4.086-19.13-8.559-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-.951-2.568-2.098-3.711-3.429-1.142-1.331-1.997-2.663-2.568-3.997-.572-1.335-.098-2.43 1.427-3.289 1.525-.859 4.281-1.276 8.28-1.276l5.708.853c3.807.763 8.516 3.042 14.133 6.851 5.614 3.806 10.229 8.754 13.846 14.842 4.38 7.806 9.657 13.754 15.846 17.847 6.184 4.093 12.419 6.136 18.699 6.136 6.28 0 11.704-.476 16.274-1.423 4.565-.952 8.848-2.383 12.847-4.285 1.713-12.758 6.377-22.559 13.988-29.41-10.848-1.14-20.601-2.857-29.264-5.14-8.658-2.286-17.605-5.996-26.835-11.14-9.235-5.137-16.896-11.516-22.985-19.126-6.09-7.614-11.088-17.61-14.987-29.979-3.901-12.374-5.852-26.648-5.852-42.826 0-23.035 7.52-42.637 22.557-58.817-7.044-17.318-6.379-36.732 1.997-58.24 5.52-1.715 13.706-.428 24.554 3.853 10.85 4.283 18.794 7.952 23.84 10.994 5.046 3.041 9.089 5.618 12.135 7.708 17.705-4.947 35.976-7.421 54.818-7.421s37.117 2.474 54.823 7.421l10.849-6.849c7.419-4.57 16.18-8.758 26.262-12.565 10.088-3.805 17.802-4.853 23.134-3.138 8.562 21.509 9.325 40.922 2.279 58.24 15.036 16.18 22.559 35.787 22.559 58.817 0 16.178-1.958 30.497-5.853 42.966-3.9 12.471-8.941 22.457-15.125 29.979-6.191 7.521-13.901 13.85-23.131 18.986-9.232 5.14-18.182 8.85-26.84 11.136-8.662 2.286-18.415 4.004-29.263 5.146 9.894 8.562 14.842 22.077 14.842 40.539v60.237c0 3.422 1.19 6.279 3.572 8.562 2.379 2.279 6.136 2.95 11.276 1.995 44.163-14.653 80.185-41.062 108.068-79.226 27.88-38.161 41.825-81.126 41.825-128.906-.01-39.771-9.818-76.454-29.414-110.049z" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shift this sag in public folder and import it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

</svg>
<span className="ml-1 text-white mt-1">{text}</span>
</div>
<div className="mt-1 flex items-center text-sm md:flex">
<svg className="w-4 h-4 text-gray-500 transition-all duration-300 group-hover:text-yellow-300" data-slot="icon" aria-hidden="true" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path clipRule="evenodd" d="M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.006 5.404.434c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.434 2.082-5.005Z" fillRule="evenodd" />
</svg>
<span className="inline-block tabular-nums tracking-wider font-display font-medium text-white"> {formatStarCount(starCount)} </span>
</div>
</button>
</Link>
Comment on lines +35 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix nested interactive elements: Link wrapping a button violates a11y and causes inconsistent behavior.

Render a styled anchor (or Link with className) instead of nesting a button inside Link.

🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 36-36: Max line length exceeded (max-len).


[error] 37-37: JSX quotes should use single quotes (jsx-quotes).


[error] 40-40: Max line length exceeded (max-len).


[error] 46-46: Max line length exceeded (max-len).


[error] 46-46: Trailing spaces not allowed (no-trailing-spaces).


[error] 47-47: Insert '/>' on its own line (prettier/prettier).


[error] 48-48: Long string literals detected; consider splitting or using template literals (prettier/prettier).

🤖 Prompt for AI Agents
components/buttons/GithubStarButton.tsx lines 35-51: the code nests a <button>
inside a <Link>, which creates nested interactive elements and breaks
accessibility; replace the inner <button> with a styled anchor (or apply
className directly to the Link's child anchor) that preserves all existing
classes, attributes (target, rel), and inner structure (SVGs, text, star count),
ensure the anchor has an appropriate aria-label and keyboard-focus styles, and
remove any button-specific props (like type) or event handlers that only apply
to button elements.

);
}

export default GithubStarBtn;
12 changes: 6 additions & 6 deletions components/navigation/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import MobileNavMenu from './MobileNavMenu';
import NavItem from './NavItem';
import otherItems from './otherItems';
import ToolsPanel from './ToolsPanel';
import GithubStarBtn from '../buttons/GithubStarButton';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import GithubStarBtn from '../buttons/GithubStarButton';
import GithubStarButton from '../buttons/GithubStarButton';


interface NavBarProps {
className?: string;
Expand Down Expand Up @@ -147,7 +148,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
<div className={`bg-white ${className} z-50`}>
<div className='flex w-full items-center justify-between py-6 lg:justify-start lg:space-x-2'>
{!hideLogo && (
<div className='lg:w-auto lg:flex-1'>
<div className='lg:w-auto lg:flex-1 lg:mr-8'>
<div className='flex'>
<Link href='/' className='cursor-pointer' aria-label='AsyncAPI' data-testid='Navbar-logo'>
<AsyncAPILogo className='w-auto' />
Expand Down Expand Up @@ -218,7 +219,7 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps

<div className='justify-content flex flex-row items-center'>
<SearchButton
className='mr-2 flex items-center space-x-2 rounded-md p-2 text-left text-gray-400 transition duration-150 ease-in-out hover:bg-gray-100 hover:text-gray-500 focus:bg-gray-100 focus:text-gray-500 focus:outline-none'
className='flex items-center space-x-2 rounded-md p-2 text-left text-gray-400 transition duration-150 ease-in-out hover:bg-gray-100 hover:text-gray-500 focus:bg-gray-100 focus:text-gray-500 focus:outline-none'
aria-label='Open Search'
>
<IconLoupe />
Expand All @@ -234,11 +235,10 @@ export default function NavBar({ className = '', hideLogo = false }: NavBarProps
selected={i18n.language ? i18n.language : 'en'}
/>

<GithubButton
<GithubStarBtn
text='Star on GitHub'
href='https://github.com/asyncapi/spec'
className='ml-2 py-2'
inNav={true}
username='asyncapi'
repo='spec'
/>
</div>
</nav>
Expand Down
2 changes: 1 addition & 1 deletion components/navigation/otherItems.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ interface OtherItem {
}

const otherItems: OtherItem[] = [
{ text: 'Case Studies', href: '/casestudies' },
{ text: 'Case Studies', href: '/casestudies', className: "whitespace-nowrap" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have an alternative for this

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright I will definitely let you know 👍🏻

{ text: 'Blog', href: '/blog' },
// If you want to add target for a specific item, you can do it here
{ text: 'Roadmap', href: '/roadmap' }
Expand Down
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"js-cookie": "^3.0.5",
"json-schema": "^0.4.0",
"lodash": "^4.17.21",
"lucide-react": "^0.542.0",
"markdown-to-txt": "^2.0.1",
"markdown-toc": "^1.2.0",
"md5": "^2.3.0",
Expand Down
Loading