- 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!
+
+
+
+
+
+ Contribute to the JSON Schema Docs
+
+
+ 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!
+
-
- To get started as a Docs contributor:
-
-
-
- -
+
+
+
+ To get started as a Docs contributor:
+
+
+ -
Familiarize yourself with our project's{' '}
Contribution Guide
{' '}
and our{' '}
Code of Conduct
.
- -
+
-
Head over to our{' '}
JSON Schema Docs Board
.
- -
+
-
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.
- -
+
-
If there is no work done in that Docs issue yet, feel free to open a
PR and get started!
-
- Docs contributor questions?
-
-
- Do you have a documentation contributor question? Please leave a comment
- in the issue or PR or join the
#contribute
or{' '}
-
#documentation
channels on{' '}
-
+
- Slack
- {' '}
- and leave a message.
+ Docs contributor questions?
+
+
+ Do you have a documentation contributor question? Please leave a
+ comment in the issue or PR or join the #contribute
or{' '}
+ #documentation
channels on{' '}
+
+ Slack
+ {' '}
+ and leave a message.
+
-
- );
-};
+
+
+);
diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx
new file mode 100644
index 000000000..6369ce192
--- /dev/null
+++ b/components/ui/badge.tsx
@@ -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
& { asChild?: boolean }) {
+ const Comp = asChild ? Slot : 'span';
+
+ return (
+
+ );
+}
+
+export { Badge, badgeVariants };
diff --git a/components/ui/card.tsx b/components/ui/card.tsx
new file mode 100644
index 000000000..8c45af7d7
--- /dev/null
+++ b/components/ui/card.tsx
@@ -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 {
+ className?: string;
+}
+
+function Card({ className, ...props }: CardProps) {
+ return (
+
+ );
+}
+
+Card.propTypes = {
+ className: PropTypes.string,
+};
+
+interface CardHeaderProps extends React.HTMLAttributes {
+ className?: string;
+}
+
+function CardHeader({ className, ...props }: CardHeaderProps) {
+ return (
+
+ );
+}
+
+CardHeader.propTypes = {
+ className: PropTypes.string,
+};
+
+interface CardTitleProps extends React.HTMLAttributes {
+ className?: string;
+}
+
+function CardTitle({ className, ...props }: CardTitleProps) {
+ return (
+
+ );
+}
+
+CardTitle.propTypes = {
+ className: PropTypes.string,
+};
+
+interface CardDescriptionProps extends React.HTMLAttributes {
+ className?: string;
+}
+
+function CardDescription({ className, ...props }: CardDescriptionProps) {
+ return (
+
+ );
+}
+
+CardDescription.propTypes = {
+ className: PropTypes.string,
+};
+
+interface CardActionProps extends React.HTMLAttributes {
+ className?: string;
+}
+
+function CardAction({ className, ...props }: CardActionProps) {
+ return (
+
+ );
+}
+
+CardAction.propTypes = {
+ className: PropTypes.string,
+};
+
+interface CardContentProps extends React.HTMLAttributes {
+ className?: string;
+}
+
+function CardContent({ className, ...props }: CardContentProps) {
+ return (
+
+ );
+}
+
+CardContent.propTypes = {
+ className: PropTypes.string,
+};
+
+interface CardFooterProps extends React.HTMLAttributes {
+ className?: string;
+}
+
+function CardFooter({ className, ...props }: CardFooterProps) {
+ return (
+
+ );
+}
+
+CardFooter.propTypes = {
+ className: PropTypes.string,
+};
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+};
diff --git a/cypress/components/Badge.cy.tsx b/cypress/components/Badge.cy.tsx
new file mode 100644
index 000000000..2f9a024fe
--- /dev/null
+++ b/cypress/components/Badge.cy.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import Badge from '../../pages/tools/components/ui/Badge';
+
+describe('Badge Component', () => {
+ it('renders with correct base classes', () => {
+ cy.mount(Test Badge);
+ cy.get('span').should('have.class', 'mx-[4px]');
+ cy.get('span').should('have.class', 'my-[4px]');
+ cy.get('span').should('have.class', 'bg-[#e2e8f0]');
+ cy.get('span').should('have.class', 'dark:bg-[#0f172a]');
+ cy.get('span').should('have.class', 'text-[#0f172a]');
+ cy.get('span').should('have.class', 'dark:text-white');
+ cy.get('span').should('have.class', 'inline-flex');
+ cy.get('span').should('have.class', 'items-center');
+ cy.get('span').should('have.class', 'justify-center');
+ cy.get('span').should('have.class', 'rounded-md');
+ cy.get('span').should('have.class', 'border');
+ });
+
+ it('renders with different variants', () => {
+ cy.mount(Default Badge);
+ cy.get('span').should('have.class', 'bg-[#e2e8f0]');
+ cy.get('span').should('have.class', 'dark:bg-[#0f172a]');
+ cy.get('span').should('have.class', 'text-[#0f172a]');
+ cy.get('span').should('have.class', 'dark:text-white');
+
+ cy.mount(Secondary Badge);
+ cy.get('span').should('have.class', 'bg-[#e2e8f0]');
+ cy.get('span').should('have.class', 'dark:bg-[#0f172a]');
+ cy.get('span').should('have.class', 'text-[#0f172a]');
+ cy.get('span').should('have.class', 'dark:text-white');
+
+ cy.mount(Outline Badge);
+ cy.get('span').should('have.class', 'bg-[#e2e8f0]');
+ cy.get('span').should('have.class', 'dark:bg-[#0f172a]');
+ cy.get('span').should('have.class', 'text-[#0f172a]');
+ cy.get('span').should('have.class', 'dark:text-white');
+
+ cy.mount(Destructive Badge);
+ cy.get('span').should('have.class', 'bg-[#e2e8f0]');
+ cy.get('span').should('have.class', 'dark:bg-[#0f172a]');
+ cy.get('span').should('have.class', 'text-[#0f172a]');
+ cy.get('span').should('have.class', 'dark:text-white');
+ });
+
+ it('renders children content correctly', () => {
+ const content = 'Test Badge Content';
+ cy.mount({content});
+ cy.get('span').should('contain', content);
+ });
+});
diff --git a/cypress/components/Remember.cy.tsx b/cypress/components/Remember.cy.tsx
index 970cdeda7..5766bd7e3 100644
--- a/cypress/components/Remember.cy.tsx
+++ b/cypress/components/Remember.cy.tsx
@@ -8,10 +8,13 @@ describe('Remember Component', () => {
cy.mount();
// Should have the correct elements and text
-
cy.get('[data-test="remember-heading"]')
- .should('have.prop', 'tagName', 'H3')
- .and('contain.text', 'Remember');
+ .should('have.prop', 'tagName', 'DIV')
+ .and('contain.text', 'Remember')
+ .and('have.class', 'text-h5mobile')
+ .and('have.class', 'md:text-h5')
+ .and('have.class', 'leading-none')
+ .and('have.class', 'font-semibold');
cy.get('[data-test="contribute-docs-span"]')
.should('have.prop', 'tagName', 'SPAN')
diff --git a/cypress/components/Tag.cy.tsx b/cypress/components/Tag.cy.tsx
new file mode 100644
index 000000000..166d9ff3b
--- /dev/null
+++ b/cypress/components/Tag.cy.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import Tag from '../../pages/tools/components/ui/Tag';
+
+describe('Tag Component', () => {
+ it('renders with default neutral intent', () => {
+ cy.mount(Default Tag);
+ cy.get('span').should('have.class', 'bg-slate-500/10');
+ cy.get('span').should('have.class', 'dark:bg-slate-500/20');
+ cy.get('span').should('have.class', 'text-slate-700');
+ cy.get('span').should('have.class', 'dark:text-slate-400');
+ cy.get('span').should('have.class', 'inline-flex');
+ cy.get('span').should('have.class', 'items-center');
+ cy.get('span').should('have.class', 'justify-center');
+ cy.get('span').should('have.class', 'rounded-md');
+ cy.get('span').should('have.class', 'border');
+ });
+
+ it('renders with success intent', () => {
+ cy.mount(Success Tag);
+ cy.get('span').should('have.class', 'bg-emerald-500/10');
+ cy.get('span').should('have.class', 'dark:bg-emerald-500/20');
+ cy.get('span').should('have.class', 'text-emerald-700');
+ cy.get('span').should('have.class', 'dark:text-emerald-400');
+ });
+
+ it('renders with warning intent', () => {
+ cy.mount(Warning Tag);
+ cy.get('span').should('have.class', 'bg-amber-500/10');
+ cy.get('span').should('have.class', 'dark:bg-amber-500/20');
+ cy.get('span').should('have.class', 'text-amber-700');
+ cy.get('span').should('have.class', 'dark:text-amber-400');
+ });
+
+ it('renders with error intent', () => {
+ cy.mount(Error Tag);
+ cy.get('span').should('have.class', 'bg-red-500/10');
+ cy.get('span').should('have.class', 'dark:bg-red-500/20');
+ cy.get('span').should('have.class', 'text-red-700');
+ cy.get('span').should('have.class', 'dark:text-red-400');
+ });
+
+ it('renders children content correctly', () => {
+ const content = 'Test Tag Content';
+ cy.mount({content});
+ cy.get('span').should('contain', content);
+ });
+
+ it('renders with correct base classes', () => {
+ cy.mount(Test Tag);
+ cy.get('span').should('have.class', 'mr-2');
+ cy.get('span').should('have.class', 'text-[12px]');
+ cy.get('span').should('have.class', 'font-semibold');
+ });
+});
diff --git a/cypress/components/ui/card.cy.tsx b/cypress/components/ui/card.cy.tsx
new file mode 100644
index 000000000..d9cfb42d6
--- /dev/null
+++ b/cypress/components/ui/card.cy.tsx
@@ -0,0 +1,36 @@
+// card.cy.tsx
+
+import React from 'react';
+import {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+} from '@/components/ui/card';
+
+describe('Card Component', () => {
+ it('renders all Card subcomponents correctly', () => {
+ cy.mount(
+
+
+ Test Title
+ Action
+
+ Description here
+ Content goes here
+ Footer
+ ,
+ );
+
+ cy.get('[data-slot="card"]').should('exist');
+ cy.get('[data-slot="card-header"]').should('exist');
+ cy.get('[data-slot="card-title"]').contains('Test Title');
+ cy.get('[data-slot="card-action"]').contains('Action');
+ cy.get('[data-slot="card-description"]').contains('Description here');
+ cy.get('[data-slot="card-content"]').contains('Content goes here');
+ cy.get('[data-slot="card-footer"]').contains('Footer');
+ });
+});
diff --git a/package.json b/package.json
index 22ab9473e..7fc4ea15b 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"dependencies": {
"@docsearch/react": "3.8.0",
"@radix-ui/react-checkbox": "^1.3.1",
- "@radix-ui/react-slot": "^1.2.2",
+ "@radix-ui/react-slot": "^1.2.3",
"@types/jsonpath": "^0.2.4",
"axios": "1.7.7",
"babel-loader": "^9.2.1",
diff --git a/pages/tools/components/ui/Badge.tsx b/pages/tools/components/ui/Badge.tsx
index 2085773c3..961615179 100644
--- a/pages/tools/components/ui/Badge.tsx
+++ b/pages/tools/components/ui/Badge.tsx
@@ -1,10 +1,19 @@
import React, { ReactNode } from 'react';
+import { Badge as ShadcnBadge } from '../../../../components/ui/badge';
-const Badge = ({ children }: { children: ReactNode }) => {
+interface BadgeProps {
+ children: ReactNode;
+ variant?: 'default' | 'secondary' | 'outline' | 'destructive';
+}
+
+const Badge = ({ children, variant = 'secondary' }: BadgeProps) => {
return (
-
+
{children}
-
+
);
};
diff --git a/pages/tools/components/ui/Tag.tsx b/pages/tools/components/ui/Tag.tsx
index 73d31ef2e..541ec6426 100644
--- a/pages/tools/components/ui/Tag.tsx
+++ b/pages/tools/components/ui/Tag.tsx
@@ -1,5 +1,5 @@
import React, { ReactNode } from 'react';
-import classnames from 'classnames';
+import { Badge as ShadcnBadge } from '../../../../components/ui/badge';
interface TagProps {
children: ReactNode;
@@ -7,23 +7,31 @@ interface TagProps {
}
const Tag = ({ children, intent = 'neutral' }: TagProps) => {
+ const styles = {
+ success:
+ 'bg-emerald-500/10 dark:bg-emerald-500/20 text-emerald-700 dark:text-emerald-400',
+ warning:
+ 'bg-amber-500/10 dark:bg-amber-500/20 text-amber-700 dark:text-amber-400',
+ error: 'bg-red-500/10 dark:bg-red-500/20 text-red-700 dark:text-red-400',
+ neutral:
+ 'bg-slate-500/10 dark:bg-slate-500/20 text-slate-700 dark:text-slate-400',
+ } as const;
+
return (
-
{children}
-
+
);
};
diff --git a/yarn.lock b/yarn.lock
index 53bb6412d..b434e8767 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3389,7 +3389,7 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/react-slot@npm:1.2.2, @radix-ui/react-slot@npm:^1.2.2":
+"@radix-ui/react-slot@npm:1.2.2":
version: 1.2.2
resolution: "@radix-ui/react-slot@npm:1.2.2"
dependencies:
@@ -3404,6 +3404,21 @@ __metadata:
languageName: node
linkType: hard
+"@radix-ui/react-slot@npm:^1.2.3":
+ version: 1.2.3
+ resolution: "@radix-ui/react-slot@npm:1.2.3"
+ dependencies:
+ "@radix-ui/react-compose-refs": "npm:1.1.2"
+ peerDependencies:
+ "@types/react": "*"
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
+ peerDependenciesMeta:
+ "@types/react":
+ optional: true
+ checksum: 10c0/5913aa0d760f505905779515e4b1f0f71a422350f077cc8d26d1aafe53c97f177fec0e6d7fbbb50d8b5e498aa9df9f707ca75ae3801540c283b26b0136138eef
+ languageName: node
+ linkType: hard
+
"@radix-ui/react-use-controllable-state@npm:1.2.2":
version: 1.2.2
resolution: "@radix-ui/react-use-controllable-state@npm:1.2.2"
@@ -8534,7 +8549,7 @@ __metadata:
"@docsearch/react": "npm:3.8.0"
"@next/eslint-plugin-next": "npm:^14.0.1"
"@radix-ui/react-checkbox": "npm:^1.3.1"
- "@radix-ui/react-slot": "npm:^1.2.2"
+ "@radix-ui/react-slot": "npm:^1.2.3"
"@svgr/webpack": "npm:^8.1.0"
"@types/babel__core": "npm:^7"
"@types/babel__preset-env": "npm:^7"