Skip to content

Commit faf6372

Browse files
authored
feat: support Zod schemas and schema generation script
1 parent b6d298e commit faf6372

File tree

632 files changed

+17107
-1282
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

632 files changed

+17107
-1282
lines changed

README.md

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
# @patternfly/patternfly-component-schemas
22

3-
JSON Schema metadata for PatternFly React components, providing structured validation and documentation for component props.
3+
JSON Schema and Zod schema metadata for PatternFly React components, providing structured validation and documentation for component props.
44

55
## 📦 Installation
66

77
```bash
88
npm install @patternfly/patternfly-component-schemas
99
```
1010

11+
> **Note:** This package includes **Zod v4** for runtime validation support.Learn more at [zod.dev/v4](https://zod.dev/v4).
12+
1113
## 🏗️ Structure
1214

1315
This package uses a split structure for optimal performance and modularity:
@@ -17,17 +19,23 @@ This package uses a split structure for optimal performance and modularity:
1719
├── components/
1820
│ ├── AboutModal/
1921
│ │ ├── schema.json # JSON Schema for AboutModal props
22+
│ │ ├── schema.zod.ts # Zod Schema for AboutModal props ✨
2023
│ │ └── index.js # Component metadata exports
2124
│ ├── Button/
2225
│ │ ├── schema.json
26+
│ │ ├── schema.zod.ts ✨
2327
│ │ └── index.js
2428
│ ├── Alert/
2529
│ │ ├── schema.json
30+
│ │ ├── schema.zod.ts ✨
2631
│ │ └── index.js
2732
│ └── ... (462 total components)
33+
├── zod/
34+
│ └── index.ts # Barrel export of all Zod schemas ✨
2835
├── scripts/
29-
│ └── generate-schemas.js # Generation script
30-
├── index.js # Main export file
36+
│ ├── generate-schemas.js # JSON Schema generation
37+
│ └── generate-zod-schemas.js # Zod Schema generation ✨
38+
├── index.js # Main JSON Schema exports
3139
├── component-metadata.json # Source metadata (dev only)
3240
└── package.json
3341
```
@@ -73,11 +81,37 @@ const buttonSchema = await getComponentSchema('Button');
7381
// Returns JSON Schema with properties, required props, etc.
7482
```
7583

84+
### Zod Schema - Runtime Validation for LLM-Generated Components ✨
85+
```typescript
86+
import { ButtonSchema, AlertSchema } from '@patternfly/patternfly-component-schemas/zod';
87+
88+
// Validate LLM-generated component props at runtime
89+
const llmGeneratedProps = {
90+
variant: "primary",
91+
size: "lg",
92+
children: "Click me"
93+
};
94+
95+
// Type-safe validation with detailed error messages
96+
const validatedProps = ButtonSchema.parse(llmGeneratedProps);
97+
// ✅ Returns type-safe props ready for React component
98+
99+
// Safe parsing with error handling
100+
const result = AlertSchema.safeParse(userInput);
101+
if (result.success) {
102+
// Use result.data with confidence
103+
return <Alert {...result.data} />;
104+
} else {
105+
console.error('Invalid props:', result.error.issues);
106+
}
107+
```
108+
76109
### AI Assistant Examples
77110
- **"What props does the Button component accept?"** → AI reads Button schema
78111
- **"Generate a PatternFly Alert component"** → AI uses Alert schema for validation
79112
- **"Show me all navigation components"** → AI filters components by name/description
80113
- **"Create a form with proper PatternFly components"** → AI selects appropriate form components
114+
- **"Validate this generated component"** → AI uses Zod schema for runtime validation ✨
81115

82116
## 📦 Package Architecture
83117

@@ -148,22 +182,69 @@ The package is generated from `component-metadata.json` which contains the raw P
148182
## 📊 Package Contents
149183

150184
- **462 PatternFly components** with JSON Schema validation
185+
- **462 PatternFly components** with Zod Schema validation ✨
186+
- **3,487 component props** converted to Zod schemas ✨
151187
- **Individual exports** for tree-shaking optimization
152188
- **TypeScript-friendly** prop definitions
189+
- **Runtime type inference** from Zod schemas ✨
153190
- **Enum validation** for variant props
154191
- **Required prop** indicators
155192
- **Default value** documentation
156193

157194
## 🤖 AI & Tooling Benefits
158195

159196
This package is specifically designed for:
160-
- **AI/LLM consumption** via Model Context Protocol
197+
- **AI/LLM consumption** via Model Context Protocol (JSON Schema)
198+
- **Runtime validation** of LLM-generated components (Zod) ✨
199+
- **TypeScript type inference** from Zod schemas ✨
161200
- **IDE autocompletion** and IntelliSense
162201
- **Component validation** and linting
163202
- **Documentation generation**
164203
- **Form builders** and UI tools
165204
- **Code generation** assistants
166205

206+
## 🎯 Usage Examples
207+
208+
### JSON Schema (Documentation & Tooling)
209+
```javascript
210+
import { Button, Alert } from '@patternfly/patternfly-component-schemas';
211+
212+
// Use for documentation, IDE support, or MCP servers
213+
console.log(Button.schema); // Full JSON Schema
214+
console.log(Button.componentName); // "Button"
215+
console.log(Button.propsCount); // 24
216+
```
217+
218+
### Zod Schema (Runtime Validation) ✨
219+
```typescript
220+
import { ButtonSchema, AlertSchema, type ButtonProps } from '@patternfly/patternfly-component-schemas/zod';
221+
import type { z } from 'zod';
222+
223+
// Basic validation
224+
const props = ButtonSchema.parse({ variant: "primary" });
225+
226+
// Type inference
227+
type InferredButtonProps = z.infer<typeof ButtonSchema>;
228+
// Or use the exported type directly
229+
const myProps: ButtonProps = { variant: "primary", size: "lg" };
230+
231+
// Safe parsing with error handling
232+
const result = AlertSchema.safeParse(dynamicProps);
233+
if (result.success) {
234+
console.log('Valid props:', result.data);
235+
} else {
236+
console.error('Validation errors:', result.error.format());
237+
}
238+
239+
// Dynamic component validation
240+
import { getComponentSchema } from '@patternfly/patternfly-component-schemas/zod';
241+
242+
async function validateComponent(name: string, props: unknown) {
243+
const schema = await getComponentSchema(name);
244+
return schema.parse(props);
245+
}
246+
```
247+
167248
## 📄 License
168249

169250
MIT
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Auto-generated Zod schema for AboutModal
2+
import { z } from 'zod'
3+
4+
export const AboutModalSchema = z.object({
5+
/** The parent container to append the modal to. Defaults to document.body */
6+
appendTo: z.any().optional(),
7+
/** Aria label for the about modal. This should be used when no productName prop is provided */
8+
'aria-label': z.string().optional(),
9+
/** The URL or file path of the image for the background */
10+
backgroundImageSrc: z.string().optional(),
11+
/** The alternate text of the brand image */
12+
brandImageAlt: z.string(),
13+
/** The URL of the image for the brand */
14+
brandImageSrc: z.string(),
15+
/** Content rendered inside the about modal */
16+
children: z.custom<React.ReactNode>(),
17+
/** Additional classes added to the about modal */
18+
className: z.string().optional(),
19+
/** Set aria label to the close button */
20+
closeButtonAriaLabel: z.string().optional(),
21+
/** Flag to disable focus trap */
22+
disableFocusTrap: z.boolean().optional(),
23+
/** Prevents the about modal from rendering content inside a container; allows for more flexible layouts */
24+
hasNoContentContainer: z.boolean().optional().default(false),
25+
/** Flag to show the about modal */
26+
isOpen: z.boolean().optional().default(false),
27+
/** A callback for when the close button is clicked */
28+
onClose: z.any().optional().default('(_e): any => undefined'),
29+
/** Product name */
30+
productName: z.string().optional(),
31+
/** Trademark information */
32+
trademark: z.string().optional()
33+
})
34+
35+
export type AboutModalProps = z.infer<typeof AboutModalSchema>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Auto-generated Zod schema for AboutModalBox
2+
// Generated on: 2025-10-10T18:12:17.794Z
3+
import { z } from 'zod'
4+
5+
export const AboutModalBoxSchema = z.object({
6+
/** Content rendered inside the about modal box */
7+
children: z.any(),
8+
/** Additional classes added to the about modal box */
9+
className: z.string().optional()
10+
})
11+
12+
export type AboutModalBoxProps = z.infer<typeof AboutModalBoxSchema>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Auto-generated Zod schema for AboutModalBoxBrand
2+
import { z } from 'zod'
3+
4+
export const AboutModalBoxBrandSchema = z.object({
5+
/** The alternate text of the brand image. */
6+
alt: z.string(),
7+
/** The URL of the image for the brand. */
8+
src: z.string()
9+
})
10+
11+
export type AboutModalBoxBrandProps = z.infer<typeof AboutModalBoxBrandSchema>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Auto-generated Zod schema for AboutModalBoxCloseButton
2+
import { z } from 'zod'
3+
4+
export const AboutModalBoxCloseButtonSchema = z.object({
5+
/** Set close button aria label */
6+
'aria-label': z.string().optional().default('Close Dialog'),
7+
/** A callback for when the close button is clicked */
8+
onClose: z.any().optional().default('(_e) => undefined as any')
9+
})
10+
11+
export type AboutModalBoxCloseButtonProps = z.infer<typeof AboutModalBoxCloseButtonSchema>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Auto-generated Zod schema for AboutModalBoxCloseButtonProps
2+
import { z } from 'zod'
3+
4+
export const AboutModalBoxCloseButtonPropsSchema = z.object({
5+
/** A callback for when the close button is clicked */
6+
onClose: z.any().optional(),
7+
/** Set close button aria label */
8+
'Unknown': z.string().optional()
9+
})
10+
11+
export type AboutModalBoxCloseButtonPropsProps = z.infer<typeof AboutModalBoxCloseButtonPropsSchema>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Auto-generated Zod schema for AboutModalBoxContent
2+
import { z } from 'zod'
3+
4+
export const AboutModalBoxContentSchema = z.object({
5+
/** Content rendered inside the about modal box content */
6+
children: z.custom<React.ReactNode>(),
7+
/** Prevents the about modal from rendering content inside a container; allows for more flexible layouts */
8+
hasNoContentContainer: z.boolean().optional().default(false),
9+
/** The trademark info for the product */
10+
trademark: z.string()
11+
})
12+
13+
export type AboutModalBoxContentProps = z.infer<typeof AboutModalBoxContentSchema>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Auto-generated Zod schema for AboutModalBoxHeader
2+
import { z } from 'zod'
3+
4+
export const AboutModalBoxHeaderSchema = z.object({
5+
/** Id to use for about modal box header */
6+
id: z.string(),
7+
/** Name of the product */
8+
productName: z.string().optional()
9+
})
10+
11+
export type AboutModalBoxHeaderProps = z.infer<typeof AboutModalBoxHeaderSchema>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Auto-generated Zod schema for AboutModalProps
2+
import { z } from 'zod'
3+
4+
export const AboutModalPropsSchema = z.object({
5+
/** The parent container to append the modal to. Defaults to document.body */
6+
appendTo: z.any().optional(),
7+
/** The URL or file path of the image for the background */
8+
backgroundImageSrc: z.string().optional(),
9+
/** The alternate text of the brand image */
10+
brandImageAlt: z.string(),
11+
/** The URL of the image for the brand */
12+
brandImageSrc: z.string(),
13+
/** Content rendered inside the about modal */
14+
children: z.custom<React.ReactNode>(),
15+
/** Additional classes added to the about modal */
16+
className: z.string().optional(),
17+
/** Set aria label to the close button */
18+
closeButtonAriaLabel: z.string().optional(),
19+
/** Flag to disable focus trap */
20+
disableFocusTrap: z.boolean().optional(),
21+
/** Prevents the about modal from rendering content inside a container; allows for more flexible layouts */
22+
hasNoContentContainer: z.boolean().optional(),
23+
/** Flag to show the about modal */
24+
isOpen: z.boolean().optional(),
25+
/** A callback for when the close button is clicked */
26+
onClose: z.any().optional(),
27+
/** Product name */
28+
productName: z.string().optional(),
29+
/** Trademark information */
30+
trademark: z.string().optional(),
31+
/** Aria label for the about modal. This should be used when no productName prop is provided */
32+
'Unknown': z.string().optional()
33+
})
34+
35+
export type AboutModalPropsProps = z.infer<typeof AboutModalPropsSchema>

components/Accordion/schema.zod.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Auto-generated Zod schema for Accordion
2+
import { z } from 'zod'
3+
4+
export const AccordionSchema = z.object({
5+
/** Adds accessible text to the Accordion */
6+
'aria-label': z.string().optional(),
7+
/** Flag to indicate whether use definition list or div */
8+
asDefinitionList: z.boolean().optional().default(true),
9+
/** Content rendered inside the Accordion */
10+
children: z.custom<React.ReactNode>().optional().default('null'),
11+
/** Additional classes added to the Accordion */
12+
className: z.string().optional().default(''),
13+
/** Display size variant. */
14+
displaySize: z.enum(['default', 'lg']).optional().default('default'),
15+
/** Heading level to use */
16+
headingLevel: z.enum(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']).optional().default('h3'),
17+
/** Flag to indicate the accordion had a border */
18+
isBordered: z.boolean().optional().default(false),
19+
/** Sets the toggle icon position for all accordion toggles. */
20+
togglePosition: z.enum(['start', 'end']).optional().default('end')
21+
})
22+
23+
export type AccordionProps = z.infer<typeof AccordionSchema>

0 commit comments

Comments
 (0)