Skip to content

Migrate Tag, Badge, and Remember Components to ShadCN and Add Tests #1689

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 14, 2025
Merged
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
131 changes: 67 additions & 64 deletions components/Remember.tsx
Original file line number Diff line number Diff line change
@@ -1,103 +1,106 @@
import React from 'react';
import { Card, CardHeader, CardContent, CardTitle } from '@/components/ui/card';
const linkProps = { className: 'underline', rel: 'noreferrer' };

export const Remember = () => {
return (
<div className='flex mt-7 flex-col rounded-md shadow-md border border-gray-200 p-4 mt-2'>
<h3
className='flex text-h5mobile md:text-h5 font-semibold border-b pb-3'
export const Remember = () => (
<Card className='mt-7'>
<CardHeader className='flex flex-row items-center space-x-3 pb-3 border-b'>
<svg
xmlns='http://www.w3.org/2000/svg'
width='32'
height='32'
fill='currentColor'
className='bi bi-info-circle-fill'
viewBox='0 0 16 16'
>
<path d='M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z' />
</svg>
<CardTitle
className='text-h5mobile md:text-h5'
data-test='remember-heading'
>
<svg
xmlns='http://www.w3.org/2000/svg'
width='32'
height='32'
fill='currentColor'
className='bi bi-info-circle-fill mr-3'
viewBox='0 0 16 16'
>
<path d='M2 6a6 6 0 1 1 10.174 4.31c-.203.196-.359.4-.453.619l-.762 1.769A.5.5 0 0 1 10.5 13a.5.5 0 0 1 0 1 .5.5 0 0 1 0 1l-.224.447a1 1 0 0 1-.894.553H6.618a1 1 0 0 1-.894-.553L5.5 15a.5.5 0 0 1 0-1 .5.5 0 0 1 0-1 .5.5 0 0 1-.46-.302l-.761-1.77a1.964 1.964 0 0 0-.453-.618A5.984 5.984 0 0 1 2 6zm6-5a5 5 0 0 0-3.479 8.592c.263.254.514.564.676.941L5.83 12h4.342l.632-1.467c.162-.377.413-.687.676-.941A5 5 0 0 0 8 1z' />
</svg>{' '}
Remember
</h3>
<span
className='inline-block mt-2 ml-2 font-medium antialiased font-semibold'
data-test='contribute-docs-span'
>
Contribute to the JSON Schema Docs
</span>
<div className='mt-2 mb-2' data-test='contribute-docs-div'>
Code isn't the only way to contribute to OSS; Docs are extremely import
for the JSON Schema Ecosystem. At JSON Schema, We value Docs
contributions as much as every other type of contribution!
</CardTitle>
</CardHeader>
<CardContent className='space-y-6'>
<div className='space-y-2'>
<span
className='antialiased font-semibold'
data-test='contribute-docs-span'
>
Contribute to the JSON Schema Docs
</span>
<p data-test='contribute-docs-div'>
Code isn't the only way to contribute to OSS; Docs are extremely
import for the JSON Schema Ecosystem. At JSON Schema, We value Docs
contributions as much as every other type of contribution!
</p>
</div>
<span
className='inline-block mt-3 ml-2 font-medium antialiased font-semibold'
data-test='get-started-span'
>
To get started as a Docs contributor:
</span>
<div className='mt-2 mb-2'>
<ol className='list-decimal mt-2 mb-4 ml-5'>
<li className='mt-1 leading-7'>

<div className='space-y-2'>
<span
className='antialiased font-semibold'
data-test='get-started-span'
>
To get started as a Docs contributor:
</span>
<ol className='list-decimal ml-5 space-y-2'>
<li className='leading-7'>
Familiarize yourself with our project's{' '}
<a
className='underline'
rel='noreferrer'
{...linkProps}
href='https://github.com/json-schema-org/community/blob/main/CONTRIBUTING.md'
>
Contribution Guide
</a>{' '}
and our{' '}
<a
className='underline'
rel='noreferrer'
{...linkProps}
href='https://github.com/json-schema-org/.github/blob/main/CODE_OF_CONDUCT.md'
>
Code of Conduct
</a>
.
</li>
<li className='mt-1 leading-7'>
<li className='leading-7'>
Head over to our{' '}
<a
className='underline'
rel='noreferrer'
{...linkProps}
href='https://github.com/orgs/json-schema-org/projects/16'
>
JSON Schema Docs Board
</a>
.
</li>
<li className='mt-1 leading-7'>
<li className='leading-7'>
Pick an issue you would like to contribute to and leave a comment
introducing yourself. This is also the perfect place to leave any
questions you may have on how to get started.
</li>
<li className='mt-1 leading-7'>
<li className='leading-7'>
If there is no work done in that Docs issue yet, feel free to open a
PR and get started!
</li>
</ol>
</div>
<span
className='inline-block ml-2 font-medium antialiased font-semibold'
data-test='contribute-docs-questions-span'
>
Docs contributor questions?
</span>
<div className='mt-2 mb-2' data-test='contribute-docs-questions-div'>
Do you have a documentation contributor question? Please leave a comment
in the issue or PR or join the <code>#contribute</code> or{' '}
<code>#documentation</code> channels on{' '}
<a
className='underline'
rel='noreferrer'
href='https://json-schema.org/slack'

<div className='space-y-2'>
<span
className='antialiased font-semibold'
data-test='contribute-docs-questions-span'
>
Slack
</a>{' '}
and leave a message.
Docs contributor questions?
</span>
<p data-test='contribute-docs-questions-div'>
Do you have a documentation contributor question? Please leave a
comment in the issue or PR or join the <code>#contribute</code> or{' '}
<code>#documentation</code> channels on{' '}
<a {...linkProps} href='https://json-schema.org/slack'>
Slack
</a>{' '}
and leave a message.
</p>
</div>
</div>
);
};
</CardContent>
</Card>
);
47 changes: 47 additions & 0 deletions components/ui/badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable linebreak-style */
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';

