gravitee-api-management / graphene-snippets
Cursor rule469 starsChanged 5 months agoApache-2.0View on GitHub
// Snippet: Render a plugin JSON Schema in a single useForm // // Use when a host module receives a JSON Schema (from a Gravitee plugin or // API definition) and the form has no fields besides those declared in the // schema. One useForm, one resolver, one submit handler. // Do NOT use for hand-coded forms — write the fields directly with Graphene // primitives (Field, Input, Select…) instead. // Do NOT use when the form has host-owned meta…
In this repository31 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .ai/AGENTS.mdAGENTS.md
- gravitee-apim-common/AGENTS.mdAGENTS.md
- gravitee-apim-console-webui/AGENTS.mdAGENTS.md
- gravitee-apim-definition/AGENTS.mdAGENTS.md
- gravitee-apim-distribution/AGENTS.mdAGENTS.md
- gravitee-apim-gateway/AGENTS.mdAGENTS.md
- gravitee-apim-plugin/AGENTS.mdAGENTS.md
- gravitee-apim-portal-webui/AGENTS.mdAGENTS.md
- gravitee-apim-portal-webui-next/AGENTS.mdAGENTS.md
- gravitee-apim-reporter/AGENTS.mdAGENTS.md
- gravitee-apim-repository/AGENTS.mdAGENTS.md
- gravitee-apim-rest-api/AGENTS.mdAGENTS.md
- .ai/CLAUDE.mdCLAUDE.md
- CLAUDE.mdCLAUDE.md
- gravitee-apim-common/CLAUDE.mdCLAUDE.md
- gravitee-apim-console-webui/CLAUDE.mdCLAUDE.md
- gravitee-apim-definition/CLAUDE.mdCLAUDE.md
- gravitee-apim-distribution/CLAUDE.mdCLAUDE.md
- gravitee-apim-gateway/CLAUDE.mdCLAUDE.md
- gravitee-apim-plugin/CLAUDE.mdCLAUDE.md
- gravitee-apim-portal-webui/CLAUDE.mdCLAUDE.md
- gravitee-apim-portal-webui-next/CLAUDE.mdCLAUDE.md
- gravitee-apim-reporter/CLAUDE.mdCLAUDE.md
- gravitee-apim-repository/CLAUDE.mdCLAUDE.md
- gravitee-apim-rest-api/CLAUDE.mdCLAUDE.md
- .cursor/rules/graphene.mdCursor rule
- .cursor/rules/graphene-snippets/context-sidebar-detail-page.tsxCursor rule
- .cursor/rules/graphene-snippets/creation-wizard-page.tsxCursor rule
- .cursor/rules/graphene-snippets/data-table-list-page.tsxCursor rule
- .cursor/rules/graphene-snippets/json-schema-form-with-meta.tsxCursor rule
The file
Files
// Snippet: Render a plugin JSON Schema in a single useForm
//
// Use when a host module receives a JSON Schema (from a Gravitee plugin or
// API definition) and the form has **no fields besides those declared in the
// schema**. One useForm, one resolver, one submit handler.
// Do NOT use for hand-coded forms — write the fields directly with Graphene
// primitives (Field, Input, Select…) instead.
// Do NOT use when the form has host-owned meta fields (name, description,
// etc.) alongside the schema — see snippets/json-schema-form-with-meta.tsx.
//
// Replace {PLACEHOLDERS} with your actual values.
// See Storybook "Composed/JsonSchemaForm → Playground" for an interactive demo.
import { useMemo } from 'react';
import { useForm } from 'react-hook-form';
import { Button, extractDefaults, JsonSchemaForm, jsonSchemaResolver } from '@gravitee/graphene-core';
import type { JsonSchema } from '@gravitee/graphene-core';
interface PluginConfigPageProps {
// Immutable: passing a new reference re-builds the form and recompiles ajv.
readonly pluginSchema: JsonSchema;
// Pass a stable reference (e.g. from a TanStack Query / SWR cache, or wrap with `useMemo`
// at the call site). A fresh object on every parent render defeats the `defaultValues`
// memo and re-seeds the form, wiping in-progress user edits.
readonly initialValue?: Record<string, unknown>;
// Host environment / feature flags / lock state read by `gioConfig.displayIf` /
// `gioConfig.disableIf` conditions of the form `{ "context.X": value }`. Remove this prop
// entirely if your schemas declare no `context.X` references.
readonly environment: 'production' | 'staging' | 'dev';
readonly readonlyMode: boolean;
readonly onSave: (data: Record<string, unknown>) => void;
}
export function PluginConfigPage({
pluginSchema,
initialValue,
environment,
readonlyMode,
onSave,
}: PluginConfigPageProps) {
// Memoize so `jsonSchemaResolver` does not recompile ajv on every parent render.
const resolver = useMemo(() => jsonSchemaResolver(pluginSchema), [pluginSchema]);
const defaultValues = useMemo(
() => initialValue ?? (extractDefaults(pluginSchema) as Record<string, unknown>) ?? {},
[pluginSchema, initialValue],
);
// Stable object identity for the `context` prop. An inline `context={{ environment, readonlyMode }}`
// would create a new ref on every parent render and bust JsonSchemaForm's internal memo, re-rendering
// every field. `useMemo` keyed on the actual values keeps the ref stable until they change.
const formContext = useMemo(() => ({ environment, readonlyMode }), [environment, readonlyMode]);
const form = useForm({
resolver,
defaultValues,
criteriaMode: 'all', // surface every failing ajv keyword per field (FieldError renders them as a list)
mode: 'onTouched', // validate on first blur, then on every change
});
// `noValidate` disables the browser's HTML5 validation popups so the resolver +
// <FieldError> remain the single source of validation messages.
return (
<form noValidate onSubmit={form.handleSubmit(onSave)} className="flex flex-1 flex-col gap-4 p-8">
<JsonSchemaForm schema={pluginSchema} control={form.control} name="" context={formContext} />
<Button type="submit">Save</Button>
</form>
);
}
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

