Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ const props: supportUsButtonProps = {
name: "", // ← your organization name
description: "", // ← short org description
logo: { src: "", alt: "" }, // ← Add file path to src and alt text for accessibility. Note: Accepts a string path/url.
url:"",// ← If a valid organization URL is provided, the organization logo becomes clickable
projectInformation: {
name: "", // ← your project name
description: "",// ← short project description
Expand Down Expand Up @@ -334,6 +335,7 @@ Information about the organization and project.
| `name` | string | Yes | Organization name |
| `description` | string | Yes | Organization description |
| `logo` | `Image` / string | No | Organization logo |
| `url` | string | No | Organization link |
| `projectInformation` | `projectInformation` | No | Project details |

</details>
Expand Down
83 changes: 59 additions & 24 deletions src/components/SupportUsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ function getButtonClasses(buttonVariant: ButtonVariant): string {
return `${base} bg-primary hover:bg-primary/90 text-black font-black py-4 transition-all active:scale-[0.98] shadow-lg shadow-primary/20`;
}

// Helper function to validate URLs and prevent XSS through 'javascript:' protocol
function validateUrl(url?: string): string | undefined {
if (!url) return undefined;
const lowerUrl = url.toLowerCase();
if (lowerUrl.startsWith("http://") || lowerUrl.startsWith("https://")) {
return url;
}
return undefined;
}
Comment thread
swathi2006 marked this conversation as resolved.

// Main component function that renders the support us button, taking in various props for customization and rendering different sections such as hero, organization information, sponsors, and call-to-action based on the provided data and selected theme and button variant
function SupportUsButton({
Theme = "AOSSIE",
Expand All @@ -70,6 +80,7 @@ function SupportUsButton({
},
buttonVariant = "AOSSIE",
}: supportUsButtonProps): React.JSX.Element {
const validatedUrl = validateUrl(organizationInformation?.url);
return (
// Container for the support us button, with dynamic classes based on the selected theme and custom class names
<div
Expand Down Expand Up @@ -161,27 +172,48 @@ function SupportUsButton({
)}

{/* Organization logo */}
<div>
{typeof organizationInformation.logo === "string" ? (
<span
className="block h-fit w-fit p-4 bg-black text-white rounded-2xl"
title={organizationInformation.logo}
>
<b className="text-2xl italic">
{organizationInformation.logo}
</b>
</span>
) : (
<img
className="w-24 h-24 bg-white/80 pointer-none:cursor-none select-none rounded-2xl object-cover object-center"
src={organizationInformation.logo?.src}
alt={organizationInformation.logo?.alt}
title={organizationInformation.logo?.alt}
draggable={false}
/>
)}
</div>


<div>
{organizationInformation?.logo ? (
validatedUrl ? (
<a
href={validatedUrl}
target="_blank"
rel="noopener noreferrer"
title={`Visit ${organizationInformation?.name}`}
className="inline-block transition-transform duration-200 hover:scale-105 hover:shadow-lg cursor-pointer"
>
{typeof organizationInformation.logo === "string" ? (
<span className="block h-fit w-fit p-4 bg-black text-white rounded-2xl">
<b className="text-2xl italic">
{organizationInformation.logo}
</b>
</span>
) : (
<img
className="w-24 h-24 bg-white/80 select-none rounded-2xl object-cover object-center"
src={organizationInformation.logo.src}
alt={organizationInformation.logo.alt}
draggable={false}
/>
)}
</a>
) : typeof organizationInformation.logo === "string" ? (
<span className="block h-fit w-fit p-4 bg-black text-white rounded-2xl">
<b className="text-2xl italic">
{organizationInformation.logo}
</b>
</span>
) : (
<img
className="w-24 h-24 bg-white/80 select-none rounded-2xl object-cover object-center"
src={organizationInformation.logo.src}
alt={organizationInformation.logo.alt}
draggable={false}
/>
)
) : null}
</div>
{/* Organization name and description */}
<div className="flex flex-col gap-4">
<h2 className={`font-extrabold text-4xl md:text-5xl lg:text-6xl`}>
Expand All @@ -192,6 +224,9 @@ function SupportUsButton({
</p>
</div>




{/* Line */}
{organizationInformation.projectInformation && (
<div
Expand Down Expand Up @@ -290,7 +325,7 @@ function SupportUsButton({
<div className="flex flex-row flex-wrap justify-center items-center gap-10 z-10">
{sponsors.map((sponsor, index) => (
<a
href={sponsor.link}
href={validateUrl(sponsor.link)}
key={index}
target="_blank"
rel="noopener noreferrer"
Expand Down Expand Up @@ -433,7 +468,7 @@ function SupportUsButton({
<div className="flex flex-wrap justify-center items-center gap-5 mt-8">
{ctaSection.sponsorLink.map((link, index) => (
<a
href={link.url}
href={validateUrl(link.url)}
key={index}
{...(link.newTab && { target: "_blank" })}
rel="noopener noreferrer"
Expand All @@ -454,4 +489,4 @@ function SupportUsButton({
);
}

export default SupportUsButton;
export default SupportUsButton;
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export type organizationInformation = {

/** Organization logo */
logo?: Image | string;

url?:string;
projectInformation?: projectInformation;
};

Expand Down
Loading