@@ -5,7 +5,7 @@ import { fireEvent, act, render, waitFor } from '@testing-library/react';
55import { Simulate } from 'react-dom/test-utils' ;
66import { findDOMNode } from 'react-dom' ;
77import { Portal } from 'react-portal' ;
8- import { getTemplate , getUiOptions } from '@rjsf/utils' ;
8+ import { getTemplate , getUiOptions , optionalControlsId , buttonId } from '@rjsf/utils' ;
99import validator , { customizeValidator } from '@rjsf/validator-ajv8' ;
1010
1111import Form from '../src' ;
@@ -4945,4 +4945,289 @@ describe('Form omitExtraData and liveOmit', () => {
49454945 expect ( errors ) . to . have . lengthOf ( 0 ) ;
49464946 } ) ;
49474947 } ) ;
4948+
4949+ describe ( 'optionalDataControls' , ( ) => {
4950+ const schema = {
4951+ title : 'test' ,
4952+ properties : {
4953+ nestedObjectOptional : {
4954+ type : 'object' ,
4955+ properties : {
4956+ test : {
4957+ type : 'string' ,
4958+ } ,
4959+ } ,
4960+ } ,
4961+ nestedArrayOptional : {
4962+ type : 'array' ,
4963+ items : {
4964+ type : 'string' ,
4965+ } ,
4966+ } ,
4967+ } ,
4968+ } ;
4969+ const arrayOnUiSchema = {
4970+ 'ui:globalOptions' : {
4971+ enableOptionalDataFieldForType : [ 'array' ] ,
4972+ } ,
4973+ } ;
4974+ const objectOnUiSchema = {
4975+ 'ui:globalOptions' : {
4976+ enableOptionalDataFieldForType : [ 'object' ] ,
4977+ } ,
4978+ } ;
4979+ const bothOnUiSchema = {
4980+ 'ui:globalOptions' : {
4981+ enableOptionalDataFieldForType : [ 'object' , 'array' ] ,
4982+ } ,
4983+ } ;
4984+ const experimental_defaultFormStateBehavior = {
4985+ // Set the emptyObjectFields to only populate required defaults to highlight the code working
4986+ emptyObjectFields : 'populateRequiredDefaults' ,
4987+ } ;
4988+ const arrayId = 'root_nestedArrayOptional' ;
4989+ const objectId = 'root_nestedObjectOptional' ;
4990+ const arrayControlAddId = optionalControlsId ( arrayId , 'Add' ) ;
4991+ const arrayControlRemoveId = optionalControlsId ( arrayId , 'Remove' ) ;
4992+ const arrayControlMsgId = optionalControlsId ( arrayId , 'Msg' ) ;
4993+ const arrayAddId = buttonId ( arrayId , 'add' ) ;
4994+ const objectControlAddId = optionalControlsId ( objectId , 'Add' ) ;
4995+ const objectControlRemoveId = optionalControlsId ( objectId , 'Remove' ) ;
4996+ const objectControlMsgId = optionalControlsId ( objectId , 'Msg' ) ;
4997+ it ( 'does not render any optional data control messages when not turned on and readonly and disabled' , ( ) => {
4998+ const props = {
4999+ schema,
5000+ experimental_defaultFormStateBehavior,
5001+ readonly : true ,
5002+ disabled : true ,
5003+ } ;
5004+ const { node } = createFormComponent ( props ) ;
5005+ const addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5006+ const removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5007+ const msgArrayControlNode = node . querySelector ( `#${ arrayControlMsgId } ` ) ;
5008+ const addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5009+ const addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5010+ const removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5011+ const msgObjectControlNode = node . querySelector ( `#${ objectControlMsgId } ` ) ;
5012+ const testInput = node . querySelector ( `#${ objectId } _test` ) ;
5013+ // Check that the expected html elements are rendered (or not) as expected
5014+ expect ( addArrayControlNode ) . eql ( null ) ;
5015+ expect ( removeArrayControlNode ) . eql ( null ) ;
5016+ expect ( msgArrayControlNode ) . eql ( null ) ;
5017+ expect ( addArrayBtn ) . not . eql ( null ) ;
5018+ expect ( addObjectControlNode ) . eql ( null ) ;
5019+ expect ( removeObjectControlNode ) . eql ( null ) ;
5020+ expect ( msgObjectControlNode ) . eql ( null ) ;
5021+ expect ( testInput ) . not . eql ( null ) ;
5022+ } ) ;
5023+ it ( 'renders optional data control messages when turned on and readonly' , ( ) => {
5024+ const props = {
5025+ schema,
5026+ uiSchema : bothOnUiSchema ,
5027+ experimental_defaultFormStateBehavior,
5028+ readonly : true ,
5029+ } ;
5030+ const { node } = createFormComponent ( props ) ;
5031+ const addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5032+ const removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5033+ const msgArrayControlNode = node . querySelector ( `#${ arrayControlMsgId } ` ) ;
5034+ const addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5035+ const addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5036+ const removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5037+ const msgObjectControlNode = node . querySelector ( `#${ objectControlMsgId } ` ) ;
5038+ const testInput = node . querySelector ( `#${ objectId } _test` ) ;
5039+ // Check that the expected html elements are rendered (or not) as expected
5040+ expect ( addArrayControlNode ) . eql ( null ) ;
5041+ expect ( removeArrayControlNode ) . eql ( null ) ;
5042+ expect ( msgArrayControlNode ) . not . eql ( null ) ;
5043+ expect ( addArrayBtn ) . eql ( null ) ;
5044+ expect ( addObjectControlNode ) . eql ( null ) ;
5045+ expect ( removeObjectControlNode ) . eql ( null ) ;
5046+ expect ( msgObjectControlNode ) . not . eql ( null ) ;
5047+ expect ( testInput ) . eql ( null ) ;
5048+ } ) ;
5049+ it ( 'renders optional data control messages when turned on and readonly' , ( ) => {
5050+ const props = {
5051+ schema,
5052+ uiSchema : bothOnUiSchema ,
5053+ experimental_defaultFormStateBehavior,
5054+ disabled : true ,
5055+ } ;
5056+ const { node } = createFormComponent ( props ) ;
5057+ const addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5058+ const removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5059+ const msgArrayControlNode = node . querySelector ( `#${ arrayControlMsgId } ` ) ;
5060+ const addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5061+ const addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5062+ const removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5063+ const msgObjectControlNode = node . querySelector ( `#${ objectControlMsgId } ` ) ;
5064+ const testInput = node . querySelector ( `#${ objectId } _test` ) ;
5065+ // Check that the expected html elements are rendered (or not) as expected
5066+ expect ( addArrayControlNode ) . eql ( null ) ;
5067+ expect ( removeArrayControlNode ) . eql ( null ) ;
5068+ expect ( msgArrayControlNode ) . not . eql ( null ) ;
5069+ expect ( addArrayBtn ) . eql ( null ) ;
5070+ expect ( addObjectControlNode ) . eql ( null ) ;
5071+ expect ( removeObjectControlNode ) . eql ( null ) ;
5072+ expect ( msgObjectControlNode ) . not . eql ( null ) ;
5073+ expect ( testInput ) . eql ( null ) ;
5074+ } ) ;
5075+ it ( 'does not render any optional data controls when not turned on' , ( ) => {
5076+ const props = {
5077+ schema,
5078+ experimental_defaultFormStateBehavior,
5079+ } ;
5080+ const { node } = createFormComponent ( props ) ;
5081+ const addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5082+ const removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5083+ const addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5084+ const addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5085+ const removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5086+ const testInput = node . querySelector ( `#${ objectId } _test` ) ;
5087+ // Check that the expected html elements are rendered (or not) as expected
5088+ expect ( addArrayControlNode ) . eql ( null ) ;
5089+ expect ( removeArrayControlNode ) . eql ( null ) ;
5090+ expect ( addArrayBtn ) . not . eql ( null ) ;
5091+ expect ( addObjectControlNode ) . eql ( null ) ;
5092+ expect ( removeObjectControlNode ) . eql ( null ) ;
5093+ expect ( testInput ) . not . eql ( null ) ;
5094+ } ) ;
5095+ it ( 'only render object optional data controls when only object is turned on' , ( ) => {
5096+ const props = {
5097+ schema,
5098+ uiSchema : objectOnUiSchema ,
5099+ experimental_defaultFormStateBehavior,
5100+ } ;
5101+ const { node } = createFormComponent ( props ) ;
5102+ const addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5103+ const removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5104+ const addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5105+ let addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5106+ let removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5107+ let testInput = node . querySelector ( `#${ objectId } _test` ) ;
5108+ // Check that the expected html elements are rendered (or not) as expected
5109+ expect ( addArrayControlNode ) . eql ( null ) ;
5110+ expect ( removeArrayControlNode ) . eql ( null ) ;
5111+ expect ( addArrayBtn ) . not . eql ( null ) ;
5112+ expect ( addObjectControlNode ) . not . eql ( null ) ;
5113+ expect ( removeObjectControlNode ) . eql ( null ) ;
5114+ expect ( testInput ) . eql ( null ) ;
5115+
5116+ // now click on the add optional data button
5117+ act ( ( ) => addObjectControlNode . click ( ) ) ;
5118+ // now check to see if the UI adjusted
5119+ addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5120+ removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5121+ testInput = node . querySelector ( `#${ objectId } _test` ) ;
5122+ expect ( addObjectControlNode ) . eql ( null ) ;
5123+ expect ( removeObjectControlNode ) . not . eql ( null ) ;
5124+ expect ( testInput ) . not . eql ( null ) ;
5125+
5126+ // now click on the remove optional data button
5127+ act ( ( ) => removeObjectControlNode . click ( ) ) ;
5128+ // now check to see if the UI adjusted
5129+ addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5130+ removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5131+ testInput = node . querySelector ( `#${ objectId } _test` ) ;
5132+ expect ( addObjectControlNode ) . not . eql ( null ) ;
5133+ expect ( removeObjectControlNode ) . eql ( null ) ;
5134+ expect ( testInput ) . eql ( null ) ;
5135+ } ) ;
5136+ it ( 'only render array optional data controls when only array is turned on' , ( ) => {
5137+ const props = {
5138+ schema,
5139+ uiSchema : arrayOnUiSchema ,
5140+ experimental_defaultFormStateBehavior,
5141+ } ;
5142+ const { node } = createFormComponent ( props ) ;
5143+ let addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5144+ let removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5145+ let addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5146+ const addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5147+ const removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5148+ const testInput = node . querySelector ( `#${ objectId } _test` ) ;
5149+ // Check that the expected html elements are rendered (or not) as expected
5150+ expect ( addArrayControlNode ) . not . eql ( null ) ;
5151+ expect ( removeArrayControlNode ) . eql ( null ) ;
5152+ expect ( addArrayBtn ) . eql ( null ) ;
5153+ expect ( addObjectControlNode ) . eql ( null ) ;
5154+ expect ( removeObjectControlNode ) . eql ( null ) ;
5155+ expect ( testInput ) . not . eql ( null ) ;
5156+
5157+ // now click on the add optional data button
5158+ act ( ( ) => addArrayControlNode . click ( ) ) ;
5159+ // now check to see if the UI adjusted
5160+ addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5161+ removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5162+ addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5163+ expect ( addArrayControlNode ) . eql ( null ) ;
5164+ expect ( removeArrayControlNode ) . not . eql ( null ) ;
5165+ expect ( addArrayBtn ) . not . eql ( null ) ;
5166+
5167+ // now click on the remove optional data button
5168+ act ( ( ) => removeArrayControlNode . click ( ) ) ;
5169+ // now check to see if the UI adjusted
5170+ addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5171+ removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5172+ addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5173+ expect ( addArrayControlNode ) . not . eql ( null ) ;
5174+ expect ( removeArrayControlNode ) . eql ( null ) ;
5175+ expect ( addArrayBtn ) . eql ( null ) ;
5176+ } ) ;
5177+ it ( 'render both kinds of optional data controls when only both are turned on' , ( ) => {
5178+ const props = {
5179+ schema,
5180+ uiSchema : bothOnUiSchema ,
5181+ experimental_defaultFormStateBehavior,
5182+ } ;
5183+ const { node } = createFormComponent ( props ) ;
5184+ let addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5185+ let removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5186+ let addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5187+ let addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5188+ let removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5189+ let testInput = node . querySelector ( `#${ objectId } _test` ) ;
5190+ // Check that the expected html elements are rendered (or not) as expected
5191+ expect ( addArrayControlNode ) . not . eql ( null ) ;
5192+ expect ( removeArrayControlNode ) . eql ( null ) ;
5193+ expect ( addArrayBtn ) . eql ( null ) ;
5194+ expect ( addObjectControlNode ) . not . eql ( null ) ;
5195+ expect ( removeObjectControlNode ) . eql ( null ) ;
5196+ expect ( testInput ) . eql ( null ) ;
5197+
5198+ // now click on the add optional data button
5199+ act ( ( ) => addArrayControlNode . click ( ) ) ;
5200+ act ( ( ) => addObjectControlNode . click ( ) ) ;
5201+ // now check to see if the UI adjusted
5202+ addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5203+ removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5204+ addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5205+ addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5206+ removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5207+ testInput = node . querySelector ( `#${ objectId } _test` ) ;
5208+ expect ( addArrayControlNode ) . eql ( null ) ;
5209+ expect ( removeArrayControlNode ) . not . eql ( null ) ;
5210+ expect ( addArrayBtn ) . not . eql ( null ) ;
5211+ expect ( addObjectControlNode ) . eql ( null ) ;
5212+ expect ( removeObjectControlNode ) . not . eql ( null ) ;
5213+ expect ( testInput ) . not . eql ( null ) ;
5214+
5215+ // now click on the remove optional data button
5216+ act ( ( ) => removeArrayControlNode . click ( ) ) ;
5217+ act ( ( ) => removeObjectControlNode . click ( ) ) ;
5218+ // now check to see if the UI adjusted
5219+ addArrayControlNode = node . querySelector ( `#${ arrayControlAddId } ` ) ;
5220+ removeArrayControlNode = node . querySelector ( `#${ arrayControlRemoveId } ` ) ;
5221+ addArrayBtn = node . querySelector ( `#${ arrayAddId } ` ) ;
5222+ addObjectControlNode = node . querySelector ( `#${ objectControlAddId } ` ) ;
5223+ removeObjectControlNode = node . querySelector ( `#${ objectControlRemoveId } ` ) ;
5224+ testInput = node . querySelector ( `#${ objectId } _test` ) ;
5225+ expect ( addArrayControlNode ) . not . eql ( null ) ;
5226+ expect ( removeArrayControlNode ) . eql ( null ) ;
5227+ expect ( addArrayBtn ) . eql ( null ) ;
5228+ expect ( addObjectControlNode ) . not . eql ( null ) ;
5229+ expect ( removeObjectControlNode ) . eql ( null ) ;
5230+ expect ( testInput ) . eql ( null ) ;
5231+ } ) ;
5232+ } ) ;
49485233} ) ;
0 commit comments