Skip to content

Commit ff7c35e

Browse files
Chore: update utility-functions.md to add missing documentation (#5036)
- Updated `utility-functions.md` to add missing documentation - Added `CLAUDE.md`
1 parent c021895 commit ff7c35e

2 files changed

Lines changed: 271 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Commands
6+
7+
```bash
8+
# Install dependencies
9+
npm install
10+
11+
# Build all packages (parallel)
12+
npm run build
13+
14+
# Build all packages (sequential, use if parallel causes issues)
15+
npm run build-serial
16+
17+
# Run all tests
18+
npm test
19+
20+
# Lint
21+
npm run lint
22+
23+
# Prettier check / format
24+
npm run cs-check
25+
npm run cs-format
26+
27+
# Run a single package's tests
28+
cd packages/core && npm test
29+
30+
# Watch mode for a single package
31+
cd packages/core && npm run test:watch
32+
33+
# Update snapshots
34+
cd packages/snapshot-tests && npm run test:update
35+
36+
# Start the playground (interactive demo)
37+
cd packages/playground && npm start
38+
39+
# Full sanity check (lint + build + test)
40+
npm run sanity-check
41+
```
42+
43+
Individual package builds output three module formats: `build:cjs`, `build:esm`, `build:umd`.
44+
45+
## Architecture
46+
47+
This is an **npm workspaces + Nx** monorepo. All packages live under `packages/` and are scoped as `@rjsf/*`.
48+
49+
### Package roles
50+
51+
| Package | Role |
52+
|---|---|
53+
| `@rjsf/utils` | Shared types, 80+ utility functions, schema helpers. No UI dependencies. |
54+
| `@rjsf/core` | Core `Form` component, Bootstrap 3 as default theme, `withTheme()` HOC. |
55+
| `@rjsf/validator-ajv8` | AJV 8-based validator. Exported via `customizeValidator()`. |
56+
| `@rjsf/snapshot-tests` | Shared snapshot test suite consumed by all theme packages. |
57+
| Theme packages (`@rjsf/mui`, `@rjsf/antd`, `@rjsf/chakra-ui`, etc.) | UI-library-specific implementations of fields, widgets, and templates. |
58+
| `@rjsf/playground` | Vite app importing all themes, used for manual testing and demos. |
59+
| `@rjsf/docs` | Docusaurus documentation site. |
60+
61+
### Registry pattern (the plugin system)
62+
63+
The `Registry` object is the core extension point passed to every field/widget/template:
64+
65+
```typescript
66+
Registry = {
67+
fields, // Map of field type → Field component
68+
widgets, // Map of widget name → Widget component
69+
templates, // Layout/structural templates
70+
rootSchema, // Root JSON Schema
71+
formContext, // Arbitrary context object threaded to all components
72+
schemaUtils, // Schema parsing & validation helpers
73+
translateString,
74+
globalFormOptions,
75+
}
76+
```
77+
78+
Override fields/widgets/templates per-form via props, or globally via `withTheme()`.
79+
80+
### Theme pattern
81+
82+
Every theme package follows the same structure:
83+
1. Imports `withTheme` from `@rjsf/core`
84+
2. Defines custom `Templates`, `Widgets`, and optionally `Fields`
85+
3. Calls `withTheme({ templates, widgets, fields })` to produce a themed `Form`
86+
4. Exports: `Form` (default), `Theme`, `Templates`, `Widgets`
87+
88+
### Field → Widget → Template hierarchy
89+
90+
- **Fields** handle schema-level logic (ObjectField, ArrayField, MultiSchemaField for oneOf/anyOf, etc.)
91+
- **Widgets** handle individual input rendering (TextWidget, SelectWidget, CheckboxWidget, etc.)
92+
- **Templates** control structural/layout rendering (FieldTemplate, ArrayFieldTemplate, ButtonTemplates, etc.)
93+
94+
Fields select which widget to render based on schema `type` and `ui:widget`. Templates wrap the output for consistent styling.
95+
96+
### Form rendering flow
97+
98+
1. Caller provides `schema`, `validator` (required), `formData`, and `uiSchema`
99+
2. `Form` creates `schemaUtils` from validator + schema
100+
3. Schema is recursively decomposed into fields → widgets → templates
101+
4. On change/blur: callbacks fire with updated `formData` and `errorSchema`
102+
5. On submit: full validation runs, then `onSubmit` fires (or `onError` if invalid)
103+
104+
### Key `@rjsf/utils` exports to know
105+
106+
- Schema helpers: `findSchemaDefinition`, `mergeSchemas`, `getSchemaType`, `createSchemaUtils`
107+
- Form data: `mergeDefaultsWithFormData`, `removeOptionalEmptyObjects`
108+
- Error handling: `toErrorSchema`, `toErrorList`
109+
- Enum helpers: `enumOptionsSelectValue`, `enumOptionsDeselectValue`
110+
- React hooks: `useDeepCompareMemo`, `useFileWidgetProps`, `useAltDateWidgetProps`
111+
112+
## Code style
113+
114+
- **TypeScript strict mode**, ES2020 target
115+
- **Prettier**: single quotes, JSX single quotes, 120-char print width
116+
- **ESLint**: enforces semicolons, curly braces, no-console, React Hooks rules
117+
- Pre-commit hook (Husky + lint-staged) auto-formats and lints staged files
118+
119+
## Testing
120+
121+
- Jest + `@swc/jest` (fast transpilation), jsdom, Testing Library
122+
- Snapshot tests in `@rjsf/snapshot-tests` are shared across theme packages — run `test:update` there when changing core rendering
123+
- Node >=20 required

packages/docs/docs/api-reference/utility-functions.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,20 @@ Otherwise, the string is wrapped by `Number()` and if that result is not `NaN`,
7070

7171
- undefined | null | string | number: The `value` converted to a number when appropriate, otherwise the `value`
7272

73+
### bracketNameGenerator()
74+
75+
Generates bracketed names for form fields.
76+
77+
#### Parameters
78+
79+
- path: FieldPathList - The path of field path units to use when generating the name
80+
- idPrefix: string - The prefix to use at the start of the generated name
81+
- [isMultiValue]: boolean | undefined - Optional flag, if true, will append `[]` to the end of the name for multi-value fields (e.g., checkboxes, multi-select)
82+
83+
#### Returns
84+
85+
- string: The generated bracketed name (e.g., `root[tasks][0][title]`, or `root[hobbies][]` for multi-value fields)
86+
7387
### buttonId()
7488

7589
Return a consistent `id` for the `btn` button element
@@ -167,6 +181,20 @@ Return a consistent `id` for the field description element.
167181

168182
- string: The consistent id for the field description element from the given `id`
169183

184+
### dotNotationNameGenerator()
185+
186+
Generates dot-notation names for form fields. Multi-value fields are handled the same as single-value fields in dot notation.
187+
188+
#### Parameters
189+
190+
- path: FieldPathList - The path of field path units to use when generating the name
191+
- idPrefix: string - The prefix to use at the start of the generated name
192+
- [_isMultiValue]: boolean | undefined - Optional flag (unused in dot notation)
193+
194+
#### Returns
195+
196+
- string: The generated dot-notation name (e.g., `root.tasks.0.title`)
197+
170198
### englishStringTranslator()
171199

172200
Translates a `TranslatableString` value `stringToTranslate` into english.
@@ -197,6 +225,25 @@ If it is a single value, then if the enum option value with the `valueIndex` in
197225

198226
- EnumOptionsType<S>["value"][]: The updated `selected` list with the `value` removed from it
199227

228+
### enumOptionSelectedValue<S extends StrictRJSFSchema = RJSFSchema>()
229+
230+
Computes the value to pass to a select element's `value` attribute.
231+
When `format` is `'realValue'`, converts form data values to strings.
232+
When `format` is `'indexed'` (the default), resolves to index-based values via `enumOptionsIndexForValue`.
233+
Returns `emptyValue` when the current value is empty.
234+
235+
#### Parameters
236+
237+
- value: any - The current form data value
238+
- enumOptions: EnumOptionsType<S>[] | undefined - The available enum options
239+
- multiple: boolean - Whether the select allows multiple selections
240+
- [format='indexed']: OptionValueFormat - How option values are encoded on the DOM
241+
- emptyValue: any - The value to return when the selection is empty
242+
243+
#### Returns
244+
245+
- any: The value to use for the select element's `value` attribute
246+
200247
### enumOptionsIndexForValue<S extends StrictRJSFSchema = RJSFSchema>()
201248

202249
Returns the index(es) of the options in `allEnumOptions` whose value(s) match the ones in `value`.
@@ -257,6 +304,41 @@ If `valueIndex` is an array, AND it contains an invalid index, the returned arra
257304

258305
- EnumOptionsType<S>["value"] | EnumOptionsType<S>["value"][] | undefined: The single or list of values specified by the single or list of indexes if they are valid. Otherwise, `emptyValue` or an empty list.
259306

307+
### enumOptionValueDecoder<S extends StrictRJSFSchema = RJSFSchema>()
308+
309+
Decodes a string from a DOM value attribute back to a typed enum value.
310+
When `format` is `'realValue'`, does a reverse lookup: finds the enum option whose `String(value)` matches the input string and returns the original typed value.
311+
For object/array values that were encoded as indices, falls back to index resolution.
312+
When `format` is `'indexed'` (the default), uses index-based resolution via `enumOptionsValueForIndex`.
313+
314+
#### Parameters
315+
316+
- value: string | string[] - The string value(s) from the DOM
317+
- enumOptions: EnumOptionsType<S>[] | undefined - The available enum options
318+
- [format='indexed']: OptionValueFormat - How the values were encoded on the DOM
319+
- emptyValue: unknown - The value to return for empty/missing selections
320+
321+
#### Returns
322+
323+
- unknown: The original typed enum value(s)
324+
325+
### enumOptionValueEncoder()
326+
327+
Encodes an enum option value into a string for a DOM value attribute.
328+
When `format` is `'realValue'`, primitive values are converted via `String()`.
329+
Non-primitive values (objects, arrays) fall back to the index since `String()` would produce `"[object Object]"`.
330+
When `format` is `'indexed'` (the default), returns the index as a string.
331+
332+
#### Parameters
333+
334+
- value: unknown - The typed enum value
335+
- index: number - The option's position in the enumOptions array
336+
- [format='indexed']: OptionValueFormat - How to encode the value for the DOM attribute
337+
338+
#### Returns
339+
340+
- string: The string to use as the DOM value attribute
341+
260342
### errorId()
261343

262344
Return a consistent `id` for the field error element.
@@ -383,6 +465,20 @@ This function does not work with discriminators of `"type": "object"` and `"type
383465

384466
- number | undefined: index of the matched option
385467

468+
### getOptionValueFormat()
469+
470+
Resolves the effective `optionValueFormat` for enum-backed widgets.
471+
Provides a single source of truth for the default DOM encoding format (`'indexed'`) used by `SelectWidget`, `RadioWidget`, and `CheckboxesWidget`.
472+
Widgets should call this helper once and pass the result to `enumOptionValueEncoder`, `enumOptionValueDecoder`, and `enumOptionSelectedValue` rather than reading `options.optionValueFormat` directly.
473+
474+
#### Parameters
475+
476+
- [options]: \{ optionValueFormat?: OptionValueFormat } | undefined - The widget options (typically from the `options` prop, already resolved from `ui:options` and `ui:globalOptions`)
477+
478+
#### Returns
479+
480+
- OptionValueFormat: The resolved `OptionValueFormat`, defaulting to `'indexed'` when not set
481+
386482
### getSchemaType()
387483

388484
Gets the type of a given `schema`.
@@ -858,6 +954,27 @@ When a `params` array is provided, each value in the array is used to replace an
858954

859955
- string: The updated string with any replacement specifiers replaced
860956

957+
### resolveUiSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
958+
959+
Resolves the uiSchema for a given schema, considering `ui:definitions` stored in the registry.
960+
Called at runtime for each field. When the schema contains a `$ref`, looks up the corresponding uiSchema definition from `registry.uiSchemaDefinitions` and merges it with local overrides.
961+
For schemas with `oneOf`/`anyOf` branches, also populates `uiSchema[keyword][i]` for branches whose `$ref` matches a definition, so `MultiSchemaField` can read dropdown option titles.
962+
963+
Resolution order (later sources override earlier):
964+
965+
1. `ui:definitions[$ref]` - base definition from registry
966+
2. `localUiSchema` - local overrides at current path
967+
968+
#### Parameters
969+
970+
- schema: S - The JSON schema (may contain `$ref` or `RJSF_REF_KEY`)
971+
- localUiSchema: UiSchema<T, S, F> | undefined - The uiSchema at the current path (local overrides)
972+
- registry: Registry<T, S, F> - The registry containing `uiSchemaDefinitions`
973+
974+
#### Returns
975+
976+
- UiSchema<T, S, F>: The resolved uiSchema with definitions merged in
977+
861978
### schemaRequiresTrueValue<S extends StrictRJSFSchema = RJSFSchema>()
862979

863980
Check to see if a `schema` specifies that a value must be true. This happens when:
@@ -875,6 +992,20 @@ Check to see if a `schema` specifies that a value must be true. This happens whe
875992

876993
- boolean: True if the schema specifies a value that must be true, false otherwise
877994

995+
### shallowEquals()
996+
997+
Implements a shallow equals comparison that uses `Object.is()` for comparing values.
998+
This function compares objects by checking if all keys and their values are equal using `Object.is()`.
999+
1000+
#### Parameters
1001+
1002+
- a: any - The first element to compare
1003+
- b: any - The second element to compare
1004+
1005+
#### Returns
1006+
1007+
- boolean: True if the `a` and `b` are shallow equal, false otherwise
1008+
8781009
### shouldRender()
8791010

8801011
Determines whether the given `component` should be rerendered by comparing its current set of props and state against the next set.
@@ -1291,6 +1422,23 @@ Checks to see if the `schema` combination represents a select
12911422

12921423
- boolean: True if schema contains a select, otherwise false
12931424

1425+
### removeOptionalEmptyObjects<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
1426+
1427+
Recursively removes optional objects from the `formData` that are empty (i.e., all their fields are undefined, null, empty strings, or themselves empty optional objects).
1428+
This solves the problem where interacting with fields inside an optional object "activates" it permanently, making the form unsubmittable when the optional object has required inner fields.
1429+
An object property is considered "optional" when it is NOT listed in its parent schema's `required` array.
1430+
1431+
#### Parameters
1432+
1433+
- validator: ValidatorType<T, S, F> - An implementation of the `ValidatorType` interface that will be used when necessary
1434+
- schema: S - The JSON schema describing the `formData`
1435+
- [rootSchema]: S | undefined - The root schema, used primarily to look up `$ref`s
1436+
- [formData]: T | undefined - The current form data to prune
1437+
1438+
#### Returns
1439+
1440+
- T | undefined: A new copy of `formData` with empty optional objects removed, or `undefined` if the entire formData was pruned
1441+
12941442
### retrieveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>()
12951443

12961444
Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies

0 commit comments

Comments
 (0)