import { cn } from '@/lib/utils';

const badgeVariants = cva(
'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
{
variants: {
variant: {
default:
'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
secondary:
'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
destructive:
'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline:
'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
},
},
defaultVariants: {
variant: 'default',
},
},
);

function Badge({
className,
variant,
asChild = false,
...props
}: React.ComponentProps<'span'> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
const Comp = asChild ? Slot : 'span';

return (
<Comp
data-slot='badge'
className={cn(badgeVariants({ variant }), className)}
{...props}
/>
);
}

export { Badge, badgeVariants };
151 changes: 151 additions & 0 deletions components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/* eslint-disable linebreak-style */
/* eslint-disable react/prop-types */
import * as React from 'react';
import PropTypes from 'prop-types';

import { cn } from '@/lib/utils';

interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function Card({ className, ...props }: CardProps) {
return (
<div
data-slot='card'
className={cn(
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
className,
)}
{...props}
/>
);
}

Card.propTypes = {
className: PropTypes.string,
};

interface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function CardHeader({ className, ...props }: CardHeaderProps) {
return (
<div
data-slot='card-header'
className={cn(
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6',
className,
)}
{...props}
/>
);
}

CardHeader.propTypes = {
className: PropTypes.string,
};

interface CardTitleProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function CardTitle({ className, ...props }: CardTitleProps) {
return (
<div
data-slot='card-title'
className={cn('leading-none font-semibold', className)}
{...props}
/>
);
}

CardTitle.propTypes = {
className: PropTypes.string,
};

interface CardDescriptionProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function CardDescription({ className, ...props }: CardDescriptionProps) {
return (
<div
data-slot='card-description'
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
}

CardDescription.propTypes = {
className: PropTypes.string,
};

interface CardActionProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function CardAction({ className, ...props }: CardActionProps) {
return (
<div
data-slot='card-action'
className={cn(
'col-start-2 row-span-2 row-start-1 self-start justify-self-end',
className,
)}
{...props}
/>
);
}

CardAction.propTypes = {
className: PropTypes.string,
};

interface CardContentProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function CardContent({ className, ...props }: CardContentProps) {
return (
<div
data-slot='card-content'
className={cn('px-6', className)}
{...props}
/>
);
}

CardContent.propTypes = {
className: PropTypes.string,
};

interface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}

function CardFooter({ className, ...props }: CardFooterProps) {
return (
<div
data-slot='card-footer'
className={cn('flex items-center px-6 [.border-t]:pt-6', className)}
{...props}
/>
);
}

CardFooter.propTypes = {
className: PropTypes.string,
};

export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
};
Loading