@@ -23,7 +23,7 @@ import {
23
23
import validator from '@rjsf/validator-ajv8' ;
24
24
import { render , screen , within } from '@testing-library/react' ;
25
25
import userEvent from '@testing-library/user-event' ;
26
- import { get , has , omit , pick } from 'lodash' ;
26
+ import { get , has , isEmpty , omit , pick } from 'lodash' ;
27
27
28
28
import LayoutGridField , {
29
29
GridType ,
@@ -593,6 +593,44 @@ const arraySchema: RJSFSchema = {
593
593
const outerArraySchema = arraySchema ?. properties ?. example as RJSFSchema ;
594
594
const innerArraySchema = outerArraySchema ?. items as RJSFSchema ;
595
595
596
+ const nestedSchema : RJSFSchema = {
597
+ type : 'object' ,
598
+ properties : {
599
+ listOfStrings : {
600
+ type : 'array' ,
601
+ title : 'A list of strings' ,
602
+ items : {
603
+ type : 'string' ,
604
+ default : 'bazinga' ,
605
+ } ,
606
+ } ,
607
+ mapOfStrings : {
608
+ type : 'object' ,
609
+ title : 'A map of strings' ,
610
+ additionalProperties : {
611
+ type : 'string' ,
612
+ default : 'bazinga' ,
613
+ } ,
614
+ } ,
615
+ } ,
616
+ } ;
617
+
618
+ const nestedUiSchema : UiSchema = {
619
+ 'ui:field' : 'LayoutGridField' ,
620
+ 'ui:layoutGrid' : {
621
+ 'ui:row' : {
622
+ children : [
623
+ {
624
+ 'ui:columns' : {
625
+ span : 6 ,
626
+ children : [ 'listOfStrings' , 'mapOfStrings' ] ,
627
+ } ,
628
+ } ,
629
+ ] ,
630
+ } ,
631
+ } ,
632
+ } ;
633
+
596
634
const ERRORS = [ 'error' ] ;
597
635
const EXTRA_ERROR = new ErrorSchemaBuilder ( ) . addErrors ( ERRORS ) . ErrorSchema ;
598
636
const DEFAULT_ID = 'test-id' ;
@@ -672,6 +710,7 @@ const gridFormSchemaRegistry = getTestRegistry(GRID_FORM_SCHEMA, REGISTRY_FIELDS
672
710
const sampleSchemaRegistry = getTestRegistry ( SAMPLE_SCHEMA , REGISTRY_FIELDS , { } , { } , REGISTRY_FORM_CONTEXT ) ;
673
711
const readonlySchemaRegistry = getTestRegistry ( readonlySchema , REGISTRY_FIELDS , { } , { } , REGISTRY_FORM_CONTEXT ) ;
674
712
const arraySchemaRegistry = getTestRegistry ( arraySchema , REGISTRY_FIELDS , { } , { } , REGISTRY_FORM_CONTEXT ) ;
713
+ const nestedSchemaRegistry = getTestRegistry ( nestedSchema , REGISTRY_FIELDS , { } , { } , REGISTRY_FORM_CONTEXT ) ;
675
714
const GRID_FORM_ID_SCHEMA = gridFormSchemaRegistry . schemaUtils . toIdSchema ( GRID_FORM_SCHEMA ) ;
676
715
const SAMPLE_SCHEMA_ID_SCHEMA = sampleSchemaRegistry . schemaUtils . toIdSchema ( SAMPLE_SCHEMA ) ;
677
716
const READONLY_ID_SCHEMA = readonlySchemaRegistry . schemaUtils . toIdSchema ( readonlySchema ) ;
@@ -713,6 +752,10 @@ function getExpectedPropsForField(
713
752
required = result ?. required ?. includes ( name ) || false ;
714
753
return schema1 ;
715
754
} , props . schema ) ;
755
+ // Null out nested properties that can show up when additionalProperties is specified
756
+ if ( ! isEmpty ( schema ?. properties ) ) {
757
+ schema . properties = { } ;
758
+ }
716
759
// Get the readonly options from the schema, if any
717
760
const readonly = get ( schema , 'readOnly' ) ;
718
761
// Get the options from the schema's oneOf, if any
@@ -1501,6 +1544,60 @@ describe('LayoutGridField', () => {
1501
1544
await userEvent . tab ( ) ;
1502
1545
expect ( props . onBlur ) . toHaveBeenCalledWith ( fieldId , 'foo' ) ;
1503
1546
} ) ;
1547
+ test ( 'renderField via name explicit layoutGridSchema, nested array' , async ( ) => {
1548
+ const fieldName = 'listOfStrings' ;
1549
+ const props = getProps ( {
1550
+ schema : nestedSchema ,
1551
+ uiSchema : nestedUiSchema ,
1552
+ layoutGridSchema : fieldName ,
1553
+ idSeparator : '.' ,
1554
+ registry : nestedSchemaRegistry ,
1555
+ } ) ;
1556
+ const fieldId = get ( props . idSchema , [ fieldName , ID_KEY ] ) ;
1557
+ render ( < LayoutGridField { ...props } /> ) ;
1558
+ // Renders a field
1559
+ const field = screen . getByTestId ( LayoutGridField . TEST_IDS . field ) ;
1560
+ expect ( field ) . toHaveTextContent ( stringifyProps ( getExpectedPropsForField ( props , fieldName ) ) ) ;
1561
+ // Test onChange, onFocus, onBlur
1562
+ const input = within ( field ) . getByRole ( 'textbox' ) ;
1563
+ // Click on the input to cause the focus
1564
+ await userEvent . click ( input ) ;
1565
+ expect ( props . onFocus ) . toHaveBeenCalledWith ( fieldId , '' ) ;
1566
+ // Type to trigger the onChange
1567
+ await userEvent . type ( input , 'foo' ) ;
1568
+ // Due to the selection of schema type = `array` the path is appended to the fieldName, duplicating it
1569
+ expect ( props . onChange ) . toHaveBeenCalledWith ( 'foo' , [ fieldName , fieldName ] , props . errorSchema , fieldId ) ;
1570
+ // Tab out of the input field to cause the blur
1571
+ await userEvent . tab ( ) ;
1572
+ expect ( props . onBlur ) . toHaveBeenCalledWith ( fieldId , 'foo' ) ;
1573
+ } ) ;
1574
+ test ( 'renderField via name explicit layoutGridSchema, nested object' , async ( ) => {
1575
+ const fieldName = 'mapOfStrings' ;
1576
+ const props = getProps ( {
1577
+ schema : nestedSchema ,
1578
+ uiSchema : nestedUiSchema ,
1579
+ layoutGridSchema : fieldName ,
1580
+ idSeparator : '.' ,
1581
+ registry : nestedSchemaRegistry ,
1582
+ } ) ;
1583
+ const fieldId = get ( props . idSchema , [ fieldName , ID_KEY ] ) ;
1584
+ render ( < LayoutGridField { ...props } /> ) ;
1585
+ // Renders a field
1586
+ const field = screen . getByTestId ( LayoutGridField . TEST_IDS . field ) ;
1587
+ expect ( field ) . toHaveTextContent ( stringifyProps ( getExpectedPropsForField ( props , fieldName ) ) ) ;
1588
+ // Test onChange, onFocus, onBlur
1589
+ const input = within ( field ) . getByRole ( 'textbox' ) ;
1590
+ // Click on the input to cause the focus
1591
+ await userEvent . click ( input ) ;
1592
+ expect ( props . onFocus ) . toHaveBeenCalledWith ( fieldId , '' ) ;
1593
+ // Type to trigger the onChange
1594
+ await userEvent . type ( input , 'foo' ) ;
1595
+ // Due to the selection of schema type = `object` the path is appended to the fieldName, duplicating it
1596
+ expect ( props . onChange ) . toHaveBeenCalledWith ( 'foo' , [ fieldName , fieldName ] , props . errorSchema , fieldId ) ;
1597
+ // Tab out of the input field to cause the blur
1598
+ await userEvent . tab ( ) ;
1599
+ expect ( props . onBlur ) . toHaveBeenCalledWith ( fieldId , 'foo' ) ;
1600
+ } ) ;
1504
1601
test ( 'renderField via object explicit layoutGridSchema, otherProps' , ( ) => {
1505
1602
const fieldName = 'employment' ;
1506
1603
const globalUiOptions = { propToApplyToAllFields : 'foobar' } ;
0 commit comments