diff --git a/src/Forms/Schema/FormSchema.php b/src/Forms/Schema/FormSchema.php index 4f23cf3c4..a6bd1c2a5 100644 --- a/src/Forms/Schema/FormSchema.php +++ b/src/Forms/Schema/FormSchema.php @@ -9,6 +9,7 @@ use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\Form; use SilverStripe\Forms\FormField; use SilverStripe\ORM\ValidationResult; +use LogicException; /** * Represents a {@link Form} as structured data which allows a frontend library to render it. @@ -112,9 +113,29 @@ class FormSchema $schema['fields'][] = $field->getSchemaData(); } + // Validate there are react components for all fields + // Note 'actions' (FormActions) are always valid because FormAction.schemaComponent has a default value + $this->recursivelyValidateSchemaData($schema['fields']); + return $schema; } + private function recursivelyValidateSchemaData(array $schemaData) + { + foreach ($schemaData as $data) { + if (!$data['schemaType'] && !$data['component']) { + $name = $data['name']; + $message = "Could not find a react component for field \"$name\"." + . "Replace or remove the field instance from the field list," + . ' or update the field class and set the schemaDataType or schemaComponent property.'; + throw new LogicException($message); + } + if (array_key_exists('children', $data)) { + $this->recursivelyValidateSchemaData($data['children']); + } + } + } + /** * Gets the current state of this form as a nested array. * diff --git a/tests/php/Forms/FormSchemaTest.php b/tests/php/Forms/FormSchemaTest.php index a23e09bb1..c032f3a53 100644 --- a/tests/php/Forms/FormSchemaTest.php +++ b/tests/php/Forms/FormSchemaTest.php @@ -14,6 +14,8 @@ use SilverStripe\Forms\TextField; use SilverStripe\Forms\RequiredFields; use SilverStripe\Forms\FormAction; use SilverStripe\Forms\PopoverField; +use SilverStripe\Forms\FormField; +use LogicException; class FormSchemaTest extends SapphireTest { @@ -39,6 +41,49 @@ class FormSchemaTest extends SapphireTest $this->assertEquals($expected, $schema); } + /** + * @dataProvider provideGetSchemaException + */ + public function testGetSchemaException(string $field, bool $expectException): void + { + $fields = []; + if ($field === '') { + $fields[] = (new FormField('TestField'))->setSchemaComponent('MyPretendComponent'); + } elseif ($field === '') { + $fields[] = new class('TestField') extends FormField { + protected $schemaDataType = FormField::SCHEMA_DATA_TYPE_CUSTOM; + }; + } elseif ($field === '') { + $fields[] = new FormField('TestField'); + } + $form = new Form(null, 'TestForm', new FieldList($fields)); + $formSchema = new FormSchema($form); + if ($expectException) { + $this->expectException(LogicException::class); + } else { + $this->expectNotToPerformAssertions(); + } + $formSchema->getSchema($form); + } + + public function provideGetSchemaException(): array + { + return [ + [ + 'field' => '', + 'expectException' => false, + ], + [ + 'field' => '', + 'expectException' => false, + ], + [ + 'field' => '', + 'expectException' => true, + ], + ]; + } + public function testGetState() { $form = new Form(null, 'TestForm', new FieldList(), new FieldList());