Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[4.x] Add support for looping over blueprint sections in frontend forms #7778

Merged
merged 10 commits into from
Apr 18, 2023
26 changes: 24 additions & 2 deletions src/Forms/Tags.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public function create()

$jsDriver = $this->parseJsParamDriverAndOptions($this->params->get('js'), $form);

$data['sections'] = $this->getSections($this->sessionHandle(), $jsDriver);
$data['fields'] = $this->getFields($this->sessionHandle(), $jsDriver);
$data['honeypot'] = $form->honeypot();

Expand Down Expand Up @@ -209,16 +210,37 @@ protected function getForm()
return $handle;
}

/**
* Get sections of fields, using sections defined in blueprint.
*
* @param string $sessionHandle
* @param JsDriver $jsDriver
* @return array
*/
protected function getSections($sessionHandle, $jsDriver)
{
return $this->form()->blueprint()->tabs()->first()->sections()
->map(function ($section) use ($sessionHandle, $jsDriver) {
return [
'display' => $section->display(),
'instructions' => $section->instructions(),
'fields' => $this->getFields($sessionHandle, $jsDriver, $section->fields()->all()),
];
})
->all();
}

/**
* Get fields with extra data for looping over and rendering.
*
* @param string $sessionHandle
* @param JsDriver $jsDriver
* @param array|null $fields
* @return array
*/
protected function getFields($sessionHandle, $jsDriver)
protected function getFields($sessionHandle, $jsDriver, $fields = null)
{
return $this->form()->fields()
return collect($fields ?? $this->form()->fields())
->map(function ($field) use ($sessionHandle, $jsDriver) {
return $this->getRenderableField($field, $sessionHandle, function ($data, $field) use ($jsDriver) {
return $jsDriver
Expand Down
57 changes: 56 additions & 1 deletion tests/Tags/Form/FormCreateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public function it_renders_form_with_redirects_to_anchor()
}

/** @test */
public function it_renders_form_dynamically_with_fields_array()
public function it_dynamically_renders_fields_array()
{
$output = $this->normalizeHtml($this->tag(<<<'EOT'
{{ form:contact }}
Expand Down Expand Up @@ -447,6 +447,61 @@ public function it_dynamically_renders_field_with_fallback_to_default_partial()
]);
}

/** @test */
public function it_dynamically_renders_sections_array()
{
$this->createForm([
'tabs' => [
'main' => [
'sections' => [
[
'display' => 'One',
'instructions' => 'One Instructions',
'fields' => [
['handle' => 'alpha', 'field' => ['type' => 'text']],
['handle' => 'bravo', 'field' => ['type' => 'text']],
],
],
[
'display' => 'Two',
'instructions' => 'Two Instructions',
'fields' => [
['handle' => 'charlie', 'field' => ['type' => 'text']],
['handle' => 'delta', 'field' => ['type' => 'text']],
],
],
[
'display' => null,
'instructions' => null,
'fields' => [
['handle' => 'echo', 'field' => ['type' => 'text']],
['handle' => 'fox', 'field' => ['type' => 'text']],
],
],
],
],
],
], 'survey');

$output = $this->normalizeHtml($this->tag(<<<'EOT'
{{ form:survey }}
{{ sections }}
<div class="section">{{ if display}}{{ display }} - {{ /if }}{{ if instructions }}{{ instructions }} - {{ /if }}{{ fields | pluck('handle') | join(',') }}</div>
{{ /sections }}
<div class="fields">{{ fields | pluck('handle') | join(',') }}</div>
{{ /form:survey }}
EOT
));

$this->assertStringContainsString('<div class="section">One - One Instructions - alpha,bravo</div>', $output);
$this->assertStringContainsString('<div class="section">Two - Two Instructions - charlie,delta</div>', $output);
$this->assertStringContainsString('<div class="section">echo,fox</div>', $output);

// Even though the fields are all nested within sections,
// we should still be able to get them via `{{ fields }}` array at top level...
$this->assertStringContainsString('<div class="fields">alpha,bravo,charlie,delta,echo,fox</div>', $output);
}

/** @test */
public function it_wont_submit_form_and_renders_errors()
{
Expand Down
18 changes: 10 additions & 8 deletions tests/Tags/Form/FormTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public function setUp(): void
{
parent::setUp();

$this->createContactForm();
$this->createForm();
$this->clearSubmissions();
}

Expand All @@ -72,13 +72,15 @@ protected function tag($tag)
return Parse::template($tag, []);
}

protected function createContactForm($fields = null)
protected function createForm($blueprintContents = null, $handle = null)
{
$blueprint = Blueprint::make()->setContents([
'fields' => $fields ?? $this->defaultFields,
]);
$defaultBlueprintContents = [
'fields' => $this->defaultFields,
];

$handle = $fields ? $this->customFieldBlueprintHandle : 'contact';
$blueprint = Blueprint::make()->setContents($blueprintContents ?? $defaultBlueprintContents);

$handle = $handle ?? 'contact';

Blueprint::shouldReceive('find')->with("forms.{$handle}")->andReturn($blueprint);
Blueprint::makePartial();
Expand All @@ -93,13 +95,13 @@ protected function assertFieldRendersHtml($expectedHtmlParts, $fieldConfig, $old
{
$randomString = str_shuffle('nobodymesseswiththehoff');

$this->customFieldBlueprintHandle = $handle = $fieldConfig['handle'].'_'.$randomString;
$handle = $fieldConfig['handle'].'_'.$randomString;

$fields = $oldData
? array_merge([['handle' => 'failing_field', 'field' => ['type' => 'text', 'validate' => 'required']]], [$fieldConfig])
: [$fieldConfig];

$this->createContactForm($fields);
$this->createForm(['fields' => $fields], $handle);

if ($oldData) {
$this->post('/!/forms/'.$handle, $oldData)
Expand Down