@syncfusion/ej2-react-form-renderer 0.1.1-beta → 0.1.2-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,7 +78,7 @@ function App() {
78
78
 
79
79
  return (
80
80
  <FormRenderer
81
- dataSchema={schema}
81
+ schema={schema}
82
82
  onSubmit={handleSubmit}
83
83
  onChange={handleChange}
84
84
  />
@@ -105,8 +105,7 @@ export default App;
105
105
 
106
106
  | Prop | Type | Description |
107
107
  |------|------|-------------|
108
- | `dataSchema` | `DataSchema \| FormSchema \| string` | Form schema (JSON or object) |
109
- | `uiSchema` | `UISchema \| string` | Optional UI layout schema |
108
+ | `schema` | `Schema \| FormSchema \| string` | Form schema (JSON or object) |
110
109
  | `layout` | `'single' \| 'twocolumn' \| 'threecolumn' \| 'fourcolumn'` | Form layout (when no UI schema) |
111
110
  | `onSubmit` | `(args: SubmitEventArgs) => void` | Submit handler |
112
111
  | `onChange` | `(args: ChangeEventArgs) => void` | Field change handler |
@@ -125,12 +124,6 @@ export const FormLayout = {
125
124
  } as const;
126
125
  ```
127
126
 
128
- ## Schema Formats
129
-
130
- The FormRenderer supports multiple schema formats:
131
-
132
- 1. **DataSchema**: JSON Schema-compatible format
133
- 2. **UISchema**: Separate UI layout configuration
134
127
 
135
128
  ## Support
136
129
 
@@ -145,4 +138,4 @@ Product support is available through the following channels:
145
138
 
146
139
  > A [free community license](https://www.syncfusion.com/products/communitylicense) is also available for companies and individuals whose organizations have less than $1 million USD in annual gross revenue and five or fewer developers.
147
140
 
148
- © Copyright 2026 Syncfusion®, Inc. All Rights Reserved. The Syncfusion® Essential® Studio license and copyright applies to this distribution.
141
+ © Copyright 2026 Syncfusion®, Inc. All Rights Reserved. The Syncfusion® Essential® Studio license and copyright applies to this distribution.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncfusion/ej2-react-form-renderer",
3
- "version": "0.1.1-beta",
3
+ "version": "0.1.2-beta",
4
4
  "description": "Form renderer component for rendering dynamic JSON-driven forms with Syncfusion EJ2 React components",
5
5
  "author": "Syncfusion Inc.",
6
6
  "license": "SEE LICENSE IN license",
@@ -42,7 +42,7 @@
42
42
  "@syncfusion/ej2-react-navigations": "*",
43
43
  "@syncfusion/ej2-react-notifications": "*",
44
44
  "@syncfusion/ej2-react-popups": "*",
45
- "@syncfusion/ej2-react-richtexteditor": "^32.1.1",
45
+ "@syncfusion/ej2-react-richtexteditor": "*",
46
46
  "@syncfusion/ej2-react-splitbuttons": "*"
47
47
  },
48
48
  "devDependencies": {}
@@ -39,7 +39,8 @@ const CheckboxGroup = ({ component, formState, value, onChange, optionRef }) =>
39
39
  width: '100%'
40
40
  };
41
41
  const htmlAttrs = resolveAttributes(component.htmlAttributes);
42
- return (_jsx("div", { className: `checkbox-group ${component.cssClass || ''}`, style: containerStyle, ...htmlAttrs, role: "group", "aria-label": groupLabel, title: component.tooltip, children: _jsx("div", { className: "checkbox-group-items", style: itemsStyle, children: (component.options || ['Option 1']).map((option, idx) => {
42
+ const options = Array.isArray(component.options) ? component.options : ['Option 1'];
43
+ return (_jsx("div", { className: `checkbox-group ${component.cssClass || ''}`, style: containerStyle, ...htmlAttrs, role: "group", "aria-label": groupLabel, title: component.tooltip, children: _jsx("div", { className: "checkbox-group-items", style: itemsStyle, children: (options).map((option, idx) => {
43
44
  const optionValue = typeof option === 'string' ? option : (option.value ?? option.label ?? option.key);
44
45
  const optionLabel = typeof option === 'string' ? option : (option.label ?? option.value ?? String(optionValue));
45
46
  return (_jsx(CheckBoxComponent, { id: `${component.id}_${idx}`, label: optionLabel, checked: selectedItems.includes(optionValue), disabled: component.disabled, labelPosition: component.checkboxLabelPosition || 'After', change: (e) => handleCheckboxChange(optionValue, e.checked), cssClass: `${component.cssClass}${component.size === 'Small' ? ' e-small' : component.size === 'Bigger' ? ' e-bigger' : ''}`, htmlAttributes: htmlAttrs, ref: optionRef ? (el) => optionRef(el, idx) : undefined }, `${component.id}-${idx}`));
@@ -34,7 +34,7 @@
34
34
  * ```
35
35
  */
36
36
  import React from 'react';
37
- import type { ConditionType, FormComponentConditions } from '../FormBuilder/types/FormSchema';
37
+ import type { ConditionType, FormComponentConditions, ConditionalDataRule, ChoiceBasedFieldRule } from '../FormBuilder/types/FormSchema';
38
38
  /**
39
39
  * Form field value type (aligned with FormValidator)
40
40
  */
@@ -250,4 +250,46 @@ export declare class ConditionalRuleEngine {
250
250
  * ```
251
251
  */
252
252
  static getConditionalValue(conditions: FormComponentConditions | undefined, formValues: Record<string, FormValueType>): any;
253
+ /**
254
+ * Evaluate conditional data availability
255
+ * Returns true if any conditional data rule's condition is met
256
+ *
257
+ * @param conditionalDataRules - Array of ConditionalDataRule objects
258
+ * @param formValues - Current form field values
259
+ * @returns Index of first matching rule, or -1 if none match
260
+ */
261
+ static evaluateConditionalDataRules(conditionalDataRules: ConditionalDataRule[] | undefined, formValues: Record<string, FormValueType>): number;
262
+ /**
263
+ * Get filtered options for a dependent dropdown based on choice-based field rule
264
+ *
265
+ * If choiceBasedField rule exists and primary field has a value,
266
+ * return only the mapped dependent options for that primary value.
267
+ * Otherwise return all options.
268
+ *
269
+ * @param rule - ChoiceBasedFieldRule configuration
270
+ * @param allDependentOptions - Complete list of dependent field options
271
+ * @param formValues - Current form values
272
+ * @param primaryFieldId - ID of primary field
273
+ * @returns Filtered options array
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * const rule = {
278
+ * primaryFieldId: 'dept-id',
279
+ * choiceMapping: {
280
+ * 'Marketing': ['Markers', 'Sticky Notes'],
281
+ * 'Finance': ['Calculator']
282
+ * }
283
+ * };
284
+ *
285
+ * const filtered = ConditionalRuleEngine.getChoiceBasedOptions(
286
+ * rule,
287
+ * ['Markers', 'Sticky Notes', 'Calculator'],
288
+ * { 'dept-id': 'Marketing' },
289
+ * 'dept-id'
290
+ * );
291
+ * // Returns: ['Markers', 'Sticky Notes']
292
+ * ```
293
+ */
294
+ static getChoiceBasedOptions(rule: ChoiceBasedFieldRule, allDependentOptions: string[], formValues: Record<string, FormValueType>, primaryFieldId: string): string[];
253
295
  }
@@ -486,4 +486,81 @@ export class ConditionalRuleEngine {
486
486
  return undefined;
487
487
  }
488
488
  }
489
+ /**
490
+ * Evaluate conditional data availability
491
+ * Returns true if any conditional data rule's condition is met
492
+ *
493
+ * @param conditionalDataRules - Array of ConditionalDataRule objects
494
+ * @param formValues - Current form field values
495
+ * @returns Index of first matching rule, or -1 if none match
496
+ */
497
+ static evaluateConditionalDataRules(conditionalDataRules, formValues) {
498
+ if (!conditionalDataRules || !Array.isArray(conditionalDataRules)) {
499
+ return -1;
500
+ }
501
+ for (let i = 0; i < conditionalDataRules.length; i++) {
502
+ const rule = conditionalDataRules[i];
503
+ if (this.evaluateCondition(rule.condition, formValues)) {
504
+ return i; // Return index of first matching rule
505
+ }
506
+ }
507
+ return -1;
508
+ }
509
+ /**
510
+ * Get filtered options for a dependent dropdown based on choice-based field rule
511
+ *
512
+ * If choiceBasedField rule exists and primary field has a value,
513
+ * return only the mapped dependent options for that primary value.
514
+ * Otherwise return all options.
515
+ *
516
+ * @param rule - ChoiceBasedFieldRule configuration
517
+ * @param allDependentOptions - Complete list of dependent field options
518
+ * @param formValues - Current form values
519
+ * @param primaryFieldId - ID of primary field
520
+ * @returns Filtered options array
521
+ *
522
+ * @example
523
+ * ```typescript
524
+ * const rule = {
525
+ * primaryFieldId: 'dept-id',
526
+ * choiceMapping: {
527
+ * 'Marketing': ['Markers', 'Sticky Notes'],
528
+ * 'Finance': ['Calculator']
529
+ * }
530
+ * };
531
+ *
532
+ * const filtered = ConditionalRuleEngine.getChoiceBasedOptions(
533
+ * rule,
534
+ * ['Markers', 'Sticky Notes', 'Calculator'],
535
+ * { 'dept-id': 'Marketing' },
536
+ * 'dept-id'
537
+ * );
538
+ * // Returns: ['Markers', 'Sticky Notes']
539
+ * ```
540
+ */
541
+ static getChoiceBasedOptions(rule, allDependentOptions, formValues, primaryFieldId) {
542
+ try {
543
+ // Get primary field value from form
544
+ const primaryValue = formValues[primaryFieldId];
545
+ if (primaryValue === null || primaryValue === undefined) {
546
+ // No primary value selected - return all or empty based on rule config
547
+ return rule.showAllWhenNotMapped ? allDependentOptions : [];
548
+ }
549
+ // Ensure primary value is treated as string for comparison
550
+ const primaryValueStr = String(primaryValue);
551
+ // Check if this primary value is mapped in the rule
552
+ if (primaryValueStr in rule.choiceMapping) {
553
+ const mappedOptions = rule.choiceMapping[primaryValueStr];
554
+ // Validate mapped options exist in all options (safety check)
555
+ return mappedOptions.filter(opt => allDependentOptions.includes(opt));
556
+ }
557
+ // Primary value not mapped - return all or empty based on rule config
558
+ return rule.showAllWhenNotMapped ? allDependentOptions : [];
559
+ }
560
+ catch (error) {
561
+ console.error('Error in getChoiceBasedOptions:', error);
562
+ // Safe default - return all options
563
+ return allDependentOptions;
564
+ }
565
+ }
489
566
  }
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React, { useImperativeHandle, useRef, useState, useCallback, useMemo, useEffect } from 'react';
3
- import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Toolbar, Edit, Page, Resize, Sort, } from '@syncfusion/ej2-react-grids';
3
+ import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Toolbar, Edit, Page, Filter, Resize, Sort, } from '@syncfusion/ej2-react-grids';
4
4
  (() => {
5
5
  try {
6
6
  const filterPrefix = '[EJ2Grid.Warning]';
@@ -36,7 +36,7 @@ import { GridComponent, ColumnsDirective, ColumnDirective, Inject, Toolbar, Edit
36
36
  * - Maps GridColumn.validationRules to ColumnDirective.validationRules
37
37
  * - Renders Syncfusion toolbar with CRUD actions based on allowAdding/Editing/Deleting
38
38
  */
39
- export const DataGridComponent = React.forwardRef(({ id, columns, dataSource = [], height = '200px', width = '100%', locale, cssClass, pageSize = 6, allowPaging = true, allowAdding = true, allowEditing = true, allowDeleting = true, onDataChange }, ref) => {
39
+ export const DataGridComponent = React.forwardRef(({ id, columns, dataSource = [], height = '200px', width = '100%', locale, cssClass, pageSize = 6, allowPaging = true, allowAdding = true, allowEditing = true, allowDeleting = true, allowFiltering = false, onDataChange }, ref) => {
40
40
  // Internal state: maintain current grid data
41
41
  const [_currentData, setCurrentData] = useState(Array.isArray(dataSource) ? dataSource : []);
42
42
  const gridRef = useRef(null);
@@ -55,11 +55,14 @@ export const DataGridComponent = React.forwardRef(({ id, columns, dataSource = [
55
55
  }
56
56
  setTimeout(() => {
57
57
  if (gridRef.current?.refresh) {
58
+ if (dataSource && Array.isArray(dataSource) && dataSource.length > 0) {
59
+ gridRef.current.dataSource = dataSource;
60
+ }
58
61
  gridRef.current.refresh();
59
62
  }
60
63
  }, 10);
61
64
  }
62
- }, [columns, allowAdding, allowDeleting, allowEditing, allowPaging, pageSize]);
65
+ }, [columns, allowAdding, allowDeleting, allowEditing, allowPaging, pageSize, allowFiltering]);
63
66
  useEffect(() => {
64
67
  const normalized = Array.isArray(dataSource) ? dataSource : [];
65
68
  setCurrentData(normalized);
@@ -134,11 +137,11 @@ export const DataGridComponent = React.forwardRef(({ id, columns, dataSource = [
134
137
  * Memoize toolbar to prevent unnecessary re-renders
135
138
  */
136
139
  const toolbarConfig = useMemo(() => buildToolbar(), [buildToolbar]);
137
- return (_jsx("div", { className: "datagrid-wrapper", style: { width, height: 'auto' }, children: _jsx("div", { className: "datagrid-grid", children: _jsxs(GridComponent, { ref: gridRef, id: id, dataSource: _currentData, allowPaging: allowPaging, pageSettings: { pageSize }, editSettings: {
140
+ return (_jsx("div", { className: "datagrid-wrapper", style: { width, height: 'auto' }, children: _jsx("div", { className: "datagrid-grid", children: _jsxs(GridComponent, { ref: gridRef, id: id, dataSource: _currentData, allowPaging: allowPaging, filterSettings: { mode: "Immediate" }, pageSettings: { pageSize }, editSettings: {
138
141
  allowEditing,
139
142
  allowAdding,
140
143
  allowDeleting,
141
144
  mode: 'Normal',
142
- }, locale: locale || 'en-US', cssClass: cssClass, toolbar: toolbarConfig, actionComplete: handleActionComplete, height: typeof height === 'string' ? height : `${height}px`, width: typeof width === 'string' ? width : `${width}px`, allowSorting: true, allowFiltering: false, allowSelection: true, children: [_jsx(ColumnsDirective, { children: columns && columns.length > 0 && columns.map((col) => (_jsx(ColumnDirective, { field: col.field, headerText: col.headerText, type: col.type || 'string', isPrimaryKey: col.isPrimaryKey || false, width: col.width, format: col.type === 'date' ? (col.format || 'MM/dd/yyyy') : col.format, editType: col.editType, validationRules: mapValidationRules(col) }, col.field))) }), _jsx(Inject, { services: [Edit, Toolbar, Page, Resize, Sort] })] }) }) }));
145
+ }, locale: locale || 'en-US', cssClass: cssClass, toolbar: toolbarConfig, actionComplete: handleActionComplete, height: typeof height === 'string' ? height : `${height}px`, width: typeof width === 'string' ? width : `${width}px`, allowSorting: true, allowFiltering: allowFiltering, allowSelection: true, children: [_jsx(ColumnsDirective, { children: columns && columns.length > 0 && columns.map((col) => (_jsx(ColumnDirective, { field: col.field, headerText: col.headerText, type: col.type || 'string', isPrimaryKey: col.isPrimaryKey || false, width: col.width, format: col.type === 'date' ? (col.format || 'MM/dd/yyyy') : col.format, editType: col.editType, validationRules: mapValidationRules(col) }, col.field))) }), _jsx(Inject, { services: [Edit, Toolbar, Page, Resize, Sort, Filter] })] }) }) }));
143
146
  });
144
147
  DataGridComponent.displayName = 'DataGridComponent';
@@ -38,13 +38,7 @@ export interface FormRendererProps extends Omit<React.HTMLAttributes<HTMLDivElem
38
38
  * - FormSchema object (components converted to DataSchema)
39
39
  * - DataSchema object (used directly)
40
40
  */
41
- dataSchema?: DataSchema | string | FormSchema | any;
42
- /**
43
- * UI Schema - can be provided as:
44
- * - JSON string (validated and parsed)
45
- * - UISchema object (used directly)
46
- */
47
- uiSchema?: UISchema | string;
41
+ schema?: DataSchema | string | FormSchema | any;
48
42
  /**
49
43
  * Specifies the locale/language for the form component.
50
44
  * Supports locale-specific formatting and translations.