Skip to content

Commit 82344a6

Browse files
- Finished up all of the testing
1 parent 0cebff7 commit 82344a6

File tree

7 files changed

+341
-7
lines changed

7 files changed

+341
-7
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ should change the heading of the (upcoming) version to include a major version b
9898
- Updated `retrieveSchema()` to add an additional property `resolveAnyOfOrOneOfRefs?: boolean` which causes `resolveAllSchemas()` to resolve `$ref`s inside of the options of `anyOf`/`oneOf` schemas
9999
- Updated `getDefaultFormState` to fix an issue where optional array props had their default set to an empty array when they shouldn't be
100100
- Updated the `TranslatableString` enum to add three new strings in support of the new feature: `OptionalObjectAdd`, `OptionalObjectRemove` and `OptionalObjectEmptyMsg`
101-
- Added three new utility functions: `isFormDataAvailable()`, `isRootSchema()` and `shouldRenderOptionalField()`
101+
- Added four new utility functions: `isFormDataAvailable()`, `isRootSchema()`, `optionalControlsId()`, and `shouldRenderOptionalField()`
102102

103103
## Dev / docs / playground
104104

packages/core/src/components/templates/ButtonTemplates/AddButton.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import IconButton from './IconButton';
55
/** The `AddButton` renders a button that represent the `Add` action on a form
66
*/
77
export default function AddButton<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>({
8+
id,
89
className,
910
onClick,
1011
disabled,
@@ -15,6 +16,7 @@ export default function AddButton<T = any, S extends StrictRJSFSchema = RJSFSche
1516
<div className='row'>
1617
<p className={`col-xs-3 col-xs-offset-9 text-right ${className}`}>
1718
<IconButton
19+
id={id}
1820
iconType='info'
1921
icon='plus'
2022
className='btn-add col-xs-12'

packages/core/src/components/templates/OptionalDataControlsTemplate.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,5 @@ export default function OptionalDataControlsTemplate<
3939
/>
4040
);
4141
}
42-
return <em>{label}</em>;
42+
return <em id={id}>{label}</em>;
4343
}

packages/core/test/Form.test.jsx

Lines changed: 286 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { fireEvent, act, render, waitFor } from '@testing-library/react';
55
import { Simulate } from 'react-dom/test-utils';
66
import { findDOMNode } from 'react-dom';
77
import { Portal } from 'react-portal';
8-
import { getTemplate, getUiOptions } from '@rjsf/utils';
8+
import { getTemplate, getUiOptions, optionalControlsId, buttonId } from '@rjsf/utils';
99
import validator, { customizeValidator } from '@rjsf/validator-ajv8';
1010

1111
import 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

Comments
 (0)