From f406b14f87bee5af778bd5201a41ee32237732f0 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 29 Dec 2015 20:40:52 +0000 Subject: [PATCH 001/217] Work on the new Form models --- spec/FormModel/ButtonCollectionSpec.php | 47 ++++++++++ spec/FormModel/FieldCollectionSpec.php | 45 +++++++++ spec/FormModel/FormSpec.php | 84 +++++++++++++++++ src/FieldBuilder.php | 102 ++++++++++---------- src/FormModel/Button.php | 36 +++++++ src/FormModel/ButtonCollection.php | 114 +++++++++++++++++++++++ src/FormModel/Field.php | 111 ++++++++++++++++++++++ src/FormModel/FieldCollection.php | 54 +++++++++++ src/FormModel/Form.php | 119 ++++++++++++++++++++++++ src/FormModel/HasAttributes.php | 32 +++++++ src/FormModel/Link.php | 51 ++++++++++ src/Link.php | 15 +++ themes/bootstrap/form.blade.php | 3 + 13 files changed, 762 insertions(+), 51 deletions(-) create mode 100644 spec/FormModel/ButtonCollectionSpec.php create mode 100644 spec/FormModel/FieldCollectionSpec.php create mode 100644 spec/FormModel/FormSpec.php create mode 100644 src/FormModel/Button.php create mode 100644 src/FormModel/ButtonCollection.php create mode 100644 src/FormModel/Field.php create mode 100644 src/FormModel/FieldCollection.php create mode 100644 src/FormModel/Form.php create mode 100644 src/FormModel/HasAttributes.php create mode 100644 src/FormModel/Link.php create mode 100644 src/Link.php create mode 100644 themes/bootstrap/form.blade.php diff --git a/spec/FormModel/ButtonCollectionSpec.php b/spec/FormModel/ButtonCollectionSpec.php new file mode 100644 index 0000000..362895d --- /dev/null +++ b/spec/FormModel/ButtonCollectionSpec.php @@ -0,0 +1,47 @@ +beConstructedWith($formBuilder, $htmlBuilder); + } + + function it_is_initializable() + { + $this->shouldHaveType('Styde\Html\FormModel\ButtonCollection'); + } + + function it_renders_buttons($formBuilder, $htmlBuilder) + { + $formBuilder->button('Submit', ['type' => 'submit', 'class' => 'btn-primary']) + ->shouldBeCalled() + ->willReturn(''); + + $formBuilder->button('Reset', ['type' => 'reset']) + ->shouldBeCalled() + ->willReturn(''); + + $formBuilder->button('Button', ['type' => 'button']) + ->shouldBeCalled() + ->willReturn('' + .'' + .'' + .'Link' + ); } } diff --git a/spec/HtmlBuilderSpec.php b/spec/HtmlBuilderSpec.php deleted file mode 100644 index 4e94df6..0000000 --- a/spec/HtmlBuilderSpec.php +++ /dev/null @@ -1,42 +0,0 @@ -beConstructedWith($url, $view); - } - - function it_is_initializable() - { - $this->shouldHaveType('Styde\Html\HtmlBuilder'); - } - - public function it_generates_html_tags() - { - $this->tag('span', 'This is a span', ['id' => 'my-span']) - ->render()->shouldReturn('This is a span'); - } - - public function it_generates_html_tags_with_dynamic_methods() - { - $this->span('This is a span')->id('my-span') - ->render()->shouldReturn('This is a span'); - } - - function it_generate_the_html_class_attribute() - { - $this->classes([ - 'home' => true, - 'main', - 'dont-use-this' => false - ])->shouldReturn(' class="home main"'); - } -} diff --git a/src/FormBuilder.php b/src/FormBuilder.php index da9a667..91c5f79 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -3,9 +3,8 @@ namespace Styde\Html; use Illuminate\Contracts\Routing\UrlGenerator; -use Collective\Html\FormBuilder as CollectiveFormBuilder; -class FormBuilder extends CollectiveFormBuilder +class FormBuilder { /** * Whether to deactivate or not the HTML5 validation (in order to test @@ -14,6 +13,14 @@ class FormBuilder extends CollectiveFormBuilder * @var bool $novalidate */ protected $novalidate = false; + + /** + * The CSRF token used by the form builder. + * + * @var string + */ + protected $csrfToken; + /** * The Theme object in charge of rendering the right view for this theme * @@ -25,34 +32,28 @@ class FormBuilder extends CollectiveFormBuilder * Creates a new Form Builder class. This extends from the Collective * Form Builder but adds a couple of extra functions. * - * @param \Styde\Html\HtmlBuilder $html * @param \Illuminate\Contracts\Routing\UrlGenerator $url * @param string $csrfToken * @param \Styde\Html\Theme $theme */ - public function __construct(HtmlBuilder $html, UrlGenerator $url, $csrfToken, Theme $theme) + public function __construct(UrlGenerator $url, $csrfToken, Theme $theme) { - parent::__construct($html, $url, $theme->getView(), $csrfToken); - $this->theme = $theme; + $this->csrfToken = $csrfToken; $this->view = $theme->getView(); } /** * Allows user to set the novalidate option for every form generated with - * the form open method, so developers can skin HTML5 validation, in order + * the form open method, so developers can skip HTML5 validation, in order * to test backend validation in a local or development environment. * - * @param null $value - * @return bool|null + * @param boolean $value + * @return null */ - public function novalidate($value = null) + public function novalidate($value = true) { - if ($value !== null) { - $this->novalidate = $value; - } - - return $this->novalidate; + $this->novalidate = $value; } /** @@ -60,17 +61,17 @@ public function novalidate($value = null) * This methods relies on the original Form::open method of the Laravel * Collective component. * - * @param array $options + * @param array $attributes * * @return string */ - public function open(array $options = array()) + public function open(array $attributes = array()) { - if ($this->novalidate()) { - $options[] = 'novalidate'; + if ($this->novalidate) { + $attributes['novalidate'] = true; } - return parent::open($options); + return (new HtmlElement('form', '', $attributes))->open(); } /** @@ -81,6 +82,66 @@ public function getModel() return $this->model; } + + /** + * Create a text input field. + * + * @param string $name + * @param string $value + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function text(string $name, $value = null, $attributes = []) + { + return $this->input('text', $name, $value, $attributes); + } + + /** + * Create a form input field. + * + * @param string $type + * @param string $name + * @param string $value + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function input($type, $name, $value = null, $attributes = []) + { + return new HtmlElement('input', false, array_merge(compact('type', 'name', 'value'), $attributes)); + } + + /** + * Create a textarea input field. + * + * @param string $type + * @param string $name + * @param string $value + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function textarea($type, $name, $value = null, $attributes = []) + { + return new HtmlElement('input', false, array_merge(compact('type', 'name', 'value'), $attributes)); + } + + /** + * Create a select box field. + * + * @param string $name + * @param array $list + * @param string $selected + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function select($name, $list = [], $selected = null, array $attributes = []) + { + return new HtmlElement('select', '', array_merge(compact('name'), $attributes)); + } + /** * Create a time input field. * @@ -88,7 +149,7 @@ public function getModel() * @param string $value * @param array $options * - * @return string + * @return \Styde\Html\HtmlElement */ public function time($name, $value = null, $options = array()) { @@ -96,15 +157,20 @@ public function time($name, $value = null, $options = array()) } /** - * Create a list of radios. + * Create a button element. * - * This function is very similar to Form::select but it generates a - * collection of radios instead of options. + * @param string $text + * @param array $attributes * - * i.e. Form::radios('status', ['a' => 'Active', 'i' => 'Inactive']) - * - * You can pass 'inline' as a value of the attribute's array, to set the - * radios as inline (they'll be rendered with the 'radios-inline' template). + * @return \Styde\Html\HtmlElement + */ + public function button($text = null, $attributes = []) + { + return new HtmlElement('button', $text, array_merge(['type' => 'button'], $attributes)); + } + + /** + * Create a list of radios. * * @param string $name * @param array $options @@ -149,15 +215,6 @@ public function radios($name, $options = array(), $selected = null, $attributes /** * Create a list of checkboxes. * - * This function is similar to Form::select, but it generates a collection - * of checkboxes instead of options. - * - * i.e. Form::checkboxes('status', ['a' => 'Active', 'i' => 'Inactive']); - * - * You can pass 'inline' as a value of the attribute's array, to set the - * checkboxes as inline (they'll be rendered using the 'checkboxes-inline' - * template). - * * @param string $name * @param array $options * @param string $selected @@ -201,4 +258,9 @@ public function checkboxes($name, $options = array(), $selected = null, $attribu $defaultTemplate ); } + + public function getValueAttribute($name, $value) + { + return $value; + } } diff --git a/src/FormModel/Button.php b/src/FormModel/Button.php deleted file mode 100644 index eb7dd8f..0000000 --- a/src/FormModel/Button.php +++ /dev/null @@ -1,36 +0,0 @@ -formBuilder = $formBuilder; - - $this->type = $type; - $this->text = $text; - $this->attributes = $attributes; - } - - public function render() - { - return $this->formBuilder->button( - $this->text, - array_merge($this->attributes, ['type' => $this->type]) - ); - } - -} \ No newline at end of file diff --git a/src/FormModel/ButtonCollection.php b/src/FormModel/ButtonCollection.php index ca337e6..5dc3eba 100644 --- a/src/FormModel/ButtonCollection.php +++ b/src/FormModel/ButtonCollection.php @@ -4,6 +4,7 @@ use Styde\Html\HtmlBuilder; use Styde\Html\FormBuilder; +use Styde\Html\HtmlElement; class ButtonCollection { @@ -78,7 +79,11 @@ public function reset($text, array $attributes = array()) */ public function add($type, $text, array $attributes = array()) { - return $this->buttons[] = new Button($this->formBuilder, $type, $text, $attributes); + $attributes['type'] = $type; + + $this->buttons[] = $button = $this->formBuilder->button($text, $attributes); + + return $button; } /** @@ -92,7 +97,7 @@ public function add($type, $text, array $attributes = array()) */ public function link($url, $title, array $attributes = array(), $secure = false) { - return $this->buttons[] = new Link($this->htmlBuilder, $url, $title, $attributes, $secure); + return $this->buttons[] = $this->htmlBuilder->link($url, $title, $attributes, $secure); } /** diff --git a/src/HtmlBuilder.php b/src/HtmlBuilder.php index db45fa5..cdd158e 100644 --- a/src/HtmlBuilder.php +++ b/src/HtmlBuilder.php @@ -62,6 +62,23 @@ public function classes(array $classes) return ''; } + /** + * Generate a HTML link. + * + * @param string $url + * @param string $title + * @param array $attributes + * @param bool $secure + * + * @return \Illuminate\Support\HtmlString + */ + public function link($url, $title = null, $attributes = [], $secure = null) + { + $attributes['url'] = $this->url->to($url, [], $secure); + + return new HtmlElement('a', $title ?: $url, $attributes); + } + /** * Generate an html tag. * diff --git a/src/HtmlElement.php b/src/HtmlElement.php index 0256f49..12cd0a1 100644 --- a/src/HtmlElement.php +++ b/src/HtmlElement.php @@ -60,7 +60,23 @@ public function attr($name, $value = true) */ public function render() { - return '<'.$this->tag.$this->renderAttributes().'>'.$this->content.'tag.'>'; + // Render a single tag. + if ($this->content === false) { + return $this->open(); + } + + // Render a paired tag. + return $this->open().$this->renderText($this->content).$this->close(); + } + + public function open() + { + return '<'.$this->tag.$this->renderAttributes().'>'; + } + + public function close() + { + return 'tag.'>'; } /** @@ -72,27 +88,39 @@ public function renderAttributes() foreach ($this->attributes as $name => $value) { - $result .= $this->renderAttribute($name, $value); + if ($attribute = $this->renderAttribute($name, $value)) { + $result .= " $attribute"; + } } return $result; } + /** + * Render an individual attribute. + * + * @param mixed $name + * @param mixed $value + * @return string|null + */ protected function renderAttribute($name, $value) { if (is_numeric($name)) { - return ' '.$value; + return $value; } if ($value === true) { - return ' '.$name; + return $name; } - if ($value === false) { - return ''; + if ($value !== false) { + return $name.'="'.$this->renderText($value).'"'; } + } - return ' '.$name.'="'.e($value).'"'; + public function renderText($value) + { + return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false); } public function __call($method, array $parameters) diff --git a/src/Link.php b/src/Link.php deleted file mode 100644 index f98dcd8..0000000 --- a/src/Link.php +++ /dev/null @@ -1,15 +0,0 @@ -htmlBuilder = new HtmlBuilder( + Mockery::mock(\Illuminate\Contracts\Routing\UrlGenerator::class), + Mockery::mock(\Illuminate\Contracts\View\Factory::class) + ); + } + + /** @test */ + function it_generates_html_tags() + { + $this->assertEquals( + 'This is a span', + $this->htmlBuilder->tag('span', 'This is a span', ['id' => 'my-span']) + ); + } + + /** @test */ + function it_generates_html_tags_with_dynamic_methods() + { + $this->assertEquals( + 'This is a span', + $this->htmlBuilder->span('This is a span')->id('my-span') + ); + } + + /** @test */ + function it_generate_the_html_class_attribute() + { + $html = $this->htmlBuilder->classes([ + 'home' => true, + 'main', + 'dont-use-this' => false, + ]); + + $this->assertEquals(' class="home main"', $html); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..fd769cf --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,13 @@ + Date: Sat, 2 Sep 2017 11:24:35 +0100 Subject: [PATCH 015/217] Progress --- src/FormBuilder.php | 126 ++++++++++++++++++------ src/HtmlElement.php | 85 +++++++++++----- src/HtmlServiceProvider.php | 11 +-- src/Theme.php | 6 +- tests/FormBuilderTest.php | 39 ++++++++ tests/HtmlBuilderTest.php | 36 +++++-- tests/TestCase.php | 46 +++++++++ themes/bootstrap/forms/radios.blade.php | 14 +-- 8 files changed, 280 insertions(+), 83 deletions(-) create mode 100644 tests/FormBuilderTest.php diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 91c5f79..62728d7 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -2,6 +2,7 @@ namespace Styde\Html; +use Illuminate\Contracts\Session\Session; use Illuminate\Contracts\Routing\UrlGenerator; class FormBuilder @@ -36,13 +37,35 @@ class FormBuilder * @param string $csrfToken * @param \Styde\Html\Theme $theme */ - public function __construct(UrlGenerator $url, $csrfToken, Theme $theme) + public function __construct(UrlGenerator $url, Theme $theme, $csrfToken) { $this->theme = $theme; $this->csrfToken = $csrfToken; $this->view = $theme->getView(); } + /** + * Set the session store implementation. + * + * @param \Illuminate\Contracts\Session\Session $session + * + * @return $this + */ + public function setSessionStore(Session $session) + { + $this->session = $session; + + return $this; + } + + /** + * Get the protected model attribute + */ + public function getModel() + { + return $this->model; + } + /** * Allows user to set the novalidate option for every form generated with * the form open method, so developers can skip HTML5 validation, in order @@ -75,13 +98,22 @@ public function open(array $attributes = array()) } /** - * Get the protected model attribute + * Create a form label element. + * + * @param string $name + * @param string $content + * @param array $attributes + * + * @return \Styde\Html\HtmlElement */ - public function getModel() + public function label($name, $content = '', $attributes = []) { - return $this->model; - } + if (empty ($content)) { + $content = ucwords(str_replace('_', ' ', $name)); + } + return new HtmlElement('label', $content, $attributes); + } /** * Create a text input field. @@ -169,47 +201,81 @@ public function button($text = null, $attributes = []) return new HtmlElement('button', $text, array_merge(['type' => 'button'], $attributes)); } + /** + * Create a radio button input field. + * + * @param string $name + * @param mixed $value + * @param bool $checked + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function radio($name, $value = null, $checked = false, $attributes = []) + { + $attributes = array_merge([ + 'type' => 'radio', + 'name' => $name, + 'value' => $value, + 'checked' => $checked, + ], $attributes); + + return new HtmlElement('input', false, $attributes); + } + + /** + * Create a checkbox input field. + * + * @param string $name + * @param mixed $value + * @param bool $checked + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function checkbox($name, $value = 1, $checked = null, $attributes = []) + { + $attributes = array_merge([ + 'type' => 'checkbox', + 'name' => $name, + 'value' => $value, + 'checked' => $checked, + ], $attributes); + + return new HtmlElement('input', false, $attributes); + } + /** * Create a list of radios. * * @param string $name * @param array $options - * @param string $selected + * @param string $checked * @param array $attributes * * @return string */ - public function radios($name, $options = array(), $selected = null, $attributes = array()) + public function radios($name, $options = array(), $checked = null, $attributes = array()) { - $selected = $this->getValueAttribute($name, $selected); + $checked = $this->getValueAttribute($name, $checked); - $defaultTemplate = in_array('inline', $attributes) - ? 'forms.radios-inline' - : 'forms.radios'; + $defaultTemplate = in_array('inline', $attributes) ? 'forms.radios-inline' : 'forms.radios'; - $template = isset($attributes['template']) - ? $attributes['template'] - : null; + $template = $attributes['template'] ?? null; - $radios = []; + unset ($attributes['inline'], $attributes['template']); - foreach ($options as $value => $label) { - $radios[] = [ - 'name' => $name, - 'value' => $value, - 'label' => $label, - 'selected' => $selected == $value, - 'id' => $name.'_'.Str::slug($value) - ]; - } + $labels = []; - unset ($attributes['inline'], $attributes['template']); + foreach ($options as $value => $text) { + $id = $name.'_'.Str::slug($value); - return $this->theme->render( - $template, - compact('name', 'radios', 'attributes'), - $defaultTemplate - ); + $radio = $this->radio($name, $value, $checked == $value, compact('id')); + + $labels[] = new HtmlElement('label', compact('text', 'radio')); + } + + return $this->theme->render($template, compact('name', 'labels'), $defaultTemplate); } /** diff --git a/src/HtmlElement.php b/src/HtmlElement.php index 12cd0a1..2cff374 100644 --- a/src/HtmlElement.php +++ b/src/HtmlElement.php @@ -2,7 +2,11 @@ namespace Styde\Html; -class HtmlElement +use Illuminate\Support\Arr; +use Illuminate\Support\HtmlString; +use Illuminate\Contracts\Support\Htmlable; + +class HtmlElement implements Htmlable { /** * The name of the HTML tag. @@ -29,10 +33,10 @@ class HtmlElement * HtmlElement constructor. * * @param string $tag - * @param string $content + * @param string|array $content * @param array $attributes */ - public function __construct($tag, $content, array $attributes = []) + public function __construct($tag, $content = '', array $attributes = []) { $this->tag = $tag; $this->content = $content; @@ -66,45 +70,41 @@ public function render() } // Render a paired tag. - return $this->open().$this->renderText($this->content).$this->close(); + return new HtmlString($this->renderOpenTag().$this->renderContent().$this->renderCloseTag()); } public function open() { - return '<'.$this->tag.$this->renderAttributes().'>'; + return new HtmlString($this->renderOpenTag()); } public function close() { - return 'tag.'>'; + return new HtmlString($this->renderCloseTag()); + } + + protected function renderOpenTag() + { + return '<'.$this->tag.$this->renderAttributes().'>'; } - /** - * Render the HTML attributes - */ public function renderAttributes() { $result = ''; - foreach ($this->attributes as $name => $value) - { - if ($attribute = $this->renderAttribute($name, $value)) { - $result .= " $attribute"; + foreach ($this->attributes as $name => $value) { + if ($attribute = $this->renderAttribute($name)) { + $result .= " {$attribute}"; } } return $result; } - /** - * Render an individual attribute. - * - * @param mixed $name - * @param mixed $value - * @return string|null - */ - protected function renderAttribute($name, $value) + protected function renderAttribute($name) { + $value = $this->attributes[$name]; + if (is_numeric($name)) { return $value; } @@ -113,23 +113,60 @@ protected function renderAttribute($name, $value) return $name; } - if ($value !== false) { - return $name.'="'.$this->renderText($value).'"'; + if ($value) { + return $name.'="'.$this->escape($value).'"'; } + + return ''; } - public function renderText($value) + public function escape($value) { return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false); } + public function renderContent() + { + $result = ''; + + foreach ((array) $this->content as $content) { + $result .= e($content); + } + + return $result; + } + + protected function renderCloseTag() + { + return 'tag.'>'; + } + public function __call($method, array $parameters) { return $this->attr($method, $parameters[0] ?? true); } + public function __get($name) + { + if (isset ($this->content[$name])) { + return $this->content[$name]; + } + + throw new \InvalidArgumentException("The property $name does not exist in this [{$this->tag}] element"); + } + public function __toString() { return $this->render(); } + + /** + * Get content as a string of HTML. + * + * @return string + */ + public function toHtml() + { + return $this->render(); + } } \ No newline at end of file diff --git a/src/HtmlServiceProvider.php b/src/HtmlServiceProvider.php index 1d6270f..bf21bd1 100644 --- a/src/HtmlServiceProvider.php +++ b/src/HtmlServiceProvider.php @@ -156,16 +156,9 @@ protected function registerFormBuilder() $this->app->singleton('form', function ($app) { $this->loadConfigurationOptions(); - $form = new FormBuilder( - $app['html'], - $app['url'], - $app['session.store']->token(), - $app->make(Theme::class) - ); + $form = new FormBuilder($app['url'], $app->make(Theme::class), $app['session.store']->token()); - $form->novalidate( - $app['config']->get('html.novalidate', false) - ); + $form->novalidate($app['config']->get('html.novalidate', false)); return $form->setSessionStore($app['session.store']); }); diff --git a/src/Theme.php b/src/Theme.php index 3409add..433408b 100644 --- a/src/Theme.php +++ b/src/Theme.php @@ -67,12 +67,12 @@ public function getView() * You can publish and customize the default template (resources/views/themes/) * or be located inside the components directory (vendor/styde/html/themes/). * - * @param string $custom + * @param string|null $custom * @param array $data - * @param null $template + * @param string|null $template * @return string */ - public function render($custom, $data = array(), $template = null) + public function render($custom = null, $data = array(), $template = null) { if ($custom != null) { return $this->view->make($custom, $data)->render(); diff --git a/tests/FormBuilderTest.php b/tests/FormBuilderTest.php new file mode 100644 index 0000000..2951c17 --- /dev/null +++ b/tests/FormBuilderTest.php @@ -0,0 +1,39 @@ +formBuilder = $this->newFormBuilder(); + } + + /** @test */ + function it_adds_the_novalidate_attribute_to_all_forms() + { + $this->formBuilder->novalidate(true); + + $html = $this->formBuilder->open(['method' => 'GET']); + + $this->assertSame('', $html); + } + + /** @test */ + function it_generates_time_inputs() + { + $html = $this->formBuilder->time('time')->render(); + + $this->assertSame('', $html); + } +} \ No newline at end of file diff --git a/tests/HtmlBuilderTest.php b/tests/HtmlBuilderTest.php index 2e27fa3..4d6b19e 100644 --- a/tests/HtmlBuilderTest.php +++ b/tests/HtmlBuilderTest.php @@ -2,22 +2,20 @@ namespace Styde\Html\Tests; -use Mockery; -use Styde\Html\HtmlBuilder; +use Styde\Html\HtmlElement; class HtmlBuilderTest extends TestCase { /** - * @var HtmlBuilder + * @var \Styde\Html\HtmlBuilder */ var $htmlBuilder; function setUp() { - $this->htmlBuilder = new HtmlBuilder( - Mockery::mock(\Illuminate\Contracts\Routing\UrlGenerator::class), - Mockery::mock(\Illuminate\Contracts\View\Factory::class) - ); + parent::setUp(); + + $this->htmlBuilder = $this->newHtmlBuilder(); } /** @test */ @@ -25,7 +23,29 @@ function it_generates_html_tags() { $this->assertEquals( 'This is a span', - $this->htmlBuilder->tag('span', 'This is a span', ['id' => 'my-span']) + $this->htmlBuilder->tag('span', 'This is a span', ['id' => 'my-span'])->render() + ); + + $this->assertEquals( + '', + $this->htmlBuilder->tag('input', false, ['type' => 'text', 'readonly'])->render() + ); + } + + /** @test */ + function it_closes_html_tags() + { + $htmlElement = new HtmlElement('span'); + + $this->assertEquals('', (string) $htmlElement->close()); + } + + /** @test */ + function it_escapes_the_attributes_of_generated_tags() + { + $this->assertEquals( + 'Span', + $this->htmlBuilder->tag('span', 'Span', ['id' => ''])->render() ); } diff --git a/tests/TestCase.php b/tests/TestCase.php index fd769cf..807ada8 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,10 +2,56 @@ namespace Styde\Html\Tests; +use Illuminate\Contracts\Routing\UrlGenerator; +use Illuminate\Contracts\View\Factory; +use Illuminate\Contracts\View\View; use Mockery; +use Styde\Html\FormBuilder; +use Styde\Html\HtmlBuilder; +use Styde\Html\Theme; class TestCase extends \PHPUnit\Framework\TestCase { + /** + * @var \Mockery\MockInterface + */ + protected $viewFactory; + + /** + * @var \Mockery\MockInterface + */ + protected $urlGenerator; + + protected function newHtmlBuilder() + { + return new HtmlBuilder($this->mockUrlGenerator(), $this->mockViewFactory()); + } + + protected function newFormBuilder() + { + return new FormBuilder($this->mockUrlGenerator(), $this->newTheme(), 'csrf_token_value'); + } + + protected function newTheme() + { + return new Theme($this->mockViewFactory(), 'bootstrap'); + } + + protected function mockViewFactory() + { + return $this->viewFactory = Mockery::mock(Factory::class); + } + + protected function mockUrlGenerator() + { + return $this->urlGenerator = Mockery::mock(UrlGenerator::class); + } + + protected function mockView() + { + return Mockery::mock(View::class); + } + protected function tearDown() { Mockery::close(); diff --git a/themes/bootstrap/forms/radios.blade.php b/themes/bootstrap/forms/radios.blade.php index 929f105..b5fe6ac 100644 --- a/themes/bootstrap/forms/radios.blade.php +++ b/themes/bootstrap/forms/radios.blade.php @@ -1,12 +1,8 @@ -@foreach($radios as $radio) +@foreach($labels as $label)
- + {{ $label->open() }} + {{ $label->radio->render() }} + {{ $label->text }} + {{ $label->close() }}
@endforeach \ No newline at end of file From ea2845a851b40f76567396e090ef37da0950bca1 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 30 Jan 2018 22:07:17 +0000 Subject: [PATCH 016/217] Progress --- .../Access/BasicAccessHandlerSpec.php | 0 {spec => __spec}/Alert/ContainerSpec.php | 0 {spec => __spec}/FieldBuilderSpec.php | 69 ++++----- .../FormModel/ButtonCollectionSpec.php | 0 .../FormModel/FieldCollectionSpec.php | 0 {spec => __spec}/FormModel/FormSpec.php | 0 {spec => __spec}/Menu/MenuGeneratorSpec.php | 0 {spec => __spec}/ThemeSpec.php | 0 composer.json | 3 +- config.php | 26 +--- spec/FormBuilderSpec.php | 115 --------------- spec/StrSpec.php | 58 -------- src/Alert/Container.php | 16 +-- src/Facades/Form.php | 18 +++ src/Facades/Html.php | 18 +++ src/FieldBuilder.php | 133 +----------------- src/FormBuilder.php | 100 +++++++++---- src/FormModel/Field.php | 14 +- src/HtmlBuilder.php | 20 +-- src/HtmlElement.php | 10 +- src/HtmlServiceProvider.php | 12 +- src/Menu/Menu.php | 46 +++--- src/Str.php | 6 +- tests/AccessHandlerTest.php | 16 +++ tests/AlertTest.php | 43 ++++++ tests/FieldBuilderTest.php | 131 +++++++++++++++++ tests/FormBuilderTest.php | 34 +++-- tests/MenuGeneratorTest.php | 118 ++++++++++++++++ tests/StrTest.php | 62 ++++++++ tests/TestCase.php | 46 ++---- tests/TestHelpers.php | 45 ++++++ tests/snapshots/bootstrap4/alert/alert.html | 6 + tests/snapshots/bootstrap4/alert/complex.html | 18 +++ tests/snapshots/bootstrap4/alert/magic.html | 12 ++ .../bootstrap4/field/checkboxes.html | 21 +++ tests/snapshots/bootstrap4/field/radios.html | 13 ++ .../bootstrap4/field/select-empty.html | 6 + .../bootstrap4/field/select-group.html | 13 ++ .../bootstrap4/field/select-multiple.html | 6 + tests/snapshots/bootstrap4/field/select.html | 6 + .../bootstrap4/field/text-custom-label.html | 6 + .../bootstrap4/field/text-required.html | 7 + tests/snapshots/bootstrap4/field/text.html | 6 + .../bootstrap4/field/text_with_errors.html | 7 + .../snapshots/bootstrap4/form/checkboxes.html | 16 +++ tests/snapshots/bootstrap4/form/radios.html | 29 ++++ .../bootstrap4/menu/access-handler.html | 12 ++ tests/snapshots/bootstrap4/menu/menu.html | 22 +++ .../snapshots/bootstrap4/menu/parameters.html | 12 ++ tests/snapshots/bootstrap4/menu/routes.html | 12 ++ tests/snapshots/bootstrap4/menu/submenu.html | 26 ++++ themes/bootstrap/fields/checkbox.blade.php | 18 --- themes/bootstrap/fields/collections.blade.php | 13 -- themes/bootstrap/fields/default.blade.php | 16 --- themes/bootstrap/forms/checkboxes.blade.php | 13 -- themes/bootstrap/forms/radios.blade.php | 8 -- themes/bootstrap/menu.blade.php | 23 --- .../{bootstrap => bootstrap4}/alert.blade.php | 24 ++-- themes/bootstrap4/fields/checkbox.blade.php | 12 ++ .../bootstrap4/fields/collections.blade.php | 12 ++ themes/bootstrap4/fields/default.blade.php | 12 ++ .../{bootstrap => bootstrap4}/form.blade.php | 0 .../forms/checkboxes-inline.blade.php | 0 themes/bootstrap4/forms/checkboxes.blade.php | 6 + .../forms/radios-inline.blade.php | 0 themes/bootstrap4/forms/radios.blade.php | 6 + themes/bootstrap4/menu.blade.php | 22 +++ 67 files changed, 999 insertions(+), 601 deletions(-) rename {spec => __spec}/Access/BasicAccessHandlerSpec.php (100%) rename {spec => __spec}/Alert/ContainerSpec.php (100%) rename {spec => __spec}/FieldBuilderSpec.php (86%) rename {spec => __spec}/FormModel/ButtonCollectionSpec.php (100%) rename {spec => __spec}/FormModel/FieldCollectionSpec.php (100%) rename {spec => __spec}/FormModel/FormSpec.php (100%) rename {spec => __spec}/Menu/MenuGeneratorSpec.php (100%) rename {spec => __spec}/ThemeSpec.php (100%) delete mode 100644 spec/FormBuilderSpec.php delete mode 100644 spec/StrSpec.php create mode 100644 src/Facades/Form.php create mode 100644 src/Facades/Html.php create mode 100644 tests/AccessHandlerTest.php create mode 100644 tests/AlertTest.php create mode 100644 tests/FieldBuilderTest.php create mode 100644 tests/MenuGeneratorTest.php create mode 100644 tests/StrTest.php create mode 100644 tests/TestHelpers.php create mode 100644 tests/snapshots/bootstrap4/alert/alert.html create mode 100644 tests/snapshots/bootstrap4/alert/complex.html create mode 100644 tests/snapshots/bootstrap4/alert/magic.html create mode 100644 tests/snapshots/bootstrap4/field/checkboxes.html create mode 100644 tests/snapshots/bootstrap4/field/radios.html create mode 100644 tests/snapshots/bootstrap4/field/select-empty.html create mode 100644 tests/snapshots/bootstrap4/field/select-group.html create mode 100644 tests/snapshots/bootstrap4/field/select-multiple.html create mode 100644 tests/snapshots/bootstrap4/field/select.html create mode 100644 tests/snapshots/bootstrap4/field/text-custom-label.html create mode 100644 tests/snapshots/bootstrap4/field/text-required.html create mode 100644 tests/snapshots/bootstrap4/field/text.html create mode 100644 tests/snapshots/bootstrap4/field/text_with_errors.html create mode 100644 tests/snapshots/bootstrap4/form/checkboxes.html create mode 100644 tests/snapshots/bootstrap4/form/radios.html create mode 100644 tests/snapshots/bootstrap4/menu/access-handler.html create mode 100644 tests/snapshots/bootstrap4/menu/menu.html create mode 100644 tests/snapshots/bootstrap4/menu/parameters.html create mode 100644 tests/snapshots/bootstrap4/menu/routes.html create mode 100644 tests/snapshots/bootstrap4/menu/submenu.html delete mode 100644 themes/bootstrap/fields/checkbox.blade.php delete mode 100644 themes/bootstrap/fields/collections.blade.php delete mode 100644 themes/bootstrap/fields/default.blade.php delete mode 100644 themes/bootstrap/forms/checkboxes.blade.php delete mode 100644 themes/bootstrap/forms/radios.blade.php delete mode 100644 themes/bootstrap/menu.blade.php rename themes/{bootstrap => bootstrap4}/alert.blade.php (58%) create mode 100644 themes/bootstrap4/fields/checkbox.blade.php create mode 100644 themes/bootstrap4/fields/collections.blade.php create mode 100644 themes/bootstrap4/fields/default.blade.php rename themes/{bootstrap => bootstrap4}/form.blade.php (100%) rename themes/{bootstrap => bootstrap4}/forms/checkboxes-inline.blade.php (100%) create mode 100644 themes/bootstrap4/forms/checkboxes.blade.php rename themes/{bootstrap => bootstrap4}/forms/radios-inline.blade.php (100%) create mode 100644 themes/bootstrap4/forms/radios.blade.php create mode 100644 themes/bootstrap4/menu.blade.php diff --git a/spec/Access/BasicAccessHandlerSpec.php b/__spec/Access/BasicAccessHandlerSpec.php similarity index 100% rename from spec/Access/BasicAccessHandlerSpec.php rename to __spec/Access/BasicAccessHandlerSpec.php diff --git a/spec/Alert/ContainerSpec.php b/__spec/Alert/ContainerSpec.php similarity index 100% rename from spec/Alert/ContainerSpec.php rename to __spec/Alert/ContainerSpec.php diff --git a/spec/FieldBuilderSpec.php b/__spec/FieldBuilderSpec.php similarity index 86% rename from spec/FieldBuilderSpec.php rename to __spec/FieldBuilderSpec.php index ac04505..3a13a5d 100644 --- a/spec/FieldBuilderSpec.php +++ b/__spec/FieldBuilderSpec.php @@ -23,30 +23,30 @@ function it_is_initializable() $this->shouldHaveType('Styde\Html\FieldBuilder'); } - function it_generates_a_text_field($form, $theme, $lang) - { - // Expect - $form->text("name", "value", ["class" => "", "id" => "name"]) - ->shouldBeCalled() - ->willReturn(''); - - $lang->get('validation.attributes.name') - ->shouldBeCalled() - ->willReturn('validation.attributes.name'); - - $theme->render(null, [ - "htmlName" => "name", - "id" => "name", - "label" => "Name", - "input" => "", - "errors" => [], - "hasErrors" => false, - "required" => false - ], "fields.default")->shouldBeCalled()->willReturn('html'); - - // When - $this->text('name', 'value')->render()->shouldReturn('html'); - } +// function it_generates_a_text_field($form, $theme, $lang) +// { +// // Expect +// $form->text("name", "value", ["class" => "", "id" => "name"]) +// ->shouldBeCalled() +// ->willReturn(''); +// +// $lang->get('validation.attributes.name') +// ->shouldBeCalled() +// ->willReturn('validation.attributes.name'); +// +// $theme->render(null, [ +// "htmlName" => "name", +// "id" => "name", +// "label" => "Name", +// "input" => "", +// "errors" => [], +// "hasErrors" => false, +// "required" => false +// ], "fields.default")->shouldBeCalled()->willReturn('html'); +// +// // When +// $this->text('name', 'value')->render()->shouldReturn('html'); +// } function it_checks_for_access(AccessHandler $access) { @@ -184,27 +184,6 @@ function it_adds_an_empty_option_to_select_fields($form, $lang) $this->select('gender', $options, 'm', ['label' => 'Gender'])->render(); } - function it_generates_a_text_field_with_errors($form, $theme, $lang, Session $session) - { - // Having - $session->get('errors')->willReturn(new MessageBag([ - 'name' => ['This is wrong'] - ])); - - $this->setSessionStore($session); - - // Expect - $form->text("name", "value", ["class" => "error", "id" => "name"])->shouldBeCalled(); - $theme->render( - null, - Argument::withEntry('errors', ['This is wrong']), - "fields.default" - )->shouldBeCalled(); - - // When - $this->text('name', 'value')->render(); - } - function it_generates_a_text_field_with_extra_data($theme) { // Expect diff --git a/spec/FormModel/ButtonCollectionSpec.php b/__spec/FormModel/ButtonCollectionSpec.php similarity index 100% rename from spec/FormModel/ButtonCollectionSpec.php rename to __spec/FormModel/ButtonCollectionSpec.php diff --git a/spec/FormModel/FieldCollectionSpec.php b/__spec/FormModel/FieldCollectionSpec.php similarity index 100% rename from spec/FormModel/FieldCollectionSpec.php rename to __spec/FormModel/FieldCollectionSpec.php diff --git a/spec/FormModel/FormSpec.php b/__spec/FormModel/FormSpec.php similarity index 100% rename from spec/FormModel/FormSpec.php rename to __spec/FormModel/FormSpec.php diff --git a/spec/Menu/MenuGeneratorSpec.php b/__spec/Menu/MenuGeneratorSpec.php similarity index 100% rename from spec/Menu/MenuGeneratorSpec.php rename to __spec/Menu/MenuGeneratorSpec.php diff --git a/spec/ThemeSpec.php b/__spec/ThemeSpec.php similarity index 100% rename from spec/ThemeSpec.php rename to __spec/ThemeSpec.php diff --git a/composer.json b/composer.json index 9b478fe..871b524 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ "require-dev": { "phpspec/phpspec": "~2.1", "phpunit/phpunit": "^6.2", - "mockery/mockery": "^0.9.9" + "mockery/mockery": "^0.9.9", + "orchestra/testbench": "~3.0" }, "autoload": { "psr-4": { diff --git a/config.php b/config.php index cec83f5..1afbe08 100644 --- a/config.php +++ b/config.php @@ -6,7 +6,7 @@ * Set the HTML theme for the components * like alerts, form fields, menus, etc. */ - 'theme' => 'bootstrap', + 'theme' => 'bootstrap4', /* * Set the folder to store the custom templates @@ -37,23 +37,14 @@ */ 'novalidate' => false, - /* - * Specify abbreviations for the form field attributes - */ - 'abbreviations' => [ - 'ph' => 'placeholder', - 'max' => 'maxlength', - 'tpl' => 'template' - ], - /* * Set the configuration for each theme */ 'themes' => [ /** - * Default configuration for the Twitter Bootstrap framework + * Default configuration for Bootstrap v4 */ - 'bootstrap' => [ + 'bootstrap4' => [ /* * Set a specific HTML template for a field type if the * type is not set, the default template will be used @@ -63,16 +54,7 @@ 'checkbox' => 'checkbox', 'checkboxes' => 'collections', 'radios' => 'collections' - ], - /* - * Set the default classes for each field type - */ - 'field_classes' => [ - // type => class or classes - 'default' => 'form-control', - 'checkbox' => '', - 'error' => 'input-with-feedback' - ], + ] ] ] diff --git a/spec/FormBuilderSpec.php b/spec/FormBuilderSpec.php deleted file mode 100644 index 795afe5..0000000 --- a/spec/FormBuilderSpec.php +++ /dev/null @@ -1,115 +0,0 @@ -getView()->shouldBeCalled()->willReturn($view); - - $this->beConstructedWith($url, 'csrf_token', $theme); - } - - function it_is_initializable() - { - $this->shouldHaveType('Styde\Html\FormBuilder'); - } - - function it_adds_the_novalidate_attribute_to_all_forms($html) - { - $this->novalidate(true); - - $this->open(['method' => 'GET'])->shouldReturn(''); - } - - function it_generates_time_inputs($html) - { - $this->time('time')->render()->shouldReturn(''); - } - - function it_generate_radios($theme) - { - // Having - $name = 'gender'; - $attributes = []; - - // Expect - $radios = [ - [ - "name" => "gender", - "value" => "m", - "label" => "Male", - "selected" => true, - "id" => "gender_m" - ], - [ - "name" => "gender", - "value" => "f", - "label" => "Female", - "selected" => false, - "id" => "gender_f" - ] - ]; - - $theme->getView()->shouldBeCalled(); - $theme->render(null, compact('name', 'radios', 'attributes'), "forms.radios")->shouldBeCalled(); - - // When - $this->radios('gender', ['m' => 'Male', 'f' => 'Female'], 'm'); - } - - function it_generate_checkboxes($theme) - { - // Having - $name = 'tags'; - $tags = ['php' => 'PHP', 'python' => 'Python', 'js' => 'JS', 'ruby' => 'Ruby on Rails']; - $checked = ['php', 'js']; - $attributes = []; - - // Expect - $checkboxes = [ - [ - "name" => "tags[]", - "value" => "php", - "label" => "PHP", - "checked" => true, - "id" => "tags_php" - ], - [ - "name" => "tags[]", - "value" => "python", - "label" => "Python", - "checked" => false, - "id" => "tags_python" - ], - [ - "name" => "tags[]", - "value" => "js", - "label" => "JS", - "checked" => true, - "id" => "tags_js" - ], - [ - "name" => "tags[]", - "value" => "ruby", - "label" => "Ruby on Rails", - "checked" => false, - "id" => "tags_ruby" - ] - ]; - $theme->getView()->shouldBeCalled(); - $theme->render(null, compact('name', 'checkboxes', 'attributes'), "forms.checkboxes")->shouldBeCalled(); - - // When - $this->checkboxes('tags', $tags, $checked); - } - -} diff --git a/spec/StrSpec.php b/spec/StrSpec.php deleted file mode 100644 index 02d1b79..0000000 --- a/spec/StrSpec.php +++ /dev/null @@ -1,58 +0,0 @@ -shouldHaveType('Styde\Html\Str'); - } - - function it_converts_strings_like_field_names_to_titles() - { - $this->title('full_name')->shouldReturn('Full name'); - } - - function it_converts_plain_text_links_to_html_links() - { - // HTTP - $text = 'Please visit http://styde.net'; - $html = 'Please visit http://styde.net'; - $this->linkify($text)->shouldReturn($html); - - // HTTPS - $text = 'Please visit https://styde.net'; - $html = 'Please visit https://styde.net'; - $this->linkify($text)->shouldReturn($html); - } - - function it_resumes_a_string() - { - $text = '"My name is Ozymandias, king of kings: - Look on my works, ye Mighty, and despair!" - Nothing beside remains. Round the decay - Of that colossal wreck, boundless and bare - The lone and level sands stretch far away'; - - $resume = '"My name is Ozymandias, king of kings: Look on my works,...'; - $this->teaser($text, 56)->shouldReturn($resume); - - // it also strips HTML - $text = '"My name is Ozymandias, king of kings: - Look on my works, ye Mighty, and despair!" - Nothing beside remains. Round the decay - Of that colossal wreck, boundless and bare - The lone and level sands stretch far away'; - - $resume = '"My name is Ozymandias, king of kings: Look on my works,...'; - $this->teaser($text, 56)->shouldReturn($resume); - - // if a string is short it does nothing - $text = 'You know nothing, John Snow'; - $this->teaser($text, 50)->shouldReturn($text); - } -} \ No newline at end of file diff --git a/src/Alert/Container.php b/src/Alert/Container.php index 764fcfc..aef98c0 100644 --- a/src/Alert/Container.php +++ b/src/Alert/Container.php @@ -2,6 +2,7 @@ namespace Styde\Html\Alert; +use Illuminate\Support\HtmlString; use Styde\Html\Theme; use Illuminate\Translation\Translator as Lang; @@ -177,16 +178,15 @@ public function render($custom = null) { $messages = $this->toArray(); - if ( ! empty ($messages)) { - $this->clearMessages(); - return $this->theme->render( - $custom, - ['messages' => $this->withDefaults($messages)], - 'alert' - ); + if (empty ($messages)) { + return ''; } - return ''; + $this->clearMessages(); + + return new HtmlString($this->theme->render( + $custom, ['messages' => $this->withDefaults($messages)], 'alert' + )); } /** diff --git a/src/Facades/Form.php b/src/Facades/Form.php new file mode 100644 index 0000000..207cac5 --- /dev/null +++ b/src/Facades/Form.php @@ -0,0 +1,18 @@ + 'placeholder', 'req' => 'required'] - * - * You can set these values in the config file - * - * @param array $abbreviations - */ - public function setAbbreviations(array $abbreviations) - { - $this->abbreviations = $abbreviations; - } - - /** - * Set the default CSS classes for each input type. - * You can set these values in the config file. - * - * @param array $cssClasses - */ - public function setCssClasses(array $cssClasses) - { - $this->cssClasses = $cssClasses; - } - /** * Set the default templates for each input type. * You can set these values in the config file. @@ -368,12 +330,10 @@ public function checkbox($name, $value = 1, $selected = null, array $attributes * @param array|null $options * @return string */ - public function build($type, $name, $value = null, array $attributes = array(), array $extra = array(), $options = null) + public function build($type, $name, $value = null, array $attributes = [], array $extra = [], $options = null) { $field = new Field($this, $name, $type); - $attributes = $this->replaceAttributes($attributes); - if (isset ($attributes['label'])) { $field->label($attributes['label']); unset($attributes['label']); @@ -424,8 +384,6 @@ public function render(Field $field) $type = $field->getType(); - $attributes = $this->getHtmlAttributes($type, $attributes, $errors, $id); - $input = $this->buildControl($type, $name, $field->getValue(), $attributes, $field->getOptions(), $htmlName); return $this->theme->render( @@ -664,56 +622,6 @@ protected function getDefaultLabel($name) return ucfirst($label); } - /** - * Get the default HTML classes for a particular type. - * - * If the type is not defined it will use the 'default' key in the - * cssClasses array, otherwise it will return an empty string. - * - * @param string $type - * @return string - */ - protected function getDefaultClasses($type) - { - if (isset($this->cssClasses[$type])) { - return $this->cssClasses[$type]; - } - - if (isset($this->cssClasses['default'])) { - return $this->cssClasses['default']; - } - - return ''; - } - - /** - * Get the HTML classes for a particular field. - * - * It concatenates the default CSS classes plus the custom classes (passed - * as the class key in the $attributes array). - * - * And it will also add an extra class if the control has any errors. - * - * @param string $type - * @param array $attributes - * @param string|null $errors - * @return string - */ - protected function getClasses($type, array $attributes = [], $errors = null) - { - $classes = $this->getDefaultClasses($type); - - if (isset($attributes['class'])) { - $classes .= ' '.$attributes['class']; - } - - if (! empty($errors)) { - $classes .= ' '.(isset($classes['error']) ? $classes['error'] : 'error'); - } - - return trim($classes); - } - /** * Get the control's errors (if any) * @@ -731,43 +639,6 @@ protected function getControlErrors($name) return []; } - /** - * Get the HTML attributes for a control (input, select, etc.) - * - * This will assign the CSS classes and the id attribute. - * - * @param string $type - * @param array $attributes - * @param array $errors - * @param string $htmlId - * @param bool $required - * @return array - */ - protected function getHtmlAttributes($type, $attributes, $errors, $htmlId) - { - $attributes['class'] = $this->getClasses($type, $attributes, $errors); - $attributes['id'] = $htmlId; - return $attributes; - } - - /** - * Search for abbreviations and replace them with the right attributes - * - * @param array $attributes - * @return array - */ - protected function replaceAttributes(array $attributes) - { - foreach ($this->abbreviations as $abbreviation => $attribute) { - if (isset($attributes[$abbreviation])) { - $attributes[$attribute] = $attributes[$abbreviation]; - unset($attributes[$abbreviation]); - } - } - - return $attributes; - } - /** * Swap values ($value and $attributes) if necessary, then call build * diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 62728d7..949e323 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Session\Session; use Illuminate\Contracts\Routing\UrlGenerator; +use Illuminate\Support\HtmlString; class FormBuilder { @@ -100,18 +101,13 @@ public function open(array $attributes = array()) /** * Create a form label element. * - * @param string $name * @param string $content * @param array $attributes * * @return \Styde\Html\HtmlElement */ - public function label($name, $content = '', $attributes = []) + public function label($content, $attributes = []) { - if (empty ($content)) { - $content = ucwords(str_replace('_', ' ', $name)); - } - return new HtmlElement('label', $content, $attributes); } @@ -171,7 +167,63 @@ public function textarea($type, $name, $value = null, $attributes = []) */ public function select($name, $list = [], $selected = null, array $attributes = []) { - return new HtmlElement('select', '', array_merge(compact('name'), $attributes)); + return new HtmlElement('select', $this->options($list, $selected), array_merge(compact('name'), $attributes)); + } + + public function options($list, $selected, array $attributes = []) + { + $options = []; + + foreach ($list as $value => $text) { + if (is_array($text)) { + $options[] = $this->optionGroup($value, $text, $selected, $attributes); + } else { + $options[] = $this->option($text, $value, $selected, $attributes); + } + } + + return $options; + } + + /** + * Create an option group form element. + * + * @param array $list + * @param string $label + * @param string $selected + * @param array $attributes + * + * @return \Illuminate\Support\HtmlString + */ + protected function optionGroup($label, $list, $selected, array $attributes = []) + { + $options = []; + + foreach ($list as $value => $text) { + $options[] = $this->option($text, $value, $selected, $attributes); + } + + return new HtmlElement('optgroup', $options, compact('label') + $attributes); + } + + /** + * Create an option element + * @param string $text + * @param mixed $value + * @param bool $selected + * @param array $attributes + * + * @return \Styde\Html\HtmlElement + */ + public function option($text, $value, $selected, array $attributes = []) + { + if (is_array($selected)) { + $isSelected = in_array($value, $selected); + } else { + $isSelected = $value == $selected; + } + + return new HtmlElement('option', $text, ['value' => $value, 'selected' => $isSelected] + $attributes); } /** @@ -263,19 +315,20 @@ public function radios($name, $options = array(), $checked = null, $attributes = $template = $attributes['template'] ?? null; - unset ($attributes['inline'], $attributes['template']); - - $labels = []; + $radios = []; foreach ($options as $value => $text) { - $id = $name.'_'.Str::slug($value); - - $radio = $this->radio($name, $value, $checked == $value, compact('id')); + $id = $name.'_'.str_slug($value,'_'); - $labels[] = new HtmlElement('label', compact('text', 'radio')); + $radios[] = [ + $this->radio($name, $value, $checked == $value, ['id' => $id]), + $this->label($text, ['for' => $id]), + ]; } - return $this->theme->render($template, compact('name', 'labels'), $defaultTemplate); + return new HtmlString( + $this->theme->render($template, compact('name', 'radios'), $defaultTemplate) + ); } /** @@ -307,21 +360,16 @@ public function checkboxes($name, $options = array(), $selected = null, $attribu $checkboxes = []; foreach ($options as $value => $label) { + $id = $name.'_'.str_slug($value,'_'); + $checkboxes[] = [ - 'name' => $name.'[]', - 'value' => $value, - 'label' => $label, - 'checked' => is_array($selected) && in_array($value, $selected), - 'id' => $name.'_'.Str::slug($value) + $this->checkbox($name.'[]', $value, in_array($value, $selected), ['id' => $id]), + $this->label($label, ['for' => $id]), ]; } - unset ($attributes['inline'], $attributes['template']); - - return $this->theme->render( - $template, - compact('name', 'checkboxes', 'attributes'), - $defaultTemplate + return new HtmlString( + $this->theme->render($template, compact('name', 'checkboxes', 'attributes'), $defaultTemplate) ); } diff --git a/src/FormModel/Field.php b/src/FormModel/Field.php index f49e4d3..0301293 100644 --- a/src/FormModel/Field.php +++ b/src/FormModel/Field.php @@ -3,8 +3,9 @@ namespace Styde\Html\FormModel; use Styde\Html\FieldBuilder; +use Illuminate\Contracts\Support\Htmlable; -class Field +class Field implements Htmlable { use HasAttributes; @@ -35,15 +36,15 @@ class Field /** * @var array */ - protected $attributes = array(); + protected $attributes = []; /** * @var array */ - protected $extra = array(); + protected $extra = []; /** * @var array */ - protected $options = array(); + protected $options = []; public function __construct(FieldBuilder $fieldBuilder, $name, $type = 'text') { @@ -147,4 +148,9 @@ public function render() return $this->fieldBuilder->render($this); } + public function toHtml() + { + return $this->render(); + } + } \ No newline at end of file diff --git a/src/HtmlBuilder.php b/src/HtmlBuilder.php index cdd158e..1ce1406 100644 --- a/src/HtmlBuilder.php +++ b/src/HtmlBuilder.php @@ -37,29 +37,33 @@ public function __construct(UrlGenerator $url = null, Factory $view) * Builds an HTML class attribute dynamically. * * @param array $classes - * + * @param bool $addClassAttribute * @return string */ - public function classes(array $classes) + public function classes(array $classes, $addClassAttribute = true) { $html = ''; foreach ($classes as $name => $bool) { if (is_int($name)) { - $name = $bool; - $bool = true; + $html .= "{$bool} "; + continue; } if ($bool) { - $html .= $name.' '; + $html .= "{$name} "; } } - if (!empty($html)) { - return ' class="'.trim($html).'"'; + if (empty($html)) { + return ''; } - return ''; + if ($addClassAttribute) { + return ' class="'.trim($html).'"'; + } else { + return trim($html); + } } /** diff --git a/src/HtmlElement.php b/src/HtmlElement.php index 2cff374..7daf11e 100644 --- a/src/HtmlElement.php +++ b/src/HtmlElement.php @@ -57,6 +57,10 @@ public function attr($name, $value = true) return $this; } + public function classes($classes) + { + return $this->attr('class', app(HtmlBuilder::class)->classes((array) $classes, false)); + } /** * Render the HTML element. * @@ -157,7 +161,7 @@ public function __get($name) public function __toString() { - return $this->render(); + return $this->toHtml(); } /** @@ -167,6 +171,6 @@ public function __toString() */ public function toHtml() { - return $this->render(); + return (string) $this->render(); } -} \ No newline at end of file +} diff --git a/src/HtmlServiceProvider.php b/src/HtmlServiceProvider.php index bf21bd1..7990481 100644 --- a/src/HtmlServiceProvider.php +++ b/src/HtmlServiceProvider.php @@ -46,8 +46,8 @@ class HtmlServiceProvider extends ServiceProvider 'Field' => \Styde\Html\Facades\Field::class, 'Alert' => \Styde\Html\Facades\Alert::class, 'Menu' => \Styde\Html\Facades\Menu::class, - 'Form' => \Collective\Html\FormFacade::class, - 'Html' => \Collective\Html\HtmlFacade::class + 'Form' => \Styde\Html\Facades\Form::class, + 'Html' => \Styde\Html\Facades\Html::class, ]; public function boot() @@ -195,14 +195,6 @@ protected function registerFieldBuilder() $fieldBuilder->setAccessHandler($app[AccessHandler::class]); } - $fieldBuilder->setAbbreviations($this->options['abbreviations']); - - if (isset ($this->options['theme_values']['field_classes'])) { - $fieldBuilder->setCssClasses( - $this->options['theme_values']['field_classes'] - ); - } - if (isset ($this->options['theme_values']['field_templates'])) { $fieldBuilder->setTemplates( $this->options['theme_values']['field_templates'] diff --git a/src/Menu/Menu.php b/src/Menu/Menu.php index 9432554..43e6d9f 100644 --- a/src/Menu/Menu.php +++ b/src/Menu/Menu.php @@ -3,6 +3,7 @@ namespace Styde\Html\Menu; use Closure; +use Illuminate\Contracts\Support\Htmlable; use Styde\Html\Str; use Styde\Html\Theme; use Styde\Html\Access\VerifyAccess; @@ -10,7 +11,7 @@ use Illuminate\Translation\Translator as Lang; use Illuminate\Contracts\Routing\UrlGenerator as Url; -class Menu +class Menu implements Htmlable { use VerifyAccess { checkAccess as traitCheckAccess; @@ -244,23 +245,6 @@ public function getCurrentId() return $this->currentId; } - /** - * Renders a new menu - * - * @param string|null $customTemplate - * @return string the menu's HTML - */ - public function render($customTemplate = null) - { - $items = $this->generateItems($this->items); - - return $this->theme->render( - $customTemplate, - ['items' => $items, 'class' => $this->class], - 'menu' - ); - } - /** * Generate and get the array of menu items but won't render the menu * @@ -551,4 +535,30 @@ protected function generateUrl($values) return '#'; } + /** + * Renders a new menu + * + * @param string|null $customTemplate + * @return string the menu's HTML + */ + public function render($customTemplate = null) + { + $items = $this->generateItems($this->items); + + return $this->theme->render( + $customTemplate, + ['items' => $items, 'class' => $this->class], + 'menu' + ); + } + + /** + * Get content as a string of HTML. + * + * @return string + */ + public function toHtml() + { + return $this->render(); + } } diff --git a/src/Str.php b/src/Str.php index fa1bfc6..c46b90d 100644 --- a/src/Str.php +++ b/src/Str.php @@ -6,8 +6,7 @@ class Str extends \Illuminate\Support\Str { /** - * Convert camel cases, underscore and hyphen separated strings to human - * format. + * Convert camel cases, underscore and hyphen separated strings to human format. * * @param $string * @return string @@ -35,8 +34,7 @@ public static function linkify($text) } /** - * Cuts a string but without leaving incomplete words and adding a $end - * string if necessary. + * Cuts a string but without leaving incomplete words and adding a $end string if necessary. * * @param $value * @param $length diff --git a/tests/AccessHandlerTest.php b/tests/AccessHandlerTest.php new file mode 100644 index 0000000..7111ac6 --- /dev/null +++ b/tests/AccessHandlerTest.php @@ -0,0 +1,16 @@ +app->make(AccessHandler::class); + + $this->assertInstanceOf(BasicAccessHandler::class, $accessHandler); + } +} \ No newline at end of file diff --git a/tests/AlertTest.php b/tests/AlertTest.php new file mode 100644 index 0000000..5a5ed37 --- /dev/null +++ b/tests/AlertTest.php @@ -0,0 +1,43 @@ +assertTemplateMatches('alert/alert', Alert::render()); + } + + /** @test */ + function it_uses_magic_methods() + { + Alert::success('Success!'); + Alert::info('Some information'); + + $this->assertTemplateMatches('alert/magic', Alert::render()); + } + + /** @test */ + function it_chains_methods_to_build_complex_alert_messages() + { + Alert::info('Your account is about to expire') + ->details('A lot of knowledge still waits for you:') + ->items([ + 'Laravel courses', + 'OOP classes', + 'Access to real projects', + 'Support', + 'And more' + ]) + ->button('Renew now!', '#', 'primary') + ->button('Take me to your leader', 'http://google.com', 'info'); + + $this->assertTemplateMatches('alert/complex', Alert::render()); + } +} diff --git a/tests/FieldBuilderTest.php b/tests/FieldBuilderTest.php new file mode 100644 index 0000000..fe4a5fa --- /dev/null +++ b/tests/FieldBuilderTest.php @@ -0,0 +1,131 @@ +assertTemplateMatches( + 'field/text', Field::text('name', 'value') + ); + } + + /** @test */ + function it_generates_a_text_field_with_a_required_label() + { + $this->assertTemplateMatches( + 'field/text-required', Field::text('name', ['required' => true]) + ); + } + + /** @test */ + public function it_generates_a_text_field_with_a_custom_label() + { + $this->assertTemplateMatches( + 'field/text-custom-label', Field::text('name', 'value', ['label' => 'Full name']) + ); + } + + /** @test */ + public function it_generates_a_select_field() + { + trans()->addLines([ + 'validation.empty_option.default' => 'Select value', + ], 'en'); + + $this->assertTemplateMatches( + 'field/select', Field::select('gender', ['m' => 'Male', 'f' => 'Female']) + ); + } + + /** @test */ + function it_adds_an_empty_option_to_select_fields() + { + $this->assertTemplateMatches( + 'field/select-empty', Field::select('gender', ['m' => 'Male', 'f' => 'Female'], ['empty' => 'Select gender']) + ); + } + + /** @test */ + function it_generates_a_multiple_select_field() + { + $options = [ + 'php' => 'PHP', + 'laravel' => 'Laravel', + 'symfony' => 'Symfony', + 'ruby' => 'Ruby on Rails' + ]; + + $this->assertTemplateMatches( + 'field/select-multiple', Field::select('tags', $options, ['php', 'laravel'], ['multiple']) + ); + + $this->assertTemplateMatches( + 'field/select-multiple', Field::selectMultiple('tags', $options, ['php', 'laravel']) + ); + } + + /** @test */ + function it_generates_a_multiple_select_field_with_optgroup() + { + $options = [ + 'backend' => [ + 'laravel' => 'Laravel', + 'rails' => 'Ruby on Rails', + ], + 'frontend' => [ + 'vue' => 'Vue', + 'angular' => 'Angular', + ], + ]; + + $this->assertTemplateMatches( + 'field/select-group', Field::selectMultiple('frameworks', $options, ['vue', 'laravel']) + ); + } + + /** @test */ + function it_generates_a_text_field_with_errors() + { + tap(app('session.store'), function ($session) { + $session->put('errors', new MessageBag([ + 'name' => ['This is really wrong'] + ])); + + Field::setSessionStore($session); + }); + + $this->assertTemplateMatches( + 'field/text_with_errors', Field::text('name') + ); + } + + /** @test */ + function it_generates_checkboxes() + { + $tags = [ + 'php' => 'PHP', + 'python' => 'Python', + 'js' => 'JS', + 'ruby' => 'Ruby on Rails' + ]; + $checked = ['php', 'js']; + + $this->assertTemplateMatches( + 'field/checkboxes', Field::checkboxes('tags', $tags, $checked) + ); + } + + /** @test */ + function it_generate_radios() + { + $this->assertTemplateMatches( + 'field/radios', Field::radios('gender', ['m' => 'Male', 'f' => 'Female'], 'm') + ); + } +} \ No newline at end of file diff --git a/tests/FormBuilderTest.php b/tests/FormBuilderTest.php index 2951c17..f02eb12 100644 --- a/tests/FormBuilderTest.php +++ b/tests/FormBuilderTest.php @@ -2,9 +2,6 @@ namespace Styde\Html\Tests; -use Mockery; -use Styde\Html\HtmlElement; - class FormBuilderTest extends TestCase { /** @@ -14,7 +11,7 @@ class FormBuilderTest extends TestCase function setUp() { - parent::setUp(); // TODO: Change the autogenerated stub + parent::setUp(); $this->formBuilder = $this->newFormBuilder(); } @@ -24,16 +21,35 @@ function it_adds_the_novalidate_attribute_to_all_forms() { $this->formBuilder->novalidate(true); - $html = $this->formBuilder->open(['method' => 'GET']); - - $this->assertSame('', $html); + $this->assertHtmlEquals( + '', $this->formBuilder->open(['method' => 'GET']) + ); } /** @test */ function it_generates_time_inputs() { - $html = $this->formBuilder->time('time')->render(); + $this->assertHtmlEquals( + '', $this->formBuilder->time('time') + ); + } + + /** @test */ + function it_generate_radios() + { + $this->assertTemplateMatches( + 'form/radios', $this->formBuilder->radios('gender', ['m' => 'Male', 'f' => 'Female'], 'm') + ); + } + + /** @test */ + function it_generate_checkboxes() + { + $tags = ['php' => 'PHP', 'python' => 'Python', 'js' => 'JS', 'ruby' => 'Ruby on Rails']; + $checked = ['php', 'js']; - $this->assertSame('', $html); + $this->assertTemplateMatches( + 'form/checkboxes', $this->formBuilder->checkboxes('tags', $tags, $checked) + ); } } \ No newline at end of file diff --git a/tests/MenuGeneratorTest.php b/tests/MenuGeneratorTest.php new file mode 100644 index 0000000..85f14ac --- /dev/null +++ b/tests/MenuGeneratorTest.php @@ -0,0 +1,118 @@ + ['url' => '/'], + 'about' => [], + 'projects' => ['title' => 'Our projects', 'url' => 'projects'], + 'contact' => ['url' => 'contact-us'], + ]; + + $this->assertTemplateMatches('menu/menu', Menu::make($items)); + } + + /** @test */ + function it_generates_routes() + { + Route::get('dashboard', ['as' => 'dashboard']); + Route::get('edit_home', ['as' => 'pages.edit']); + + $items = [ + 'dashboard' => ['route' => 'dashboard'], + 'edit_home' => ['route' => ['pages.edit', 'home']], + ]; + + $this->assertTemplateMatches('menu/routes', Menu::make($items)); + } + + /** @test */ + function it_implements_routes_with_dynamic_parameters() + { + Route::get('account/{user_id}', ['as' => 'account']); + Route::get('calendar/{year}/{month}/{day}', ['as' => 'calendar']); + + $items = [ + 'account' => [ + 'route' => ['account', ':user_id'], + ], + 'calendar' => [ + 'route' => ['calendar', ':year', ':month', ':day'], + ], + ]; + + $menu = Menu::make($items) + ->setParams(['year' => 2015, 'month' => 07, 'day' => 11]) + ->setParam('user_id', 20); + + $this->assertTemplateMatches('menu/parameters', $menu); + } + + /** @test */ + function it_checks_for_access_using_the_access_handler_and_the_gate() + { + $fakeUser = new class extends Model implements AuthenticatableInterface { + use Authenticatable; + }; + + $fakePost = new class { public $id = 1; }; + + Auth::login($fakeUser); + + Gate::define('update-post', function ($user, $post) use ($fakePost) { + return $post->id === $fakePost->id; + }); + + Gate::define('delete-post', function ($user) { + return false; + }); + + $items = array( + 'view-post' => [ + ], + 'edit-post' => [ + 'allows' => ['update-post', ':post'] + ], + 'review-post' => [ + 'denies' => ['update-post', ':post'] + ], + 'delete-post' => [ + 'allows' => 'delete-post' + ] + ); + + $menu = Menu::make($items)->setParam('post', $fakePost); + + $this->assertTemplateMatches('menu/access-handler', $menu); + } + + /** @test */ + function it_generates_submenus() + { + $items = [ + 'home' => ['url' => '/'], + 'about' => [ + 'submenu' => [ + 'team' => [], + 'careers' => ['title' => 'Work with us'], + ], + ], + 'projects' => ['title' => 'Our projects', 'url' => 'projects'], + 'contact' => ['url' => 'contact-us'], + ]; + + $this->assertTemplateMatches('menu/submenu', Menu::make($items)); + } + +} diff --git a/tests/StrTest.php b/tests/StrTest.php new file mode 100644 index 0000000..68deabb --- /dev/null +++ b/tests/StrTest.php @@ -0,0 +1,62 @@ +assertSame('Full name', Str::title('full_name')); + } + + /** @test */ + function it_converts_plain_text_links_to_html_links() + { + // HTTP + $this->assertSame( + 'Please visit http://styde.net', + Str::linkify('Please visit http://styde.net') + ); + + // HTTPS + $this->assertSame( + 'Please visit https://styde.net', + Str::linkify('Please visit https://styde.net') + ); + } + + /** @test */ + function it_resumes_a_string() + { + $fullText = '"My name is Ozymandias, king of kings: + Look on my works, ye Mighty, and despair!" + Nothing beside remains. Round the decay + Of that colossal wreck, boundless and bare + The lone and level sands stretch far away'; + + $this->assertSame( + '"My name is Ozymandias, king of kings: Look on my works,...', + Str::teaser($fullText, 56) + ); + + // it also strips HTML + $textWithHtml = '"My name is Ozymandias, king of kings: + Look on my works, ye Mighty, and despair!" + Nothing beside remains. Round the decay + Of that colossal wreck, boundless and bare + The lone and level sands stretch far away'; + + $this->assertSame( + '"My name is Ozymandias, king of kings: Look on my works,...', + Str::teaser($textWithHtml, 56) + ); + + // if a string is short it does nothing + $text = 'You know nothing, John Snow'; + + $this->assertSame($text, Str::teaser($text, 28)); + } +} \ No newline at end of file diff --git a/tests/TestCase.php b/tests/TestCase.php index 807ada8..cae1fb0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,16 +2,14 @@ namespace Styde\Html\Tests; -use Illuminate\Contracts\Routing\UrlGenerator; -use Illuminate\Contracts\View\Factory; -use Illuminate\Contracts\View\View; -use Mockery; -use Styde\Html\FormBuilder; -use Styde\Html\HtmlBuilder; -use Styde\Html\Theme; - -class TestCase extends \PHPUnit\Framework\TestCase +use Styde\Html\HtmlServiceProvider; +use Styde\Html\{FormBuilder, HtmlBuilder}; +use Orchestra\Testbench\TestCase as OrchestraTestCase; + +class TestCase extends OrchestraTestCase { + use TestHelpers; + /** * @var \Mockery\MockInterface */ @@ -24,36 +22,16 @@ class TestCase extends \PHPUnit\Framework\TestCase protected function newHtmlBuilder() { - return new HtmlBuilder($this->mockUrlGenerator(), $this->mockViewFactory()); + return app(HtmlBuilder::class); } protected function newFormBuilder() { - return new FormBuilder($this->mockUrlGenerator(), $this->newTheme(), 'csrf_token_value'); - } - - protected function newTheme() - { - return new Theme($this->mockViewFactory(), 'bootstrap'); - } - - protected function mockViewFactory() - { - return $this->viewFactory = Mockery::mock(Factory::class); - } - - protected function mockUrlGenerator() - { - return $this->urlGenerator = Mockery::mock(UrlGenerator::class); - } - - protected function mockView() - { - return Mockery::mock(View::class); + return app(FormBuilder::class); } - protected function tearDown() + protected function getPackageProviders($app) { - Mockery::close(); + return [HtmlServiceProvider::class]; } -} \ No newline at end of file +} diff --git a/tests/TestHelpers.php b/tests/TestHelpers.php new file mode 100644 index 0000000..945a285 --- /dev/null +++ b/tests/TestHelpers.php @@ -0,0 +1,45 @@ +assertInstanceOf(Htmlable::class, $actual); + + $this->assertSame($expected, $actual->toHtml()); + } + + protected function assertTemplateMatches($template, $actual) + { + $this->assertInstanceOf(Htmlable::class, $actual); + + $actual = $actual->toHtml(); + + $theme = config('html.theme', 'bootstrap4'); + + $template = __DIR__ . "/snapshots/$theme/$template.html"; + + if ( ! file_exists($template)) { + file_put_contents($template, $actual); + $this->markTestIncomplete("The template [$template] was created"); + + return; + } + + $html = file_get_contents($template); + + return $this->assertEquals( + $this->removeExtraWhitespaces($html), + $this->removeExtraWhitespaces($actual) + ); + } + + private function removeExtraWhitespaces($string) + { + return trim(str_replace('> <', '><', preg_replace('/\s+/', ' ', $string))); + } +} \ No newline at end of file diff --git a/tests/snapshots/bootstrap4/alert/alert.html b/tests/snapshots/bootstrap4/alert/alert.html new file mode 100644 index 0000000..dd909ca --- /dev/null +++ b/tests/snapshots/bootstrap4/alert/alert.html @@ -0,0 +1,6 @@ + diff --git a/tests/snapshots/bootstrap4/alert/complex.html b/tests/snapshots/bootstrap4/alert/complex.html new file mode 100644 index 0000000..21a8b5c --- /dev/null +++ b/tests/snapshots/bootstrap4/alert/complex.html @@ -0,0 +1,18 @@ + diff --git a/tests/snapshots/bootstrap4/alert/magic.html b/tests/snapshots/bootstrap4/alert/magic.html new file mode 100644 index 0000000..e167a74 --- /dev/null +++ b/tests/snapshots/bootstrap4/alert/magic.html @@ -0,0 +1,12 @@ + + diff --git a/tests/snapshots/bootstrap4/field/checkboxes.html b/tests/snapshots/bootstrap4/field/checkboxes.html new file mode 100644 index 0000000..0b4e635 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/checkboxes.html @@ -0,0 +1,21 @@ +

+ Tags +

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
diff --git a/tests/snapshots/bootstrap4/field/radios.html b/tests/snapshots/bootstrap4/field/radios.html new file mode 100644 index 0000000..3786969 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/radios.html @@ -0,0 +1,13 @@ +

+ Gender +

+
+ + +
+
+ + +
+ +
diff --git a/tests/snapshots/bootstrap4/field/select-empty.html b/tests/snapshots/bootstrap4/field/select-empty.html new file mode 100644 index 0000000..7361233 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/select-empty.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/select-group.html b/tests/snapshots/bootstrap4/field/select-group.html new file mode 100644 index 0000000..4bbd4ab --- /dev/null +++ b/tests/snapshots/bootstrap4/field/select-group.html @@ -0,0 +1,13 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/select-multiple.html b/tests/snapshots/bootstrap4/field/select-multiple.html new file mode 100644 index 0000000..732f7f8 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/select-multiple.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/select.html b/tests/snapshots/bootstrap4/field/select.html new file mode 100644 index 0000000..a4ec7db --- /dev/null +++ b/tests/snapshots/bootstrap4/field/select.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/text-custom-label.html b/tests/snapshots/bootstrap4/field/text-custom-label.html new file mode 100644 index 0000000..89341a4 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/text-custom-label.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/text-required.html b/tests/snapshots/bootstrap4/field/text-required.html new file mode 100644 index 0000000..04b72c0 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/text-required.html @@ -0,0 +1,7 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/text.html b/tests/snapshots/bootstrap4/field/text.html new file mode 100644 index 0000000..5b9c5ee --- /dev/null +++ b/tests/snapshots/bootstrap4/field/text.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/tests/snapshots/bootstrap4/field/text_with_errors.html b/tests/snapshots/bootstrap4/field/text_with_errors.html new file mode 100644 index 0000000..44e8b6d --- /dev/null +++ b/tests/snapshots/bootstrap4/field/text_with_errors.html @@ -0,0 +1,7 @@ +
+ + +
This is really wrong
+
diff --git a/tests/snapshots/bootstrap4/form/checkboxes.html b/tests/snapshots/bootstrap4/form/checkboxes.html new file mode 100644 index 0000000..6dc1a8e --- /dev/null +++ b/tests/snapshots/bootstrap4/form/checkboxes.html @@ -0,0 +1,16 @@ +
+ + +
+
+ + +
+
+ + +
+
+ + +
diff --git a/tests/snapshots/bootstrap4/form/radios.html b/tests/snapshots/bootstrap4/form/radios.html new file mode 100644 index 0000000..282e570 --- /dev/null +++ b/tests/snapshots/bootstrap4/form/radios.html @@ -0,0 +1,29 @@ +
+ + +
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/tests/snapshots/bootstrap4/menu/access-handler.html b/tests/snapshots/bootstrap4/menu/access-handler.html new file mode 100644 index 0000000..8a38457 --- /dev/null +++ b/tests/snapshots/bootstrap4/menu/access-handler.html @@ -0,0 +1,12 @@ + diff --git a/tests/snapshots/bootstrap4/menu/menu.html b/tests/snapshots/bootstrap4/menu/menu.html new file mode 100644 index 0000000..5d5b3e4 --- /dev/null +++ b/tests/snapshots/bootstrap4/menu/menu.html @@ -0,0 +1,22 @@ + diff --git a/tests/snapshots/bootstrap4/menu/parameters.html b/tests/snapshots/bootstrap4/menu/parameters.html new file mode 100644 index 0000000..69fce69 --- /dev/null +++ b/tests/snapshots/bootstrap4/menu/parameters.html @@ -0,0 +1,12 @@ + diff --git a/tests/snapshots/bootstrap4/menu/routes.html b/tests/snapshots/bootstrap4/menu/routes.html new file mode 100644 index 0000000..1aa9285 --- /dev/null +++ b/tests/snapshots/bootstrap4/menu/routes.html @@ -0,0 +1,12 @@ + diff --git a/tests/snapshots/bootstrap4/menu/submenu.html b/tests/snapshots/bootstrap4/menu/submenu.html new file mode 100644 index 0000000..44ccc60 --- /dev/null +++ b/tests/snapshots/bootstrap4/menu/submenu.html @@ -0,0 +1,26 @@ + diff --git a/themes/bootstrap/fields/checkbox.blade.php b/themes/bootstrap/fields/checkbox.blade.php deleted file mode 100644 index 640871c..0000000 --- a/themes/bootstrap/fields/checkbox.blade.php +++ /dev/null @@ -1,18 +0,0 @@ -
$hasErrors]) !!}> - - - @if ($required) - Required - @endif - - @if (!empty($errors)) -
- @foreach ($errors as $error) -

{{ $error }}

- @endforeach -
- @endif -
diff --git a/themes/bootstrap/fields/collections.blade.php b/themes/bootstrap/fields/collections.blade.php deleted file mode 100644 index e293d74..0000000 --- a/themes/bootstrap/fields/collections.blade.php +++ /dev/null @@ -1,13 +0,0 @@ -

{{ $label }}

- -{!! $input !!} - -@if ( ! empty($errors)) -
- @foreach ($errors as $error) -

{{ $error }}

- @endforeach -
-@endif - -
diff --git a/themes/bootstrap/fields/default.blade.php b/themes/bootstrap/fields/default.blade.php deleted file mode 100644 index d0222de..0000000 --- a/themes/bootstrap/fields/default.blade.php +++ /dev/null @@ -1,16 +0,0 @@ -
$hasErrors]) !!}> - - - @if ($required) - Required - @endif - -
- {!! $input !!} - @foreach ($errors as $error) -

{{ $error }}

- @endforeach -
-
diff --git a/themes/bootstrap/forms/checkboxes.blade.php b/themes/bootstrap/forms/checkboxes.blade.php deleted file mode 100644 index 66f8813..0000000 --- a/themes/bootstrap/forms/checkboxes.blade.php +++ /dev/null @@ -1,13 +0,0 @@ -@foreach($checkboxes as $checkbox) -
- -
-@endforeach \ No newline at end of file diff --git a/themes/bootstrap/forms/radios.blade.php b/themes/bootstrap/forms/radios.blade.php deleted file mode 100644 index b5fe6ac..0000000 --- a/themes/bootstrap/forms/radios.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -@foreach($labels as $label) -
- {{ $label->open() }} - {{ $label->radio->render() }} - {{ $label->text }} - {{ $label->close() }} -
-@endforeach \ No newline at end of file diff --git a/themes/bootstrap/menu.blade.php b/themes/bootstrap/menu.blade.php deleted file mode 100644 index f4239c5..0000000 --- a/themes/bootstrap/menu.blade.php +++ /dev/null @@ -1,23 +0,0 @@ - \ No newline at end of file diff --git a/themes/bootstrap/alert.blade.php b/themes/bootstrap4/alert.blade.php similarity index 58% rename from themes/bootstrap/alert.blade.php rename to themes/bootstrap4/alert.blade.php index 74765bc..7ed57fb 100644 --- a/themes/bootstrap/alert.blade.php +++ b/themes/bootstrap4/alert.blade.php @@ -1,24 +1,17 @@ @foreach ($messages as $msg) -
- -

- {{ $msg['message'] }} -

- + -@endforeach \ No newline at end of file +@endforeach diff --git a/themes/bootstrap4/fields/checkbox.blade.php b/themes/bootstrap4/fields/checkbox.blade.php new file mode 100644 index 0000000..8802eee --- /dev/null +++ b/themes/bootstrap4/fields/checkbox.blade.php @@ -0,0 +1,12 @@ +
+ {{ $input->classes(['form-check-input', 'is-invalid' => $hasErrors]) }} + $hasErrors]) !!} for="{{ $id }}"> + {{ $label }} + @if ($required) + Required + @endif + + @foreach ($errors as $error) +
{{ $error }}
+ @endforeach +
diff --git a/themes/bootstrap4/fields/collections.blade.php b/themes/bootstrap4/fields/collections.blade.php new file mode 100644 index 0000000..d28712d --- /dev/null +++ b/themes/bootstrap4/fields/collections.blade.php @@ -0,0 +1,12 @@ + $hasErrors]) !!}> + {{ $label }} + @if ($required) + Required + @endif + +{!! $input !!} +@foreach ($errors as $error) +

{{ $error }}

+@endforeach + +
diff --git a/themes/bootstrap4/fields/default.blade.php b/themes/bootstrap4/fields/default.blade.php new file mode 100644 index 0000000..d04c120 --- /dev/null +++ b/themes/bootstrap4/fields/default.blade.php @@ -0,0 +1,12 @@ +
+ + {{ $input->id($id)->classes(['form-control', 'is-invalid' => $hasErrors]) }} +@foreach ($errors as $error) +
{{ $error }}
+@endforeach +
diff --git a/themes/bootstrap/form.blade.php b/themes/bootstrap4/form.blade.php similarity index 100% rename from themes/bootstrap/form.blade.php rename to themes/bootstrap4/form.blade.php diff --git a/themes/bootstrap/forms/checkboxes-inline.blade.php b/themes/bootstrap4/forms/checkboxes-inline.blade.php similarity index 100% rename from themes/bootstrap/forms/checkboxes-inline.blade.php rename to themes/bootstrap4/forms/checkboxes-inline.blade.php diff --git a/themes/bootstrap4/forms/checkboxes.blade.php b/themes/bootstrap4/forms/checkboxes.blade.php new file mode 100644 index 0000000..0f2cb92 --- /dev/null +++ b/themes/bootstrap4/forms/checkboxes.blade.php @@ -0,0 +1,6 @@ +@foreach($checkboxes as [$checkbox, $label]) +
+ {{ $checkbox->class('form-check-input')->render() }} + {{ $label->class('form-check-label')->render() }} +
+@endforeach diff --git a/themes/bootstrap/forms/radios-inline.blade.php b/themes/bootstrap4/forms/radios-inline.blade.php similarity index 100% rename from themes/bootstrap/forms/radios-inline.blade.php rename to themes/bootstrap4/forms/radios-inline.blade.php diff --git a/themes/bootstrap4/forms/radios.blade.php b/themes/bootstrap4/forms/radios.blade.php new file mode 100644 index 0000000..cfd1818 --- /dev/null +++ b/themes/bootstrap4/forms/radios.blade.php @@ -0,0 +1,6 @@ +@foreach($radios as [$radio, $label]) +
+ {{ $radio->class('form-check-input')->render() }} + {{ $label->class('form-check-label')->render() }} +
+@endforeach diff --git a/themes/bootstrap4/menu.blade.php b/themes/bootstrap4/menu.blade.php new file mode 100644 index 0000000..890048e --- /dev/null +++ b/themes/bootstrap4/menu.blade.php @@ -0,0 +1,22 @@ + From 81b00b267f392b5c87045246c733b48e3547adbe Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 30 Jan 2018 22:21:00 +0000 Subject: [PATCH 017/217] Use facades to access the form and the html builder --- src/FormModel/ButtonCollection.php | 2 +- src/FormModel/Link.php | 2 +- tests/FormBuilderTest.php | 24 +++++++----------------- tests/HtmlBuilderTest.php | 23 ++++++----------------- 4 files changed, 15 insertions(+), 36 deletions(-) diff --git a/src/FormModel/ButtonCollection.php b/src/FormModel/ButtonCollection.php index 5dc3eba..5cb7c0f 100644 --- a/src/FormModel/ButtonCollection.php +++ b/src/FormModel/ButtonCollection.php @@ -97,7 +97,7 @@ public function add($type, $text, array $attributes = array()) */ public function link($url, $title, array $attributes = array(), $secure = false) { - return $this->buttons[] = $this->htmlBuilder->link($url, $title, $attributes, $secure); + return $this->buttons[] = Html::link($url, $title, $attributes, $secure); } /** diff --git a/src/FormModel/Link.php b/src/FormModel/Link.php index 82381ab..395d759 100644 --- a/src/FormModel/Link.php +++ b/src/FormModel/Link.php @@ -45,7 +45,7 @@ public function secure($secure = true) */ public function render() { - return $this->htmlBuilder->link($this->url, $this->title, $this->attributes, $this->secure); + return Html::link($this->url, $this->title, $this->attributes, $this->secure); } } \ No newline at end of file diff --git a/tests/FormBuilderTest.php b/tests/FormBuilderTest.php index f02eb12..4ebff3c 100644 --- a/tests/FormBuilderTest.php +++ b/tests/FormBuilderTest.php @@ -2,27 +2,17 @@ namespace Styde\Html\Tests; +use Styde\Html\Facades\Form; + class FormBuilderTest extends TestCase { - /** - * @var \Styde\Html\FormBuilder - */ - protected $formBuilder; - - function setUp() - { - parent::setUp(); - - $this->formBuilder = $this->newFormBuilder(); - } - /** @test */ function it_adds_the_novalidate_attribute_to_all_forms() { - $this->formBuilder->novalidate(true); + Form::novalidate(true); $this->assertHtmlEquals( - '', $this->formBuilder->open(['method' => 'GET']) + '', Form::open(['method' => 'GET']) ); } @@ -30,7 +20,7 @@ function it_adds_the_novalidate_attribute_to_all_forms() function it_generates_time_inputs() { $this->assertHtmlEquals( - '', $this->formBuilder->time('time') + '', Form::time('time') ); } @@ -38,7 +28,7 @@ function it_generates_time_inputs() function it_generate_radios() { $this->assertTemplateMatches( - 'form/radios', $this->formBuilder->radios('gender', ['m' => 'Male', 'f' => 'Female'], 'm') + 'form/radios', Form::radios('gender', ['m' => 'Male', 'f' => 'Female'], 'm') ); } @@ -49,7 +39,7 @@ function it_generate_checkboxes() $checked = ['php', 'js']; $this->assertTemplateMatches( - 'form/checkboxes', $this->formBuilder->checkboxes('tags', $tags, $checked) + 'form/checkboxes', Form::checkboxes('tags', $tags, $checked) ); } } \ No newline at end of file diff --git a/tests/HtmlBuilderTest.php b/tests/HtmlBuilderTest.php index 4d6b19e..cea9c85 100644 --- a/tests/HtmlBuilderTest.php +++ b/tests/HtmlBuilderTest.php @@ -3,32 +3,21 @@ namespace Styde\Html\Tests; use Styde\Html\HtmlElement; +use Styde\Html\Facades\Html; class HtmlBuilderTest extends TestCase { - /** - * @var \Styde\Html\HtmlBuilder - */ - var $htmlBuilder; - - function setUp() - { - parent::setUp(); - - $this->htmlBuilder = $this->newHtmlBuilder(); - } - /** @test */ function it_generates_html_tags() { $this->assertEquals( 'This is a span', - $this->htmlBuilder->tag('span', 'This is a span', ['id' => 'my-span'])->render() + Html::tag('span', 'This is a span', ['id' => 'my-span'])->render() ); $this->assertEquals( '', - $this->htmlBuilder->tag('input', false, ['type' => 'text', 'readonly'])->render() + Html::tag('input', false, ['type' => 'text', 'readonly'])->render() ); } @@ -45,7 +34,7 @@ function it_escapes_the_attributes_of_generated_tags() { $this->assertEquals( 'Span', - $this->htmlBuilder->tag('span', 'Span', ['id' => ''])->render() + Html::tag('span', 'Span', ['id' => ''])->render() ); } @@ -54,14 +43,14 @@ function it_generates_html_tags_with_dynamic_methods() { $this->assertEquals( 'This is a span', - $this->htmlBuilder->span('This is a span')->id('my-span') + Html::span('This is a span')->id('my-span') ); } /** @test */ function it_generate_the_html_class_attribute() { - $html = $this->htmlBuilder->classes([ + $html = Html::classes([ 'home' => true, 'main', 'dont-use-this' => false, From 5498eb415fa440c9f0b392d7838406d35cfede2b Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 30 Jan 2018 22:42:21 +0000 Subject: [PATCH 018/217] Fix dependencies --- composer.json | 7 ++----- src/HtmlServiceProvider.php | 8 ++++++-- tests/TestCase.php | 21 --------------------- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index 871b524..1897911 100644 --- a/composer.json +++ b/composer.json @@ -9,14 +9,11 @@ } ], "require": { - "php": ">=5.5.9", - "laravelcollective/html": "5.4.*", - "laravel/framework": "5.4.*" + "php": ">=7.0", + "laravel/framework": "5.5.*" }, "require-dev": { - "phpspec/phpspec": "~2.1", "phpunit/phpunit": "^6.2", - "mockery/mockery": "^0.9.9", "orchestra/testbench": "~3.0" }, "autoload": { diff --git a/src/HtmlServiceProvider.php b/src/HtmlServiceProvider.php index 7990481..c731d2d 100644 --- a/src/HtmlServiceProvider.php +++ b/src/HtmlServiceProvider.php @@ -6,13 +6,13 @@ use Styde\Html\Menu\MenuGenerator; use Styde\Html\Access\AccessHandler; use Illuminate\Foundation\AliasLoader; +use Illuminate\Support\ServiceProvider; use Styde\Html\Alert\Container as Alert; use Styde\Html\Access\BasicAccessHandler; use Styde\Html\FormModel\FormMakeCommand; use Illuminate\Contracts\Auth\Access\Gate; use Styde\Html\Alert\Middleware as AlertMiddleware; use Styde\Html\Alert\SessionHandler as AlertSessionHandler; -use Collective\Html\HtmlServiceProvider as ServiceProvider; class HtmlServiceProvider extends ServiceProvider { @@ -75,7 +75,9 @@ protected function mergeDefaultConfiguration() */ public function register() { - parent::register(); + $this->registerHtmlBuilder(); + + $this->registerFormBuilder(); $this->registerAccessHandler(); @@ -174,6 +176,8 @@ protected function registerHtmlBuilder() $this->app->singleton('html', function ($app) { return new HtmlBuilder($app['url'], $app['view']); }); + + $this->app->alias('html', HtmlBuilder::class); } /** diff --git a/tests/TestCase.php b/tests/TestCase.php index cae1fb0..75ca583 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,33 +3,12 @@ namespace Styde\Html\Tests; use Styde\Html\HtmlServiceProvider; -use Styde\Html\{FormBuilder, HtmlBuilder}; use Orchestra\Testbench\TestCase as OrchestraTestCase; class TestCase extends OrchestraTestCase { use TestHelpers; - /** - * @var \Mockery\MockInterface - */ - protected $viewFactory; - - /** - * @var \Mockery\MockInterface - */ - protected $urlGenerator; - - protected function newHtmlBuilder() - { - return app(HtmlBuilder::class); - } - - protected function newFormBuilder() - { - return app(FormBuilder::class); - } - protected function getPackageProviders($app) { return [HtmlServiceProvider::class]; From 2b2f4e4d2a4a6b40c81c38327e1a93df4ae63b08 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 30 Jan 2018 23:09:29 +0000 Subject: [PATCH 019/217] update travis config --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c76ad7..547c44b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: php php: - - 5.6 - 7.0 sudo: false @@ -11,7 +10,7 @@ install: composer install script: - - vendor/bin/phpspec + - vendor/bin/phpunit matrix: fast_finish: true \ No newline at end of file From 3633ffae0dc750de393fc43effe61fa359158bb1 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 30 Jan 2018 23:12:06 +0000 Subject: [PATCH 020/217] add php 7.1 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 547c44b..ff01ff6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ language: php php: - 7.0 + - 7.1 sudo: false From 8a2d7ada294ef187d77ae1c96efdcc1fd84c9c68 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Thu, 1 Feb 2018 12:23:20 +0000 Subject: [PATCH 021/217] Progress with submenu generation --- src/Access/BasicAccessHandler.php | 7 - src/HtmlServiceProvider.php | 10 +- src/Menu/Item.php | 62 ++++ src/Menu/Item/Action.php | 23 ++ src/Menu/Item/RawUrl.php | 23 ++ src/Menu/Item/Route.php | 26 ++ src/Menu/Item/Url.php | 37 +++ src/Menu/ItemCollection.php | 67 ++++ src/Menu/Menu.php | 287 +++--------------- src/Menu/MenuGenerator.php | 53 +--- tests/MenuComposerTest.php | 24 ++ tests/MenuGeneratorTest.php | 79 ++--- tests/snapshots/bootstrap4/menu/menu.html | 42 +-- .../snapshots/bootstrap4/menu/parameters.html | 22 +- tests/snapshots/bootstrap4/menu/routes.html | 24 +- tests/snapshots/bootstrap4/menu/submenu.html | 50 +-- themes/bootstrap4/menu.blade.php | 18 +- 17 files changed, 436 insertions(+), 418 deletions(-) create mode 100644 src/Menu/Item.php create mode 100644 src/Menu/Item/Action.php create mode 100644 src/Menu/Item/RawUrl.php create mode 100644 src/Menu/Item/Route.php create mode 100644 src/Menu/Item/Url.php create mode 100644 src/Menu/ItemCollection.php create mode 100644 tests/MenuComposerTest.php diff --git a/src/Access/BasicAccessHandler.php b/src/Access/BasicAccessHandler.php index 399380d..99360b8 100644 --- a/src/Access/BasicAccessHandler.php +++ b/src/Access/BasicAccessHandler.php @@ -72,13 +72,6 @@ public function check(array $options) protected function checkGate($arguments) { - if ($this->gate == null) { - throw new MissingGateException( - 'You have to upgrade to Laravel 5.1.12 or superior' - .' to use the allows, checks or denies options' - ); - } - if (is_array($arguments)) { $ability = array_shift($arguments); } else { diff --git a/src/HtmlServiceProvider.php b/src/HtmlServiceProvider.php index c731d2d..c7cc07d 100644 --- a/src/HtmlServiceProvider.php +++ b/src/HtmlServiceProvider.php @@ -276,20 +276,12 @@ protected function registerMenuGenerator() $this->loadConfigurationOptions(); - $menu = new MenuGenerator( - $app['url'], - $app['config'], - $app->make(Theme::class) - ); + $menu = new MenuGenerator($app['url'], $app[Theme::class]); if ($this->options['control_access']) { $menu->setAccessHandler($app[AccessHandler::class]); } - if ($this->options['translate_texts']) { - $menu->setLang($app['translator']); - } - return $menu; }); } diff --git a/src/Menu/Item.php b/src/Menu/Item.php new file mode 100644 index 0000000..fc49b04 --- /dev/null +++ b/src/Menu/Item.php @@ -0,0 +1,62 @@ +text = $text; + $this->parameters = $parameters; + $this->secure = $secure; + } + + public abstract function url(); + + public function parameters(array $value) + { + $this->parameters = $value; + + return $this; + } + + public function secure(bool $value = true) + { + $this->secure = $value; + + return $this; + } + + public function classes($classes) + { + $this->class = Html::classes((array) $classes, false); + + return $this; + } + + public function active(bool $value = true) + { + $this->active = $value; + + return $this; + } + + public function submenu(Closure $setup) + { + $this->submenu = $setup; + + return $this; + } +} \ No newline at end of file diff --git a/src/Menu/Item/Action.php b/src/Menu/Item/Action.php new file mode 100644 index 0000000..082ba1b --- /dev/null +++ b/src/Menu/Item/Action.php @@ -0,0 +1,23 @@ +action = $action; + + parent::__construct($text, $parameters, $secure); + } + + public function url() + { + return app(UrlGenerator::class)->action($this->action, $this->parameters, $this->secure); + } +} \ No newline at end of file diff --git a/src/Menu/Item/RawUrl.php b/src/Menu/Item/RawUrl.php new file mode 100644 index 0000000..867e213 --- /dev/null +++ b/src/Menu/Item/RawUrl.php @@ -0,0 +1,23 @@ +url = $url; + + parent::__construct($text); + } + + public function url() + { + return $this->url; + } +} \ No newline at end of file diff --git a/src/Menu/Item/Route.php b/src/Menu/Item/Route.php new file mode 100644 index 0000000..9e47c4c --- /dev/null +++ b/src/Menu/Item/Route.php @@ -0,0 +1,26 @@ +route = $route; + + parent::__construct($text, $parameters); + } + + public function url() + { + return app(UrlGenerator::class)->route($this->route, $this->parameters, true); + } +} \ No newline at end of file diff --git a/src/Menu/Item/Url.php b/src/Menu/Item/Url.php new file mode 100644 index 0000000..d05166c --- /dev/null +++ b/src/Menu/Item/Url.php @@ -0,0 +1,37 @@ +path = $path; + $this->text = $text; + $this->parameters = $parameters; + $this->secure = $secure; + } + + public function parameters(array $value) + { + $this->parameters = $value; + } + + public function secure(bool $value = true) + { + $this->secure = $value; + } + + public function url() + { + return app(UrlGenerator::class)->to($this->path, $this->parameters, $this->secure); + } +} \ No newline at end of file diff --git a/src/Menu/ItemCollection.php b/src/Menu/ItemCollection.php new file mode 100644 index 0000000..78eb1d8 --- /dev/null +++ b/src/Menu/ItemCollection.php @@ -0,0 +1,67 @@ +defaultSecure = $defaultSecure; + } + + /** + * Add a menu item. + * + * @param \Styde\Html\Menu\Item $item + * @return \Styde\Html\Menu\Item + */ + public function add(Item $item): Item + { + $this->items[] = $item; + + return $item; + } + + public function raw(string $url, string $text) + { + return $this->add(new RawUrl($url, $text)); + } + + public function url(string $url, $text, $parameters = []) + { + return $this->add(new Url($url, $text, $parameters, $this->defaultSecure)); + } + + public function route(string $url, $text, $parameters = []) + { + return $this->add(new Route($url, $text, $parameters)); + } + + public function action(string $url, $text, $parameters = []) + { + return $this->add(new Action($url, $text, $parameters, $this->defaultSecure)); + } + + public function placeholder($text) + { + return $this->add(new RawUrl('#', $text)); + } + + public function submenu($text, Closure $setup) + { + return $this->add(new RawUrl('#', $text))->submenu($setup); + } + + public function getIterator() + { + return new ArrayIterator($this->items); + } +} diff --git a/src/Menu/Menu.php b/src/Menu/Menu.php index 43e6d9f..0da5f6f 100644 --- a/src/Menu/Menu.php +++ b/src/Menu/Menu.php @@ -48,11 +48,11 @@ class Menu implements Htmlable */ protected $dropDownClass = 'dropdown'; /** - * List of menu items + * Menu configuration callback. * - * @var array + * @var \Closure */ - protected $items; + protected $config; /** * Current item's id (active menu item), it will be obtained after the menu * is rendered. @@ -67,11 +67,11 @@ class Menu implements Htmlable */ protected $defaultSecure = false; /** - * Active URL (this will be taken from the Url::current method by default) + * Current URL (this will be taken from the Url::current method by default) * * @var string */ - protected $activeUrl; + protected $currentUrl; /** * Allow dynamic parameters for routes and actions. * @@ -94,14 +94,14 @@ class Menu implements Htmlable * * @param Url $url * @param Theme $theme - * @param $items + * @param $config */ - public function __construct(URL $url, Theme $theme, $items) + public function __construct(URL $url, Theme $theme, $config) { $this->url = $url; $this->theme = $theme; - $this->items = $items; - $this->activeUrl = $this->url->current(); + $this->config = $config; + $this->currentUrl = $this->url->current(); $this->baseUrl = $this->url->to(''); } @@ -218,9 +218,9 @@ public function setActiveUrlResolver(Closure $closure) * * @param $value */ - public function setActiveUrl($value) + public function setCurrentUrl($value) { - $this->activeUrl = $value; + $this->currentUrl = $value; } /** @@ -252,7 +252,7 @@ public function getCurrentId() */ public function getItems() { - return $this->generateItems($this->items); + return $this->buildItems($this->config); } public function checkAccess(array $options) @@ -275,264 +275,70 @@ public function checkAccess(array $options) * * This method will called itself if an item has a 'submenu' key. * - * @param array $items - * @param array|null $parentItem + * @param Closure $config + * @param \Styde\Html\Menu\Item|null $parentItem * @return array */ - protected function generateItems($items, &$parentItem = null) + protected function buildItems($config, $parentItem = null) { - foreach ($items as $id => &$values) { - $values = $this->setDefaultValues($id, $values); - - if (!$this->checkAccess($values)) { - unset($items[$id]); - continue; - } - - $values['title'] = $this->getTitle($id, $values['title']); - - $values['url'] = $this->generateUrl($values); + $items = new ItemCollection($this->defaultSecure); - if ($this->isActiveUrl($values)) { - $this->markAsActive($values, $parentItem); - $this->currentId = $id; - } + $config($items); - if (isset($values['submenu'])) { - $values['submenu'] = $this->generateItems($values['submenu'], $values); - } + //@TODO: add access handler back +// if (!$this->checkAccess($values)) { +// unset($items[$id]); +// continue; +// } - if ($values['active']) { - $values['class'] .= ' '.$this->activeClass; + foreach ($items as $item) { + if ($this->isActive($item)) { + $this->markAsActive($item, $parentItem); + $this->currentId = $item->id; } - if ($values['submenu']) { - $values['class'] .= ' '.$this->dropDownClass; + if ($item->submenu != null) { + $item->items = $this->buildItems($item->submenu, $item); } - - $values['class'] = trim($values['class']); - - unset( - $values['callback'], $values['logged'], $values['roles'], $values['secure'], - $values['params'], $values['route'], $values['action'], $values['full_url'], - $values['allows'], $values['check'], $values['denies'] - ); } return $items; } - /** - * Merge the default values for a menu item - * - * @param $id - * @param array $values - * @return array - */ - protected function setDefaultValues($id, array $values) - { - return array_merge([ - 'class' => '', - 'submenu' => null, - 'id' => $id, - 'active' => false - ], $values); - } - /** * Checks whether this is the current URL or not * - * @param array $values + * @param \Styde\Html\Menu\Item $item * @return bool */ - protected function isActiveUrl(array $values) + protected function isActive($item) { // Do we have a custom resolver? If so, use it: if($activeUrlResolver = $this->activeUrlResolver) { - return $activeUrlResolver($values); + return $activeUrlResolver($item); } // Otherwise use the default resolver: - if ($values['url'] != $this->baseUrl) { - return strpos($this->activeUrl, $values['url']) === 0; - } - - return $this->activeUrl === $this->baseUrl; - } - - /** - * Marks an item an it's optional parent item as active - * - * @param array $values - * @param array|null $parentItem - */ - protected function markAsActive(&$values, &$parentItem = null) - { - // Set this item as active - $values['active'] = true; - // If this is a submenu, set the parent's item as active as well - if ($parentItem != null) { - $parentItem['active'] = true; - } - } - - /** - * Returns the menu's title. The title is determined following this order: - * - * 1. If a title is set then it will be returned and used as the menu title. - * 2. If a translator is set this function will rely on the translateTitle - * method (see below). - * 3. Otherwise it will transform the item $key string to title format. - * - * @param $key - * @param $title - * @return string - */ - protected function getTitle($key, &$title) - { - if (isset($title)) { - return $title; - } - - if(!is_null($this->lang)) { - return $this->translateTitle($key); - } - - return Str::title($key); - } - - /** - * Translates and return a title for a menu item. - * - * This method will attempt to find a "menu.key_item" through the translator - * component. If no translation is found for this item, it will attempt to - * transform the item $key string to a title readable format. - * - * @param $key - * @return string - */ - protected function translateTitle($key) - { - $translation = $this->lang->get('menu.'.$key); - - if ($translation != 'menu.'.$key) { - return $translation; - } - - return Str::title($key); - } - - /** - * Retrieve a route or action name and its parameters - * - * If $params is a string, then it returns it as the name of the route or - * action and the parameters will be an empty array. - * - * If it is an array then it takes the first element as the name of the - * route or action and the other elements as the parameters. - * - * Then it will try to replace any dynamic parameters (relying on the - * replaceDynamicParameters method, see below) - * - * Finally it will return an array where the first value will be the name of - * the route or action and the second value will be the array of parameters. - * - * @param $params - * @return array - */ - protected function getRouteAndParameters($params) - { - if (is_string($params)) { - return [$params, []]; - } - - return [ - // The first position in the array is the route or action name - array_shift($params), - // After that they are parameters and they could be dynamic - $this->replaceDynamicParameters($params) - ]; - } - - /** - * Allows variable or dynamic parameters for all the menu's routes and URLs - * - * Just precede the parameter's name with ":" - * For example: :user_id - * - * This method will cycle through all the parameters and replace the dynamic - * ones with their corresponding values stored through the setParams and - * setParam methods, - * - * If a dynamic value is not found the literal value will be returned. - * - * @param array $params - * @return array - */ - protected function replaceDynamicParameters(array $params) - { - foreach ($params as &$param) { - if (strpos($param, ':') !== 0) { - continue; - } - $name = substr($param, 1); - if (isset($this->params[$name])) { - $param = $this->params[$name]; - } + if ($item->url() != $this->baseUrl) { + return strpos($this->currentUrl, $item->url()) === 0; } - return $params; + return $this->currentUrl === $this->baseUrl; } /** - * Generates the menu item URL, using any of the following options, in order: - * - * If you pass a 'full_url' key within the item configuration, in that case - * it will return it as the URL with no additional action. - * - * If you pass a 'url' key then it will call the Url::to method to complete - * the base URL, you can also specify a 'secure' key to indicate whether - * this URL should be secure or not. Otherwise the defaultSecure option will - * be used. + * Mark an item an it's optional parent item as active. * - * If you pass a 'route' key then it will call Url::route - * - * If you pass an 'action' it will call the Url::action method instead. - * - * If you need to pass parameters for the url, route or action, just specify - * an array where the first position will be the url, route or action name - * and the rest of the array will contain the parameters. You can specify - * dynamic parameters (see methods above). - * - * If none of these options are found then this function will simple return - * a placeholder (#). - * - * @param $values - * @return mixed + * @param \Styde\Html\Menu\Item $item + * @param \Styde\Html\Menu\Item|null $parent */ - protected function generateUrl($values) + protected function markAsActive($item, $parent = null) { - if (isset($values['full_url'])) { - return $values['full_url']; - } + $item->active(true); - if (isset($values['url'])) { - list($url, $params) = $this->getRouteAndParameters($values['url']); - $secure = isset($values['secure']) ? $values['secure'] : $this->defaultSecure; - return $this->url->to($url, $params, $secure); + if ($parent != null) { + $parent->active(true); } - - if (isset($values['route'])) { - list($route, $params) = $this->getRouteAndParameters($values['route']); - return $this->url->route($route, $params); - } - - if (isset($values['action'])) { - list($route, $params) = $this->getRouteAndParameters($values['action']); - return $this->url->action($route, $params); - } - - return '#'; } /** @@ -543,13 +349,10 @@ protected function generateUrl($values) */ public function render($customTemplate = null) { - $items = $this->generateItems($this->items); - - return $this->theme->render( - $customTemplate, - ['items' => $items, 'class' => $this->class], - 'menu' - ); + return $this->theme->render($customTemplate, [ + 'items' => $this->getItems(), + 'class' => $this->class + ], 'menu'); } /** diff --git a/src/Menu/MenuGenerator.php b/src/Menu/MenuGenerator.php index 659b7d2..d6951ae 100644 --- a/src/Menu/MenuGenerator.php +++ b/src/Menu/MenuGenerator.php @@ -19,27 +19,13 @@ class MenuGenerator * @var \Illuminate\Contracts\Routing\UrlGenerator */ protected $url; - /** - * Laravel or custom implementation to retrieve the menu items from a - * configuration file. - * - * @var \Illuminate\Contracts\Config\Repository - */ - protected $config; + /** * Class used to render the menu according to the current's theme. * * @var \Styde\Html\Theme */ protected $theme; - /** - * Optional class used to translate the messages, in case i18n is needed. - * This can be change in the configuration file (config/html.php) - * With the option translate_texts (true or false) - * - * @var \Illuminate\Translation\Translator - */ - protected $lang; /** * Store an optional custom active URL resolver. @@ -53,26 +39,14 @@ class MenuGenerator * This class is a factory that will allow us to generate different menus. * * @param UrlGenerator $url - * @param Config $config * @param Theme $theme */ - public function __construct(UrlGenerator $url, Config $config, Theme $theme) + public function __construct(UrlGenerator $url, Theme $theme) { $this->url = $url; - $this->config = $config; $this->theme = $theme; } - /** - * Set the translator object - * - * @param Lang $lang - */ - public function setLang(Lang $lang) - { - $this->lang = $lang; - } - /** * Set a custom callback to resolve the logic to determine if a URL is active or not. * @@ -89,31 +63,22 @@ public function setActiveUrlResolver(Closure $closure) * As the first argument you can send an array of items or reference a * configuration key where you can store the items array. * - * @param array|string $items array of items or a config file key - * @param string|null $classes main CSS classes for the menu - * - * @return \Styde\Html\Menu\Menu + * @param Closure $config + * @param string $classes main CSS classes for the menu + * @return Menu */ - public function make($items, $classes = null) + public function make(Closure $config, $classes = '') { - if (is_string($items)) { - $items = $this->config->get($items); - } + $menu = new Menu($this->url, $this->theme, $config); - $menu = new Menu($this->url, $this->theme, $items); - - if (!is_null($this->lang)) { - $menu->setLang($this->lang); + if ($classes != '') { + $menu->setClass($classes); } if (!is_null($this->accessHandler)) { $menu->setAccessHandler($this->accessHandler); } - if ($classes != null) { - $menu->setClass($classes); - } - if ($this->activeUrlResolver != null) { $menu->setActiveUrlResolver($this->activeUrlResolver); } diff --git a/tests/MenuComposerTest.php b/tests/MenuComposerTest.php new file mode 100644 index 0000000..98485a4 --- /dev/null +++ b/tests/MenuComposerTest.php @@ -0,0 +1,24 @@ +assertTrue(true); // @TODO: create menu composer + } +} + + +class MyMenuComposer +{ + public function setup($items) + { + $items->url('/', 'Home'); + $items->placeholder('About'); + $items->url('projects', 'Our projects'); + $items->url('contact-us', 'Contact'); + } +} \ No newline at end of file diff --git a/tests/MenuGeneratorTest.php b/tests/MenuGeneratorTest.php index 85f14ac..5a12655 100644 --- a/tests/MenuGeneratorTest.php +++ b/tests/MenuGeneratorTest.php @@ -13,48 +13,46 @@ class MenuGeneratorTest extends TestCase /** @test */ function it_render_menus() { - $items = [ - 'home' => ['url' => '/'], - 'about' => [], - 'projects' => ['title' => 'Our projects', 'url' => 'projects'], - 'contact' => ['url' => 'contact-us'], - ]; - - $this->assertTemplateMatches('menu/menu', Menu::make($items)); + $menu = Menu::make(function ($items) { + $items->url('/', 'Home'); + $items->placeholder('About us'); + $items->url('projects', 'Our projects'); + $items->url('contact-us', 'Contact us'); + }); + + $this->assertTemplateMatches('menu/menu', $menu); } /** @test */ - function it_generates_routes() + function it_generates_links_from_routes() { Route::get('dashboard', ['as' => 'dashboard']); - Route::get('edit_home', ['as' => 'pages.edit']); + Route::get('edit/{page}', ['as' => 'pages.edit']); - $items = [ - 'dashboard' => ['route' => 'dashboard'], - 'edit_home' => ['route' => ['pages.edit', 'home']], - ]; + $menu = Menu::make(function ($items) { + $items->route('dashboard', 'Dashboard'); + $items->route('pages.edit', 'Edit home', ['home']); + }); - $this->assertTemplateMatches('menu/routes', Menu::make($items)); + $this->assertTemplateMatches('menu/routes', $menu); } /** @test */ - function it_implements_routes_with_dynamic_parameters() + function it_generates_links_from_routes_with_parameters() { Route::get('account/{user_id}', ['as' => 'account']); Route::get('calendar/{year}/{month}/{day}', ['as' => 'calendar']); - $items = [ - 'account' => [ - 'route' => ['account', ':user_id'], - ], - 'calendar' => [ - 'route' => ['calendar', ':year', ':month', ':day'], - ], - ]; + // If you have external parameters, just pass them to the Closure and build the routes there. + $user_id = 20; + $year = 2015; + $month = 7; + $day = 11; - $menu = Menu::make($items) - ->setParams(['year' => 2015, 'month' => 07, 'day' => 11]) - ->setParam('user_id', 20); + $menu = Menu::make(function ($items) use ($user_id, $year, $month, $day) { + $items->route('account', 'Account')->parameters(compact('user_id')); + $items->route('calendar', 'Calendar')->parameters(compact('year', 'month', 'day')); + }); $this->assertTemplateMatches('menu/parameters', $menu); } @@ -62,6 +60,8 @@ function it_implements_routes_with_dynamic_parameters() /** @test */ function it_checks_for_access_using_the_access_handler_and_the_gate() { + $this->markTestIncomplete(); + $fakeUser = new class extends Model implements AuthenticatableInterface { use Authenticatable; }; @@ -100,19 +100,20 @@ function it_checks_for_access_using_the_access_handler_and_the_gate() /** @test */ function it_generates_submenus() { - $items = [ - 'home' => ['url' => '/'], - 'about' => [ - 'submenu' => [ - 'team' => [], - 'careers' => ['title' => 'Work with us'], - ], - ], - 'projects' => ['title' => 'Our projects', 'url' => 'projects'], - 'contact' => ['url' => 'contact-us'], - ]; + $menu = Menu::make(function ($items) { + $items->url('/', 'Home'); + + $items->submenu('About us', function ($items) { + $items->placeholder('Team'); + $items->url('careers', 'Work with us'); + }); + + $items->url('projects', 'Our projects'); + + $items->url('contact-us', 'Contact us'); + }); - $this->assertTemplateMatches('menu/submenu', Menu::make($items)); + $this->assertTemplateMatches('menu/submenu', $menu); } } diff --git a/tests/snapshots/bootstrap4/menu/menu.html b/tests/snapshots/bootstrap4/menu/menu.html index 5d5b3e4..d9e1350 100644 --- a/tests/snapshots/bootstrap4/menu/menu.html +++ b/tests/snapshots/bootstrap4/menu/menu.html @@ -1,22 +1,22 @@ + + + + + diff --git a/tests/snapshots/bootstrap4/menu/parameters.html b/tests/snapshots/bootstrap4/menu/parameters.html index 69fce69..945a4b9 100644 --- a/tests/snapshots/bootstrap4/menu/parameters.html +++ b/tests/snapshots/bootstrap4/menu/parameters.html @@ -1,12 +1,12 @@ + + + diff --git a/tests/snapshots/bootstrap4/menu/routes.html b/tests/snapshots/bootstrap4/menu/routes.html index 1aa9285..ea8c789 100644 --- a/tests/snapshots/bootstrap4/menu/routes.html +++ b/tests/snapshots/bootstrap4/menu/routes.html @@ -1,12 +1,14 @@ + + + + + diff --git a/tests/snapshots/bootstrap4/menu/submenu.html b/tests/snapshots/bootstrap4/menu/submenu.html index 44ccc60..64b22d5 100644 --- a/tests/snapshots/bootstrap4/menu/submenu.html +++ b/tests/snapshots/bootstrap4/menu/submenu.html @@ -1,26 +1,26 @@ + + + + + diff --git a/themes/bootstrap4/menu.blade.php b/themes/bootstrap4/menu.blade.php index 890048e..ea86616 100644 --- a/themes/bootstrap4/menu.blade.php +++ b/themes/bootstrap4/menu.blade.php @@ -1,19 +1,19 @@
    @foreach ($items as $item) - @if (empty($item['submenu'])) - @else - From 08cfeec92bbf5f1fbfc73dde0b5447b21d62b63a Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Sat, 10 Feb 2018 15:24:26 +0000 Subject: [PATCH 022/217] backup --- src/Access/AuthAccess.php | 11 +++++++++++ tests/MenuGeneratorTest.php | 30 +++++++++++++----------------- 2 files changed, 24 insertions(+), 17 deletions(-) create mode 100644 src/Access/AuthAccess.php diff --git a/src/Access/AuthAccess.php b/src/Access/AuthAccess.php new file mode 100644 index 0000000..f4d4923 --- /dev/null +++ b/src/Access/AuthAccess.php @@ -0,0 +1,11 @@ +markTestIncomplete(); - $fakeUser = new class extends Model implements AuthenticatableInterface { use Authenticatable; }; @@ -78,21 +76,19 @@ function it_checks_for_access_using_the_access_handler_and_the_gate() return false; }); - $items = array( - 'view-post' => [ - ], - 'edit-post' => [ - 'allows' => ['update-post', ':post'] - ], - 'review-post' => [ - 'denies' => ['update-post', ':post'] - ], - 'delete-post' => [ - 'allows' => 'delete-post' - ] - ); - - $menu = Menu::make($items)->setParam('post', $fakePost); + $menu = Menu::make(function ($items) use ($fakePost) { + $items->url('view-post'); + + $items->url('edit-post')->ifCan('update', $fakePost); + + $items->url('review-post')->ifCan('update', $fakePost); + + $items->url('delete-post')->ifIs('admin'); + + $items->route('logout')->ifAuth(); + + $items->route('sign-in')->ifGuest(); + }); $this->assertTemplateMatches('menu/access-handler', $menu); } From 91ce8e501735669abc961a78b328caadeab7f625 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Mon, 12 Feb 2018 10:16:40 +0000 Subject: [PATCH 023/217] exclude items from the menu based on auth / gate conditions --- src/Menu/Item.php | 39 ++++++++++++++++++- src/Menu/ItemCollection.php | 16 +++++++- src/Menu/Menu.php | 10 +---- tests/MenuGeneratorTest.php | 28 +++++++++---- .../bootstrap4/menu/access-handler.html | 18 +++++++-- 5 files changed, 89 insertions(+), 22 deletions(-) diff --git a/src/Menu/Item.php b/src/Menu/Item.php index fc49b04..b145e26 100644 --- a/src/Menu/Item.php +++ b/src/Menu/Item.php @@ -3,6 +3,8 @@ namespace Styde\Html\Menu; use Closure; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Gate; use Styde\Html\Facades\Html; abstract class Item @@ -15,6 +17,7 @@ abstract class Item public $active = false; public $submenu; public $items = []; + public $included = true; public function __construct(string $text, array $parameters = [], bool $secure = true) { @@ -59,4 +62,38 @@ public function submenu(Closure $setup) return $this; } -} \ No newline at end of file + + public function include(bool $value = true) + { + $this->included = $value; + + return $this; + } + + public function ifAuth() + { + return $this->include(Auth::check()); + } + + public function ifGuest() + { + return $this->include(Auth::guest()); + } + + public function ifCan($ability, $arguments = []) + { + return $this->include(Gate::allows($ability, $arguments)); + } + + public function ifCannot($ability, $arguments = []) + { + return $this->include(Gate::denies($ability, $arguments)); + } + + public function ifIs($role) + { + $user = Auth::user(); + + return $this->include($user && $user->isA($role)); + } +} diff --git a/src/Menu/ItemCollection.php b/src/Menu/ItemCollection.php index 78eb1d8..04c0e2d 100644 --- a/src/Menu/ItemCollection.php +++ b/src/Menu/ItemCollection.php @@ -12,9 +12,23 @@ class ItemCollection implements IteratorAggregate public $items = []; public $defaultSecure; - public function __construct(bool $defaultSecure = true) + public function __construct($config, bool $defaultSecure = true) { $this->defaultSecure = $defaultSecure; + + $config($this); + + $this->removeExcluded(); + } + + /** + * Remove the excluded items. + */ + public function removeExcluded() + { + $this->items = array_filter($this->items, function ($item) { + return $item->included; + }); } /** diff --git a/src/Menu/Menu.php b/src/Menu/Menu.php index 0da5f6f..59dd95a 100644 --- a/src/Menu/Menu.php +++ b/src/Menu/Menu.php @@ -281,15 +281,7 @@ public function checkAccess(array $options) */ protected function buildItems($config, $parentItem = null) { - $items = new ItemCollection($this->defaultSecure); - - $config($items); - - //@TODO: add access handler back -// if (!$this->checkAccess($values)) { -// unset($items[$id]); -// continue; -// } + $items = new ItemCollection($config, $this->defaultSecure); foreach ($items as $item) { if ($this->isActive($item)) { diff --git a/tests/MenuGeneratorTest.php b/tests/MenuGeneratorTest.php index a444737..8de392c 100644 --- a/tests/MenuGeneratorTest.php +++ b/tests/MenuGeneratorTest.php @@ -58,10 +58,18 @@ function it_generates_links_from_routes_with_parameters() } /** @test */ - function it_checks_for_access_using_the_access_handler_and_the_gate() + function it_can_exclude_items_based_on_permissions() { + Route::get('/login', ['as' => 'login']); + Route::get('/logout', ['as' => 'logout']); + $fakeUser = new class extends Model implements AuthenticatableInterface { use Authenticatable; + + public function isA($role) + { + return $role == 'admin'; + } }; $fakePost = new class { public $id = 1; }; @@ -77,17 +85,23 @@ function it_checks_for_access_using_the_access_handler_and_the_gate() }); $menu = Menu::make(function ($items) use ($fakePost) { - $items->url('view-post'); + // Should be included always. + $items->url('posts/1', 'View post'); - $items->url('edit-post')->ifCan('update', $fakePost); + // Should be included. + $items->url('posts/1/edit', 'Edit post')->ifCan('update-post', $fakePost); - $items->url('review-post')->ifCan('update', $fakePost); + // Should be excluded. + $items->url('posts/1/suggest-changes', 'Suggest changes')->ifCannot('update-post', $fakePost); - $items->url('delete-post')->ifIs('admin'); + // Should be included: the fake user is an admin. + $items->url('posts/1/publish', 'Publish post')->ifIs('admin'); - $items->route('logout')->ifAuth(); + // Should be included: the user is authenticated. + $items->route('logout', 'Logout')->ifAuth(); - $items->route('sign-in')->ifGuest(); + // Should be excluded: the user is not a guest. + $items->route('login', 'Sign in')->ifGuest(); }); $this->assertTemplateMatches('menu/access-handler', $menu); diff --git a/tests/snapshots/bootstrap4/menu/access-handler.html b/tests/snapshots/bootstrap4/menu/access-handler.html index 8a38457..08b3856 100644 --- a/tests/snapshots/bootstrap4/menu/access-handler.html +++ b/tests/snapshots/bootstrap4/menu/access-handler.html @@ -1,12 +1,22 @@ From 701f43e6d157016f8608cf2d02356930ff193174 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Mon, 12 Feb 2018 10:41:34 +0000 Subject: [PATCH 024/217] Delete the default secure parameter --- src/Menu/Item.php | 19 +------------------ src/Menu/Item/Action.php | 24 +++++++++++++++++++++--- src/Menu/Item/RawUrl.php | 4 ++-- src/Menu/Item/Route.php | 15 +++++++++++---- src/Menu/Item/Url.php | 12 ++++++------ src/Menu/ItemCollection.php | 9 +++------ src/Menu/Menu.php | 29 ++++++++++------------------- src/Menu/MenuGenerator.php | 2 +- tests/MenuComposerTest.php | 24 ------------------------ 9 files changed, 55 insertions(+), 83 deletions(-) delete mode 100644 tests/MenuComposerTest.php diff --git a/src/Menu/Item.php b/src/Menu/Item.php index b145e26..eb0310d 100644 --- a/src/Menu/Item.php +++ b/src/Menu/Item.php @@ -10,7 +10,6 @@ abstract class Item { public $text; - public $parameters; public $secure; public $id; public $class = ''; @@ -19,29 +18,13 @@ abstract class Item public $items = []; public $included = true; - public function __construct(string $text, array $parameters = [], bool $secure = true) + public function __construct(string $text) { $this->text = $text; - $this->parameters = $parameters; - $this->secure = $secure; } public abstract function url(); - public function parameters(array $value) - { - $this->parameters = $value; - - return $this; - } - - public function secure(bool $value = true) - { - $this->secure = $value; - - return $this; - } - public function classes($classes) { $this->class = Html::classes((array) $classes, false); diff --git a/src/Menu/Item/Action.php b/src/Menu/Item/Action.php index 082ba1b..9134fbf 100644 --- a/src/Menu/Item/Action.php +++ b/src/Menu/Item/Action.php @@ -8,16 +8,34 @@ class Action extends Item { public $action; + public $parameters; + public $secure; - public function __construct(string $action, string $text, array $parameters = [], bool $secure = true) + public function __construct(string $action, string $text, array $parameters = [], $secure = null) { + parent::__construct($text); + $this->action = $action; + $this->parameters = $parameters; + $this->secure = $secure; + } + + public function parameters(array $value) + { + $this->parameters = $value; + + return $this; + } + + public function secure($value = true) + { + $this->secure = $value; - parent::__construct($text, $parameters, $secure); + return $this; } public function url() { return app(UrlGenerator::class)->action($this->action, $this->parameters, $this->secure); } -} \ No newline at end of file +} diff --git a/src/Menu/Item/RawUrl.php b/src/Menu/Item/RawUrl.php index 867e213..7fba8d4 100644 --- a/src/Menu/Item/RawUrl.php +++ b/src/Menu/Item/RawUrl.php @@ -11,9 +11,9 @@ class RawUrl extends Item public function __construct(string $url, string $text) { - $this->url = $url; - parent::__construct($text); + + $this->url = $url; } public function url() diff --git a/src/Menu/Item/Route.php b/src/Menu/Item/Route.php index 9e47c4c..15ab2ba 100644 --- a/src/Menu/Item/Route.php +++ b/src/Menu/Item/Route.php @@ -10,17 +10,24 @@ class Route extends Item public $route; public $text; public $parameters; - public $secure; public function __construct(string $route, string $text, array $parameters = []) { + parent::__construct($text); + $this->route = $route; + $this->parameters = $parameters; + } + + public function parameters(array $value) + { + $this->parameters = $value; - parent::__construct($text, $parameters); + return $this; } public function url() { - return app(UrlGenerator::class)->route($this->route, $this->parameters, true); + return app(UrlGenerator::class)->route($this->route, $this->parameters); } -} \ No newline at end of file +} diff --git a/src/Menu/Item/Url.php b/src/Menu/Item/Url.php index d05166c..a8f0cd8 100644 --- a/src/Menu/Item/Url.php +++ b/src/Menu/Item/Url.php @@ -8,16 +8,16 @@ class Url extends Item { public $path; - public $text; public $parameters; public $secure; - public function __construct(string $path, string $text, array $parameters, bool $secure) + public function __construct(string $path, string $text, array $parameters = [], $secure = null) { + parent::__construct($text); + $this->path = $path; - $this->text = $text; - $this->parameters = $parameters; $this->secure = $secure; + $this->parameters = $parameters; } public function parameters(array $value) @@ -25,7 +25,7 @@ public function parameters(array $value) $this->parameters = $value; } - public function secure(bool $value = true) + public function secure($value = true) { $this->secure = $value; } @@ -34,4 +34,4 @@ public function url() { return app(UrlGenerator::class)->to($this->path, $this->parameters, $this->secure); } -} \ No newline at end of file +} diff --git a/src/Menu/ItemCollection.php b/src/Menu/ItemCollection.php index 04c0e2d..dc69406 100644 --- a/src/Menu/ItemCollection.php +++ b/src/Menu/ItemCollection.php @@ -10,12 +10,9 @@ class ItemCollection implements IteratorAggregate { public $items = []; - public $defaultSecure; - public function __construct($config, bool $defaultSecure = true) + public function __construct($config) { - $this->defaultSecure = $defaultSecure; - $config($this); $this->removeExcluded(); @@ -51,7 +48,7 @@ public function raw(string $url, string $text) public function url(string $url, $text, $parameters = []) { - return $this->add(new Url($url, $text, $parameters, $this->defaultSecure)); + return $this->add(new Url($url, $text, $parameters)); } public function route(string $url, $text, $parameters = []) @@ -61,7 +58,7 @@ public function route(string $url, $text, $parameters = []) public function action(string $url, $text, $parameters = []) { - return $this->add(new Action($url, $text, $parameters, $this->defaultSecure)); + return $this->add(new Action($url, $text, $parameters)); } public function placeholder($text) diff --git a/src/Menu/Menu.php b/src/Menu/Menu.php index 59dd95a..242616b 100644 --- a/src/Menu/Menu.php +++ b/src/Menu/Menu.php @@ -21,38 +21,45 @@ class Menu implements Htmlable * @var \Illuminate\Contracts\Routing\UrlGenerator */ protected $url; + /** * @var \Styde\Html\Theme */ protected $theme; + /** * @var \Illuminate\Translation\Translator */ protected $lang; + /** * Default CSS class(es) for the menu * * @var string */ protected $class = 'nav'; + /** * Default CSS class(es) for the active item(s) * * @var string */ protected $activeClass = 'active'; + /** * Default CSS class(es) for the sub-menus * * @var string */ protected $dropDownClass = 'dropdown'; + /** * Menu configuration callback. * * @var \Closure */ protected $config; + /** * Current item's id (active menu item), it will be obtained after the menu * is rendered. @@ -60,18 +67,14 @@ class Menu implements Htmlable * @var string */ protected $currentId; - /** - * Whether all URLs should be secure (https) or not (http) by default - * - * @var bool - */ - protected $defaultSecure = false; + /** * Current URL (this will be taken from the Url::current method by default) * * @var string */ protected $currentUrl; + /** * Allow dynamic parameters for routes and actions. * @@ -191,18 +194,6 @@ public function setDropDownClass($value) return $this; } - /** - * Set whether all URLs should be secure (https) by default or not (http) - * - * @param $value - * @return \Styde\Html\Menu\Menu $this - */ - public function setDefaultSecure($value) - { - $this->defaultSecure = $value; - return $this; - } - /** * Set a custom callback to resolve the logic to determine if a URL is active or not. * @@ -281,7 +272,7 @@ public function checkAccess(array $options) */ protected function buildItems($config, $parentItem = null) { - $items = new ItemCollection($config, $this->defaultSecure); + $items = new ItemCollection($config); foreach ($items as $item) { if ($this->isActive($item)) { diff --git a/src/Menu/MenuGenerator.php b/src/Menu/MenuGenerator.php index d6951ae..ef4b1ee 100644 --- a/src/Menu/MenuGenerator.php +++ b/src/Menu/MenuGenerator.php @@ -75,7 +75,7 @@ public function make(Closure $config, $classes = '') $menu->setClass($classes); } - if (!is_null($this->accessHandler)) { + if ($this->accessHandler != null) { $menu->setAccessHandler($this->accessHandler); } diff --git a/tests/MenuComposerTest.php b/tests/MenuComposerTest.php deleted file mode 100644 index 98485a4..0000000 --- a/tests/MenuComposerTest.php +++ /dev/null @@ -1,24 +0,0 @@ -assertTrue(true); // @TODO: create menu composer - } -} - - -class MyMenuComposer -{ - public function setup($items) - { - $items->url('/', 'Home'); - $items->placeholder('About'); - $items->url('projects', 'Our projects'); - $items->url('contact-us', 'Contact'); - } -} \ No newline at end of file From 003eab342e6686a8cc23ab5a2f5b4a0ad063c0aa Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Mon, 12 Feb 2018 12:00:38 +0000 Subject: [PATCH 025/217] Add package autodiscovery, config aliases in composer.json, add helper html_classes --- composer.json | 14 +++++++++ src/HtmlServiceProvider.php | 29 ------------------- src/helpers.php | 18 ++++++++++-- themes/bootstrap4/fields/checkbox.blade.php | 2 +- .../bootstrap4/fields/collections.blade.php | 2 +- themes/bootstrap4/fields/default.blade.php | 2 +- themes/bootstrap4/menu.blade.php | 4 +-- 7 files changed, 35 insertions(+), 36 deletions(-) diff --git a/composer.json b/composer.json index 1897911..1b5d1b2 100644 --- a/composer.json +++ b/composer.json @@ -29,5 +29,19 @@ "Styde\\Html\\Tests\\": "tests/" } }, + "extra": { + "laravel": { + "providers": [ + "Styde\\Html\\HtmlServiceProvider" + ], + "aliases": { + "Field": "Styde\\Html\\Facades\\Field", + "Alert": "Styde\\Html\\Facades\\Alert", + "Menu": "Styde\\Html\\Facades\\Menu", + "Form": "Styde\\Html\\Facades\\Form", + "Html": "Styde\\Html\\Facades\\Html" + } + } + }, "minimum-stability": "stable" } diff --git a/src/HtmlServiceProvider.php b/src/HtmlServiceProvider.php index c7cc07d..2cbcd0c 100644 --- a/src/HtmlServiceProvider.php +++ b/src/HtmlServiceProvider.php @@ -5,7 +5,6 @@ use Styde\Html\Menu\Menu; use Styde\Html\Menu\MenuGenerator; use Styde\Html\Access\AccessHandler; -use Illuminate\Foundation\AliasLoader; use Illuminate\Support\ServiceProvider; use Styde\Html\Alert\Container as Alert; use Styde\Html\Access\BasicAccessHandler; @@ -38,17 +37,6 @@ class HtmlServiceProvider extends ServiceProvider * @var bool */ protected $defer = false; - /** - * Automatically assign the global aliases for the component's facades - * @var bool - */ - protected $globalAliases = [ - 'Field' => \Styde\Html\Facades\Field::class, - 'Alert' => \Styde\Html\Facades\Alert::class, - 'Menu' => \Styde\Html\Facades\Menu::class, - 'Form' => \Styde\Html\Facades\Form::class, - 'Html' => \Styde\Html\Facades\Html::class, - ]; public function boot() { @@ -91,10 +79,6 @@ public function register() $this->registerAlertMiddleware(); $this->registerMenuGenerator(); - - if (!empty ($this->globalAliases)) { - $this->registerFacadeAliases(); - } } /** @@ -286,19 +270,6 @@ protected function registerMenuGenerator() }); } - /** - * Register the facade aliases for this component - */ - protected function registerFacadeAliases() - { - $this->app->booting(function () { - $loader = AliasLoader::getInstance(); - foreach ($this->globalAliases as $alias => $facade) { - $loader->alias($alias, $facade); - } - }); - } - /** * Get the services provided by the provider. * diff --git a/src/helpers.php b/src/helpers.php index 7bd1fa1..a8aa6af 100644 --- a/src/helpers.php +++ b/src/helpers.php @@ -4,7 +4,7 @@ * Convenient helpers in case you prefer to use them instead of the facades */ -if (!function_exists('alert')) { +if (! function_exists('alert')) { /** * Creates a new alert message (alias of Alert::make) * @@ -18,7 +18,7 @@ function alert($message = '', $type = 'success', $args = []) { } } -if(!function_exists('menu')) { +if(! function_exists('menu')) { /** * Generates a new menu (alias of Menu::make) * @@ -29,4 +29,18 @@ function alert($message = '', $type = 'success', $args = []) { function menu($items, $classes = null) { return App::make('menu')->make($items, $classes); } +} + +if(! function_exists('html_classes')) { + /** + * Builds an HTML class attribute dynamically. + * + * @param array $classes + * @param bool $addClassAttribute + * @return string + */ + function html_classes(array $classes, $addClassAttribute = true) + { + return app('html')->classes($classes, $addClassAttribute); + } } \ No newline at end of file diff --git a/themes/bootstrap4/fields/checkbox.blade.php b/themes/bootstrap4/fields/checkbox.blade.php index 8802eee..d9c54bf 100644 --- a/themes/bootstrap4/fields/checkbox.blade.php +++ b/themes/bootstrap4/fields/checkbox.blade.php @@ -1,6 +1,6 @@
    {{ $input->classes(['form-check-input', 'is-invalid' => $hasErrors]) }} - $hasErrors]) !!} for="{{ $id }}"> + $hasErrors]) !!} for="{{ $id }}"> {{ $label }} @if ($required) Required diff --git a/themes/bootstrap4/fields/collections.blade.php b/themes/bootstrap4/fields/collections.blade.php index d28712d..597d37b 100644 --- a/themes/bootstrap4/fields/collections.blade.php +++ b/themes/bootstrap4/fields/collections.blade.php @@ -1,4 +1,4 @@ - $hasErrors]) !!}> + $hasErrors]) !!}> {{ $label }} @if ($required) Required diff --git a/themes/bootstrap4/fields/default.blade.php b/themes/bootstrap4/fields/default.blade.php index d04c120..de32d1b 100644 --- a/themes/bootstrap4/fields/default.blade.php +++ b/themes/bootstrap4/fields/default.blade.php @@ -1,5 +1,5 @@
    -
    +
    + + +
    diff --git a/tests/snapshots/bootstrap4/form-model/login-form.html b/tests/snapshots/bootstrap4/form-model/login-form.html new file mode 100644 index 0000000..14e4645 --- /dev/null +++ b/tests/snapshots/bootstrap4/form-model/login-form.html @@ -0,0 +1,22 @@ +
    +
    + + +
    +
    + + +
    +
    + + +
    + +auth.forgot_link +
    diff --git a/themes/bootstrap4/fields/checkbox.blade.php b/themes/bootstrap4/fields/checkbox.blade.php index d9c54bf..55a19bc 100644 --- a/themes/bootstrap4/fields/checkbox.blade.php +++ b/themes/bootstrap4/fields/checkbox.blade.php @@ -1,6 +1,6 @@
    {{ $input->classes(['form-check-input', 'is-invalid' => $hasErrors]) }} - $hasErrors]) !!} for="{{ $id }}"> + $hasErrors]) !!} for="{{ $id }}"> {{ $label }} @if ($required) Required From 2ed1bc3902cc786682322e55383aac9c5b245abb Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Wed, 14 Feb 2018 19:51:21 +0000 Subject: [PATCH 032/217] simplify code --- tests/FormModelTest.php | 4 +--- tests/snapshots/bootstrap4/form-model/login-form.html | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index d2d5ba8..dff6b26 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -24,9 +24,7 @@ class LoginForm extends FormModel public function setup() { - $this->form->route('login') - ->method('POST') - ->attr('role', 'form'); + $this->form->route('login')->role('form'); $this->fields->email('email'); $this->fields->password('password'); diff --git a/tests/snapshots/bootstrap4/form-model/login-form.html b/tests/snapshots/bootstrap4/form-model/login-form.html index 14e4645..ac792da 100644 --- a/tests/snapshots/bootstrap4/form-model/login-form.html +++ b/tests/snapshots/bootstrap4/form-model/login-form.html @@ -1,4 +1,4 @@ -
    +
    diff --git a/tests/snapshots/bootstrap4/field/select.html b/tests/snapshots/bootstrap4/field/select.html index a4ec7db..e992848 100644 --- a/tests/snapshots/bootstrap4/field/select.html +++ b/tests/snapshots/bootstrap4/field/select.html @@ -2,5 +2,5 @@ - +
    From 381ba56016b07b704664e6c2fe87984119e47262 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Mon, 5 Mar 2018 17:35:25 +0000 Subject: [PATCH 046/217] Make request parameter optional --- src/FormModel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/FormModel.php b/src/FormModel.php index 47d7ee0..f848ab3 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -99,9 +99,9 @@ public function render($customTemplate = null) ], 'form'); } - public function validate(Request $request) + public function validate(Request $request = null) { - return $request->validate($this->getValidationRules()); + return ($request ?: request())->validate($this->getValidationRules()); } public function getValidationRules() From 3474e329b89c0603d410c47ef327a59b8f855104 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 6 Mar 2018 15:59:37 +0000 Subject: [PATCH 047/217] progress --- src/FieldBuilder.php | 28 ++++++-------- src/FormBuilder.php | 2 +- src/FormModel.php | 4 +- src/FormModel/Field.php | 81 +++++++++++++++++++++-------------------- src/HandlesAccess.php | 44 ++++++++++++++++++++++ src/HtmlBuilder.php | 9 +++-- src/Menu/Item.php | 40 ++------------------ 7 files changed, 110 insertions(+), 98 deletions(-) create mode 100644 src/HandlesAccess.php diff --git a/src/FieldBuilder.php b/src/FieldBuilder.php index 939aa72..6c777c8 100644 --- a/src/FieldBuilder.php +++ b/src/FieldBuilder.php @@ -361,38 +361,34 @@ public function build($type, $name, $value = null, array $attributes = [], array */ public function render(Field $field) { - $attributes = $field->getAttributes(); - - if (! $this->checkAccess($attributes)) { + if (! $this->checkAccess($field->attributes)) { return ''; } - $name = $field->getName(); - - $required = $this->getRequired($attributes); + $required = $this->getRequired($field->attributes); - $label = $field->getLabel(); + $label = $field->label; if ($label == null) { - $label = $this->getDefaultLabel($name); + $label = $this->getDefaultLabel($field->name); } - $htmlName = $this->getHtmlName($name); - $id = $this->getHtmlId($name, $attributes); + $htmlName = $this->getHtmlName($field->name); + $id = $this->getHtmlId($field->name, $field->attributes); $errors = $this->getControlErrors($id); $hasErrors = !empty($errors); - $type = $field->getType(); - - $input = $this->buildControl($type, $name, $field->getValue(), $attributes, $field->getOptions(), $htmlName); + $input = $this->buildControl( + $field->type, $field->name, $field->value, $field->attributes, $field->getOptions(), $htmlName + ); return $this->theme->render( - $field->getTemplate(), + $field->template, array_merge( - $field->getExtra(), + $field->extra, compact('htmlName', 'id', 'label', 'input', 'errors', 'hasErrors', 'required') ), - 'fields.'.$this->getDefaultTemplate($type) + 'fields.'.$this->getDefaultTemplate($field->type) ); } diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 2522bd3..bdcf4e8 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -55,7 +55,7 @@ public function __construct(UrlGenerator $url, Theme $theme, $session) */ public function getModel() { - return $this->model; + return $this->currentModel; } /** diff --git a/src/FormModel.php b/src/FormModel.php index f848ab3..9d31ec4 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -109,7 +109,9 @@ public function getValidationRules() $rules = []; foreach ($this->fields->all() as $name => $field) { - $rules[$name] = $field->getValidationRules(); + if ($field->included) { + $rules[$name] = $field->getValidationRules(); + } } return $rules; diff --git a/src/FormModel/Field.php b/src/FormModel/Field.php index d05d265..a955276 100644 --- a/src/FormModel/Field.php +++ b/src/FormModel/Field.php @@ -2,13 +2,15 @@ namespace Styde\Html\FormModel; -use Illuminate\Validation\Rule; use Styde\Html\FieldBuilder; +use Styde\Html\HandlesAccess; +use Illuminate\Validation\Rule; +use Illuminate\Support\Facades\DB; use Illuminate\Contracts\Support\Htmlable; class Field implements Htmlable { - use HasAttributes; + use HasAttributes, HandlesAccess; /** * @var \Styde\Html\FieldBuilder @@ -17,36 +19,44 @@ class Field implements Htmlable /** * @var string */ - protected $name; + public $name; /** * @var string */ - protected $type; + public $type; /** * @var mixed */ - protected $value; + public $value; /** * @var string */ - protected $label; + public $label; /** * @var template */ - protected $template; + public $template; /** * @var array */ - protected $attributes = []; + public $attributes = []; /** * @var array */ - protected $extra = []; + public $extra = []; /** * @var array */ protected $options = []; + protected $table; + + protected $tableText; + + protected $tableId; + + protected $query; + public function __construct(FieldBuilder $fieldBuilder, $name, $type = 'text') { $this->fieldBuilder = $fieldBuilder; @@ -104,49 +114,36 @@ public function options($options) return $this; } - public function getType() + public function from($table, $text, $id = 'id', $query = null) { - return $this->type; - } + $this->table = $table; + $this->tableText = $text; + $this->tableId = $id; + $this->query = $query; - public function getName() - { - return $this->name; + return $this; } - - public function getValue() + + public function getOptions() { - return $this->value; - } + if ($this->table) { + $query = DB::table($this->table); - public function getAttributes() - { - return $this->attributes; - } + if ($this->query) { + call_user_func($this->query, $query); + } - public function getExtra() - { - return $this->extra; - } + return $query->pluck($this->tableText, $this->tableId)->all(); + } - public function getOptions() - { return $this->options; } - public function getTemplate() - { - return $this->template; - } - - public function getLabel() - { - return $this->label; - } - public function render() { - return $this->fieldBuilder->render($this); + if ($this->included) { + return $this->fieldBuilder->render($this); + } } public function toHtml() @@ -174,6 +171,10 @@ public function getValidationRules() } } + if ($this->table) { + $rules[] = Rule::exists($this->table, $this->tableId)->where($this->query); + } + return $rules; } } diff --git a/src/HandlesAccess.php b/src/HandlesAccess.php new file mode 100644 index 0000000..c20f259 --- /dev/null +++ b/src/HandlesAccess.php @@ -0,0 +1,44 @@ +included = $value; + + return $this; + } + + public function ifAuth() + { + return $this->includeIf(Auth::check()); + } + + public function ifGuest() + { + return $this->includeIf(Auth::guest()); + } + + public function ifCan($ability, $arguments = []) + { + return $this->includeIf(Gate::allows($ability, $arguments)); + } + + public function ifCannot($ability, $arguments = []) + { + return $this->includeIf(Gate::denies($ability, $arguments)); + } + + public function ifIs($role) + { + $user = Auth::user(); + + return $this->includeIf($user && $user->isA($role)); + } +} diff --git a/src/HtmlBuilder.php b/src/HtmlBuilder.php index 55a6553..9bfc294 100644 --- a/src/HtmlBuilder.php +++ b/src/HtmlBuilder.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\Routing\UrlGenerator; +use Illuminate\Support\HtmlString; class HtmlBuilder { @@ -59,11 +60,13 @@ public function classes(array $classes, $addClassAttribute = true) return ''; } + $html = trim($html); + if ($addClassAttribute) { - return ' class="'.trim($html).'"'; - } else { - return trim($html); + $html = ' class="'.$html.'"'; } + + return new HtmlString($html); } /** diff --git a/src/Menu/Item.php b/src/Menu/Item.php index 4ef4077..b12e2c4 100644 --- a/src/Menu/Item.php +++ b/src/Menu/Item.php @@ -3,12 +3,13 @@ namespace Styde\Html\Menu; use Closure; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Gate; use Styde\Html\Facades\Html; +use Styde\Html\HandlesAccess; abstract class Item { + use HandlesAccess; + public $text; public $secure; public $id; @@ -16,7 +17,6 @@ abstract class Item public $active = false; public $submenu; public $items = []; - public $included = true; public $extra = []; public $parent; @@ -52,40 +52,6 @@ public function submenu(Closure $setup) return $this; } - public function include(bool $value = true) - { - $this->included = $value; - - return $this; - } - - public function ifAuth() - { - return $this->include(Auth::check()); - } - - public function ifGuest() - { - return $this->include(Auth::guest()); - } - - public function ifCan($ability, $arguments = []) - { - return $this->include(Gate::allows($ability, $arguments)); - } - - public function ifCannot($ability, $arguments = []) - { - return $this->include(Gate::denies($ability, $arguments)); - } - - public function ifIs($role) - { - $user = Auth::user(); - - return $this->include($user && $user->isA($role)); - } - public function __call($method, array $parameters) { $this->extra[$method] = $parameters[0] ?? true; From 73eb03a582bec74258aa9a4ebc8c9aee25962b9e Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 6 Mar 2018 18:16:43 +0000 Subject: [PATCH 048/217] Add menu composers --- src/Menu/MenuComposer.php | 27 +++++++++++++++++++++++++++ tests/MenuGeneratorTest.php | 20 +++++++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/Menu/MenuComposer.php diff --git a/src/Menu/MenuComposer.php b/src/Menu/MenuComposer.php new file mode 100644 index 0000000..0a994b1 --- /dev/null +++ b/src/Menu/MenuComposer.php @@ -0,0 +1,27 @@ +compose($items); + }); + + return $menu->render($this->template); + } + + abstract public function compose(ItemCollection $items); + + public function toHtml() + { + return $this->render(); + } +} \ No newline at end of file diff --git a/tests/MenuGeneratorTest.php b/tests/MenuGeneratorTest.php index 71a436a..0d07e97 100644 --- a/tests/MenuGeneratorTest.php +++ b/tests/MenuGeneratorTest.php @@ -8,6 +8,8 @@ use Illuminate\Support\Facades\{Auth, Gate, Route}; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableInterface; use Styde\Html\Menu\Item\Url; +use Styde\Html\Menu\ItemCollection; +use Styde\Html\Menu\MenuComposer; class MenuGeneratorTest extends TestCase { @@ -237,6 +239,22 @@ function menu_items_can_have_extra_classes() $item->classes(['font-weight-bold', 'text-primary']); - return $this->assertSame('font-weight-bold text-primary', $item->class); + return $this->assertSame('font-weight-bold text-primary', $item->class->toHtml()); + } + + /** @test */ + function can_create_menus_using_a_menu_composer() + { + $menu = new class extends MenuComposer { + public function compose(ItemCollection $items) + { + $items->url('/', 'Home'); + $items->placeholder('About us'); + $items->url('projects', 'Our projects'); + $items->url('contact-us', 'Contact us'); + } + }; + + $this->assertTemplateMatches('menu/menu', $menu); } } From 269f655d16bda6d96ff19f4fd2aa94801b0169b8 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Wed, 7 Mar 2018 16:28:50 +0000 Subject: [PATCH 049/217] Add active class to the link not the li --- tests/snapshots/bootstrap4/menu/menu.html | 4 ++-- tests/snapshots/bootstrap4/menu/submenu.html | 4 ++-- themes/bootstrap4/menu.blade.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/snapshots/bootstrap4/menu/menu.html b/tests/snapshots/bootstrap4/menu/menu.html index d9e1350..ab5a8e0 100644 --- a/tests/snapshots/bootstrap4/menu/menu.html +++ b/tests/snapshots/bootstrap4/menu/menu.html @@ -1,6 +1,6 @@
    - +
    From 61ca9e765e9f1ea72aad89a4753ea474ca4fb0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Tue, 13 Nov 2018 09:47:08 -0400 Subject: [PATCH 121/217] Set novalidate attribute for a form model --- src/FormModel.php | 15 +++++++++++++ tests/FormModelTest.php | 20 +++++++++++++++++ .../form-model/form-with-novalidate.html | 22 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 tests/snapshots/bootstrap4/form-model/form-with-novalidate.html diff --git a/src/FormModel.php b/src/FormModel.php index fd4aed2..cbf86c4 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -168,6 +168,21 @@ public function model($model) return $this; } + /** + * Set the novalidate attribute for a form, so developers can + * skip HTML5 validation, in order to test backend validation + * in a local or development environment. + * + * @param boolean $value + * @return $this + */ + public function novalidate($value = true) + { + $this->formBuilder->novalidate($value); + + return $this; + } + /** * Render all form to Html * diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index d795e5d..eef1e9c 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -82,6 +82,26 @@ function it_builds_a_update_form() $this->assertTemplateMatches('form-model/user-form-for-update', $userForm); } + + /** @test */ + function it_set_a_novalidate_attribute() + { + Route::post('login', ['as' => 'login']); + + $form = app(LoginForm::class)->novalidate(); + + $this->assertTemplateMatches('form-model/form-with-novalidate', $form); + } + + /** @test */ + function it_set_a_novalidate_attribute_as_false() + { + Route::post('login', ['as' => 'login']); + + $form = app(LoginForm::class)->novalidate(false); + + $this->assertTemplateMatches('form-model/login-form', $form); + } } class LoginForm extends FormModel diff --git a/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html b/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html new file mode 100644 index 0000000..bac1f0a --- /dev/null +++ b/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html @@ -0,0 +1,22 @@ +
    +
    + + +
    +
    + + +
    +
    + + +
    + +auth.forgot_link +
    From 30315b8c55dd1ffbca2b178c5f95778dbbe65e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Fri, 16 Nov 2018 09:40:00 -0400 Subject: [PATCH 122/217] Fix hasAttribute method and add missing tests --- src/FormModel/HasAttributes.php | 2 +- tests/FieldAttributesValidationTest.php | 68 +++++++++++++++++++++---- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/FormModel/HasAttributes.php b/src/FormModel/HasAttributes.php index 66f97a6..1b7769e 100644 --- a/src/FormModel/HasAttributes.php +++ b/src/FormModel/HasAttributes.php @@ -64,7 +64,7 @@ public function attr($attributes, $value = null) */ public function hasAttribute($name) { - return in_array($name, $this->attributes); + return array_key_exists($name, $this->attributes); } /** diff --git a/tests/FieldAttributesValidationTest.php b/tests/FieldAttributesValidationTest.php index ee3c548..9c8e00c 100644 --- a/tests/FieldAttributesValidationTest.php +++ b/tests/FieldAttributesValidationTest.php @@ -3,8 +3,9 @@ namespace Styde\Html\Tests; use Illuminate\Support\Facades\Gate; -use Styde\Html\Facades\Field; use Illuminate\Validation\Rules\Exists; +use Illuminate\Validation\Rules\NotIn; +use Styde\Html\Facades\Field; class FieldAttributesValidationTest extends TestCase { @@ -74,6 +75,16 @@ function it_adds_the_max_rule() $field = Field::text('name')->max(10); $this->assertEquals(['max:10'], $field->getValidationRules()); + $this->assertTrue($field->hasAttribute('max')); + } + + /** @test */ + function it_adds_the_maxlength_rule() + { + $field = Field::text('name')->maxlength(10); + + $this->assertEquals(['max:10'], $field->getValidationRules()); + $this->assertTrue($field->hasAttribute('maxlength')); } /** @test */ @@ -117,11 +128,21 @@ function it_adds_the_required_rule() } /** @test */ - function it_adds_the_min_rule() + function it_adds_the_minlength_rule() { $field = Field::text('name')->minlength(10); $this->assertSame(['min:10'], $field->getValidationRules()); + $this->assertTrue($field->hasAttribute('minlength')); + } + + /** @test */ + function it_adds_the_min_rule() + { + $field = Field::text('name')->min(10); + + $this->assertSame(['min:10'], $field->getValidationRules()); + $this->assertTrue($field->hasAttribute('min')); } /** @test */ @@ -132,6 +153,15 @@ function it_adds_the_regex_rule() $this->assertSame(['regex:/.{6,}/'], $field->getValidationRules()); } + /** @test */ + function it_adds_the_pattern_rule() + { + $field = Field::text('name')->pattern('.{6,}'); + + $this->assertSame(['regex:/.{6,}/'], $field->getValidationRules()); + $this->assertTrue($field->hasAttribute('pattern')); + } + /** @test */ function it_adds_the_placeholder_attribute() { @@ -159,9 +189,11 @@ function it_adds_the_required_if_rule() /** @test */ function it_adds_the_required_unless_rule() { - $field = Field::text('offer_code')->requiredUnless('price', '>=', 500); + $fieldA = Field::text('offer_code')->requiredUnless('price', '>=', 500); + $fieldB = Field::text('offer_code')->requiredUnless('price', 500); - $this->assertSame(['required_unless:price,>=,500'], $field->getValidationRules()); + $this->assertSame(['required_unless:price,>=,500'], $fieldA->getValidationRules()); + $this->assertSame(['required_unless:price,500'], $fieldB->getValidationRules()); } /** @test */ @@ -407,9 +439,11 @@ function it_adds_the_email_rule() /** @test */ function it_adds_the_exists_rule() { - $field = Field::text('foo')->exists('table', 'column'); + $fieldA = Field::text('foo')->exists('table', 'column'); + $fieldB = Field::text('foo')->exists('table'); - return $this->assertSame(['exists:table,column'], $field->getValidationRules()); + $this->assertSame(['exists:table,column'], $fieldA->getValidationRules()); + $this->assertSame(['exists:table'], $fieldB->getValidationRules()); } /** @test */ @@ -445,11 +479,19 @@ function it_adds_the_gte_rule() } /** @test */ - function it_adds_the_in_rule() + function it_adds_the_in_rule_string() { $field = Field::text('name')->in('first-zone', 'second-zone'); - return $this->assertSame(['in:first-zone,second-zone'], $field->getValidationRules()); + $this->assertSame(['in:first-zone,second-zone'], $field->getValidationRules()); + } + + /** @test */ + function it_adds_the_in_rule_class() + { + $field = Field::text('name')->in(['first-zone', 'second-zone']); + + $this->assertEquals([new \Illuminate\Validation\Rules\In(['first-zone','second-zone'])], $field->getValidationRules()); } /** @test */ @@ -533,13 +575,21 @@ function it_adds_the_mimes_rule() } /** @test */ - function it_adds_the_not_in_rule() + function it_adds_the_not_in_rule_string() { $field = Field::text('name')->notIn('first-zone', 'second-zone'); return $this->assertSame(['not_in:first-zone,second-zone'], $field->getValidationRules()); } + /** @test */ + function it_adds_the_not_in_rule_class() + { + $field = Field::text('name')->notIn(['first-zone', 'second-zone']); + + return $this->assertEquals([new NotIn(['first-zone','second-zone'])], $field->getValidationRules()); + } + /** @test */ function it_adds_the_not_regex_rule() { From a94455a13defe4cffcafb71836fb53265aae77c1 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 20 Nov 2018 21:43:46 +0000 Subject: [PATCH 123/217] Refactor unique rule --- src/FallbackToParent.php | 23 ++++++++++++++++++++ src/FormModel/IncludeRulesHelpers.php | 28 +++++++------------------ src/FormModel/ValidationRules.php | 4 +++- src/Rules/Unique.php | 24 +++++++++++++++++++++ tests/FieldAttributesValidationTest.php | 5 ++--- tests/FormModelTest.php | 2 +- 6 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 src/FallbackToParent.php create mode 100644 src/Rules/Unique.php diff --git a/src/FallbackToParent.php b/src/FallbackToParent.php new file mode 100644 index 0000000..6fdcbb7 --- /dev/null +++ b/src/FallbackToParent.php @@ -0,0 +1,23 @@ +setParent($parent); + } + + public function setParent($parent) + { + $this->parent = $parent; + } + + public function __call($method, $parameters) + { + return $this->parent->$method(...$parameters); + } +} \ No newline at end of file diff --git a/src/FormModel/IncludeRulesHelpers.php b/src/FormModel/IncludeRulesHelpers.php index b5ebaf4..a1e82f1 100644 --- a/src/FormModel/IncludeRulesHelpers.php +++ b/src/FormModel/IncludeRulesHelpers.php @@ -3,6 +3,7 @@ namespace Styde\Html\FormModel; use Illuminate\Validation\Rule; +use Styde\Html\Rules\Unique; trait IncludeRulesHelpers { @@ -600,31 +601,18 @@ public function timezone() return $this->setRule('timezone'); } - /** - * @param mixed ...$values - * @return mixed + * @param string $table + * @param string|null $column + * @return \Styde\Html\Rules\Unique */ - public function unique(...$values) + public function unique($table, $column = null) { - if (empty($values)) { - return $this->setRule("unique:{$this->table}"); - } - - foreach ($values as $key => $value) { - if (is_array($value) && array_key_exists('ignore', $value)) { - if (is_array($value['ignore'])) { - return $this->setRule( - Rule::unique($values[0], $values[1])->ignore($value['ignore'][0], $value['ignore'][1]) - ); - } - return $this->setRule(Rule::unique($values[0], $values[1])->ignore($value['ignore'])); - } - } + $rule = new Unique($table, $column, $this); - $data = implode(',', $values); + $this->setRule($rule); - return $this->setRule("unique:$data"); + return $rule; } /** diff --git a/src/FormModel/ValidationRules.php b/src/FormModel/ValidationRules.php index 1f1ccd4..a70db0f 100644 --- a/src/FormModel/ValidationRules.php +++ b/src/FormModel/ValidationRules.php @@ -38,7 +38,9 @@ public function getValidationRules() { $this->setRuleIn(); - return $this->rules; + return array_map(function ($rule) { + return (string) $rule; + }, $this->rules); } /** diff --git a/src/Rules/Unique.php b/src/Rules/Unique.php new file mode 100644 index 0000000..e22aa19 --- /dev/null +++ b/src/Rules/Unique.php @@ -0,0 +1,24 @@ +setParent($parent); + } +} diff --git a/tests/FieldAttributesValidationTest.php b/tests/FieldAttributesValidationTest.php index ee3c548..1ad9037 100644 --- a/tests/FieldAttributesValidationTest.php +++ b/tests/FieldAttributesValidationTest.php @@ -64,8 +64,7 @@ function it_builds_the_exists_rule_when_options_come_from_a_table() }) ->label('Parent'); - $this->assertSame('exists:table_name,id', (string) $field->getValidationRules()[0]); - $this->assertInstanceOf(Exists::class, $field->getValidationRules()[0]); + $this->assertSame('exists:table_name,id', $field->getValidationRules()[0]); } /** @test */ @@ -586,7 +585,7 @@ function it_adds_the_unique_rule() //TODO: fix test and code $field = Field::text('name')->unique('users', 'name'); - $this->assertSame(['unique:users,name'], $field->getValidationRules()); + $this->assertSame('unique:users,name,NULL,id', $field->getValidationRules()[0]); } /** @test */ diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index d795e5d..7248cf2 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -46,7 +46,7 @@ function it_returns_the_rules_from_all_fields() $expect = [ 'name' => [], - 'email' => ['email', 'unique:users', 'required'], + 'email' => ['email', 'unique:users,,NULL,id', 'required'], 'password' => ['confirmed', 'min:6', 'max:12', 'required'], 'password_confirmation' => ['min:6', 'max:12', 'required'], 'remember_me' => ['required'] From eb165f5fd56fd5f603e37b5339f32a4416551fb9 Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Tue, 20 Nov 2018 21:46:03 +0000 Subject: [PATCH 124/217] Fix generated rule --- src/FormModel/IncludeRulesHelpers.php | 4 ++-- tests/FormModelTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FormModel/IncludeRulesHelpers.php b/src/FormModel/IncludeRulesHelpers.php index a1e82f1..90226d9 100644 --- a/src/FormModel/IncludeRulesHelpers.php +++ b/src/FormModel/IncludeRulesHelpers.php @@ -603,10 +603,10 @@ public function timezone() /** * @param string $table - * @param string|null $column + * @param string $column * @return \Styde\Html\Rules\Unique */ - public function unique($table, $column = null) + public function unique($table, $column = 'NULL') { $rule = new Unique($table, $column, $this); diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index 7248cf2..11c6575 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -46,7 +46,7 @@ function it_returns_the_rules_from_all_fields() $expect = [ 'name' => [], - 'email' => ['email', 'unique:users,,NULL,id', 'required'], + 'email' => ['email', 'unique:users,NULL,NULL,id', 'required'], 'password' => ['confirmed', 'min:6', 'max:12', 'required'], 'password_confirmation' => ['min:6', 'max:12', 'required'], 'remember_me' => ['required'] From a24202bda456b00b5c5962ce8b3efa15f17b0799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Fri, 14 Dec 2018 12:34:24 -0400 Subject: [PATCH 125/217] Fix render a file input in a form --- src/FormBuilder.php | 13 +++++++++++++ src/FormModel/IncludeRulesHelpers.php | 2 +- tests/FieldAttributesValidationTest.php | 2 +- tests/FormModelTest.php | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 24bff4d..3d7c4f4 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -237,6 +237,19 @@ public function password(string $name, $attributes = []) return new Input('password', $name, null, $attributes); } + /** + * Create a file input field. + * + * @param string $name + * @param array $attributes + * + * @return \Styde\Html\Htmltag + */ + public function file(string $name, $attributes = []) + { + return new Input('file', $name, null, $attributes); + } + /** * Create a hidden input field. * diff --git a/src/FormModel/IncludeRulesHelpers.php b/src/FormModel/IncludeRulesHelpers.php index 90226d9..f1f08fe 100644 --- a/src/FormModel/IncludeRulesHelpers.php +++ b/src/FormModel/IncludeRulesHelpers.php @@ -191,7 +191,7 @@ public function dimensions(array $data) $rule .= "$key=$value,"; } - return $this->setRule($rule); + return $this->setRule(rtrim($rule, ',')); } /** diff --git a/tests/FieldAttributesValidationTest.php b/tests/FieldAttributesValidationTest.php index 1ad9037..b70289a 100644 --- a/tests/FieldAttributesValidationTest.php +++ b/tests/FieldAttributesValidationTest.php @@ -384,7 +384,7 @@ function it_adds_the_dimensions_rule() { $field = Field::file('avatar')->dimensions(['min_width' => 100, 'max_height' => 100]); - return $this->assertSame(['file', 'dimensions:min_width=100,max_height=100,'], $field->getValidationRules()); + return $this->assertSame(['file', 'dimensions:min_width=100,max_height=100'], $field->getValidationRules()); } /** @test */ diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index 11c6575..d88ff52 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -49,6 +49,7 @@ function it_returns_the_rules_from_all_fields() 'email' => ['email', 'unique:users,NULL,NULL,id', 'required'], 'password' => ['confirmed', 'min:6', 'max:12', 'required'], 'password_confirmation' => ['min:6', 'max:12', 'required'], + 'photo' => ['file', 'required', 'image', 'dimensions:ratio=3/2'], 'remember_me' => ['required'] ]; @@ -107,6 +108,7 @@ public function setup() $this->email('email')->unique('users')->required(); $this->password('password')->confirmed()->min(6)->max(12)->required(); $this->password('password_confirmation')->min(6)->max(12)->required(); + $this->file('photo')->required()->image()->dimensions(['ratio' => '3/2']); $this->checkbox('remember_me')->required(); } } From 5001aca7b797f2f94922f17beff6dbd137be32ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Mon, 17 Dec 2018 14:28:56 -0400 Subject: [PATCH 126/217] Add an help text block for a field and change the collection label --- src/FieldBuilder.php | 8 +++-- src/FormModel/Field.php | 27 ++++++++++++++++ tests/FieldBuilderTest.php | 32 +++++++++++++++++++ .../bootstrap4/field/checkboxes.html | 8 ++--- .../field/checkboxes_help_text.html | 22 +++++++++++++ tests/snapshots/bootstrap4/field/radios.html | 8 ++--- .../bootstrap4/field/text-help-text.html | 7 ++++ .../bootstrap4/field/text-raw-help-text.html | 7 ++++ .../bootstrap4/fields/collections.blade.php | 18 +++++------ themes/bootstrap4/fields/default.blade.php | 3 ++ 10 files changed, 121 insertions(+), 19 deletions(-) create mode 100644 tests/snapshots/bootstrap4/field/checkboxes_help_text.html create mode 100644 tests/snapshots/bootstrap4/field/text-help-text.html create mode 100644 tests/snapshots/bootstrap4/field/text-raw-help-text.html diff --git a/src/FieldBuilder.php b/src/FieldBuilder.php index 9f04a81..493034d 100644 --- a/src/FieldBuilder.php +++ b/src/FieldBuilder.php @@ -363,10 +363,14 @@ public function render(Field $field) $label = $this->getDefaultLabel($field->name); } + $helpText = $field->helpText; + $htmlName = $this->getHtmlName($field->name); + $id = $this->getHtmlId($field->name, $field->attributes); $errors = $this->getControlErrors($id); + $hasErrors = !empty($errors); $input = $this->buildControl( @@ -377,7 +381,7 @@ public function render(Field $field) $field->template, array_merge( $field->extra, - compact('htmlName', 'id', 'label', 'input', 'errors', 'hasErrors', 'required') + compact('htmlName', 'id', 'label', 'input', 'errors', 'hasErrors', 'required', 'helpText') ), 'fields.'.$this->getDefaultTemplate($field->type) ); @@ -385,7 +389,7 @@ public function render(Field $field) protected function setCustomAttributes(&$attributes, $field) { - $custom = ['label', 'template', 'id']; + $custom = ['label', 'template', 'id', 'helpText']; foreach ($custom as $key) { if (isset($attributes[$key])) { diff --git a/src/FormModel/Field.php b/src/FormModel/Field.php index f9a172a..7f6a9d0 100644 --- a/src/FormModel/Field.php +++ b/src/FormModel/Field.php @@ -32,6 +32,10 @@ class Field implements Htmlable * @var string */ public $label; + /** + * @var string + */ + public $helpText; /** * @var template */ @@ -104,6 +108,29 @@ public function rawLabel($html) return $this; } + /** + * @param $helpText + * @return $this + */ + public function helpText($helpText) + { + $this->helpText = $helpText; + + return $this; + } + + /** + * Add a help Text that contains HTML (be careful because it won't be escaped). + * + * @param $html + * @return $this + */ + public function rawHelpText($html) + { + $this->helpText = new HtmlString($html); + + return $this; + } /** * @param $value * @return $this diff --git a/tests/FieldBuilderTest.php b/tests/FieldBuilderTest.php index 1291094..c6c4877 100644 --- a/tests/FieldBuilderTest.php +++ b/tests/FieldBuilderTest.php @@ -50,6 +50,22 @@ public function it_generates_a_text_field_with_a_custom_label() ); } + /** @test */ + public function it_generates_a_text_field_with_an_help_text() + { + $this->assertTemplateMatches( + 'field/text-help-text', Field::text('name', 'value', ['helpText' => 'This is a help text for the field']) + ); + } + + /** @test */ + public function it_generates_a_text_field_with_a_raw_help_text() + { + $this->assertTemplateMatches( + 'field/text-raw-help-text', Field::text('name', 'value')->rawHelpText('This is a text with a link') + ); + } + /** @test */ public function it_generates_a_text_field_with_a_custom_id() { @@ -160,6 +176,22 @@ function it_generates_checkboxes() ); } + /** @test */ + function it_generates_checkboxes_with_help_text() + { + $tags = [ + 'php' => 'PHP', + 'python' => 'Python', + 'js' => 'JS', + 'ruby' => 'Ruby on Rails' + ]; + $checked = ['php', 'js']; + + $this->assertTemplateMatches( + 'field/checkboxes_help_text', Field::checkboxes('tags', $tags, $checked)->helpText('This is a help text')->required() + ); + } + /** @test */ function it_generates_radios() { diff --git a/tests/snapshots/bootstrap4/field/checkboxes.html b/tests/snapshots/bootstrap4/field/checkboxes.html index 0b4e635..23edafc 100644 --- a/tests/snapshots/bootstrap4/field/checkboxes.html +++ b/tests/snapshots/bootstrap4/field/checkboxes.html @@ -1,6 +1,6 @@ -

    - Tags -

    +
    +
    @@ -18,4 +18,4 @@

    -
    +
    \ No newline at end of file diff --git a/tests/snapshots/bootstrap4/field/checkboxes_help_text.html b/tests/snapshots/bootstrap4/field/checkboxes_help_text.html new file mode 100644 index 0000000..cf3dcd6 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/checkboxes_help_text.html @@ -0,0 +1,22 @@ +
    + +
    + + +
    +
    + + +
    +
    + + +
    +
    + + +
    + + This is a help text +
    \ No newline at end of file diff --git a/tests/snapshots/bootstrap4/field/radios.html b/tests/snapshots/bootstrap4/field/radios.html index 3786969..dd3b8f8 100644 --- a/tests/snapshots/bootstrap4/field/radios.html +++ b/tests/snapshots/bootstrap4/field/radios.html @@ -1,6 +1,6 @@ -

    - Gender -

    +
    +
    @@ -10,4 +10,4 @@

    -
    +
    \ No newline at end of file diff --git a/tests/snapshots/bootstrap4/field/text-help-text.html b/tests/snapshots/bootstrap4/field/text-help-text.html new file mode 100644 index 0000000..2a01704 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/text-help-text.html @@ -0,0 +1,7 @@ +
    + + + This is a help text for the field +
    diff --git a/tests/snapshots/bootstrap4/field/text-raw-help-text.html b/tests/snapshots/bootstrap4/field/text-raw-help-text.html new file mode 100644 index 0000000..cb3dcf5 --- /dev/null +++ b/tests/snapshots/bootstrap4/field/text-raw-help-text.html @@ -0,0 +1,7 @@ +
    + + + This is a text with a link +
    diff --git a/themes/bootstrap4/fields/collections.blade.php b/themes/bootstrap4/fields/collections.blade.php index 597d37b..334144c 100644 --- a/themes/bootstrap4/fields/collections.blade.php +++ b/themes/bootstrap4/fields/collections.blade.php @@ -1,12 +1,12 @@ - $hasErrors]) !!}> - {{ $label }} - @if ($required) - Required - @endif - +
    + {!! $input !!} +@if ($helpText) + {{ $helpText }} +@endif @foreach ($errors as $error) -

    {{ $error }}

    +
    {{ $error }}
    @endforeach - -
    +
    \ No newline at end of file diff --git a/themes/bootstrap4/fields/default.blade.php b/themes/bootstrap4/fields/default.blade.php index de32d1b..d71b76d 100644 --- a/themes/bootstrap4/fields/default.blade.php +++ b/themes/bootstrap4/fields/default.blade.php @@ -6,6 +6,9 @@ @endif {{ $input->id($id)->classes(['form-control', 'is-invalid' => $hasErrors]) }} + @if ($helpText) +{{ $helpText }} + @endif @foreach ($errors as $error)
    {{ $error }}
    @endforeach From 1896a4542f8483d2d1940f39aa92c759ffbf2699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Tue, 15 Jan 2019 18:11:12 -0400 Subject: [PATCH 127/217] Throw error with clear message when dispatching dynamic method calls --- src/FormModel.php | 20 ++++++++++++++++++-- tests/FormModelTest.php | 9 +++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/FormModel.php b/src/FormModel.php index cbf86c4..8fb09e2 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -2,12 +2,15 @@ namespace Styde\Html; -use Illuminate\Http\Request; use Illuminate\Contracts\Support\Htmlable; +use Illuminate\Http\Request; +use Illuminate\Support\Traits\ForwardsCalls; use Styde\Html\FormModel\{Field, FieldCollection, ButtonCollection}; class FormModel implements Htmlable { + use ForwardsCalls; + /** * @var \Styde\Html\FormBuilder */ @@ -239,6 +242,15 @@ public function getValidationRules() return $rules; } + /** + * Dynamically handle calls to the form model. + * + * @param string $method + * @param array $parameters + * + * @return mixed + * @throws \BadMethodCallException + */ public function __call($method, $parameters = []) { if (method_exists($this->form, $method)) { @@ -249,7 +261,11 @@ public function __call($method, $parameters = []) return $this->buttons->$method(...$parameters); } - return $this->fields->$method(...$parameters); + if (method_exists($this->fields, $method)) { + return $this->fields->$method(...$parameters); + } + + static::throwBadMethodCallException($method); } /** diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index 4337c2a..d63272e 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -103,6 +103,15 @@ function it_set_a_novalidate_attribute_as_false() $this->assertTemplateMatches('form-model/login-form', $form); } + + /** @test */ + function it_thrown_an_exception_when_call_a_bad_method() + { + $this->expectException('BadMethodCallException'); + $this->expectExceptionMessage('Call to undefined method Styde\Html\Tests\LoginForm::badMethod()'); + + $form = app(LoginForm::class)->badMethod(); + } } class LoginForm extends FormModel From 79b779dfa53e6d43e3b3934f0998fd2d1827b534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Tue, 15 Jan 2019 18:18:52 -0400 Subject: [PATCH 128/217] Remove undefined variable --- src/FormBuilder.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 3d7c4f4..de41e0d 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -275,7 +275,11 @@ public function hidden($name, $value = null, $attributes = []) */ public function textarea($name, $value = null, $attributes = []) { - return new Htmltag('textarea', $this->getValueAttribute($name, $value), array_merge(compact('type', 'name'), $attributes)); + return new Htmltag( + 'textarea', + $this->getValueAttribute($name, $value), + array_merge(compact('name'), $attributes) + ); } /** From 05a5c8f9bdcc79889c83aa9b7866f10685693c6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Tue, 15 Jan 2019 18:38:01 -0400 Subject: [PATCH 129/217] Add a test for make:menu command and fix the test for make:form command --- tests/FormMakeCommandTest.php | 18 ++++++++++++++++++ tests/MenuMakeCommandTest.php | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/MenuMakeCommandTest.php diff --git a/tests/FormMakeCommandTest.php b/tests/FormMakeCommandTest.php index 9337238..83558ed 100644 --- a/tests/FormMakeCommandTest.php +++ b/tests/FormMakeCommandTest.php @@ -9,10 +9,28 @@ class FormMakeCommandTest extends TestCase /** @test */ function it_can_run_the_make_command() { + $this->cleanDirectory(); + $result = Artisan::call('make:form', ['name' => 'UserForm']); $this->assertDirectoryExists(app_path('Http/Forms')); $this->assertFileExists(app_path('Http/Forms/UserForm.php')); $this->assertFileExists(app_path('Http/Forms/FormModel.php')); } + + protected function rmFile($filename) + { + if (file_exists($filename)) { + unlink($filename); + } + } + + protected function cleanDirectory() + { + $this->rmFile(app_path('Http/Forms/UserForm.php')); + $this->rmFile(app_path('Http/Forms/FormModel.php')); + if (is_dir(app_path('Http/Forms'))) { + rmdir(app_path('Http/Forms')); + } + } } diff --git a/tests/MenuMakeCommandTest.php b/tests/MenuMakeCommandTest.php new file mode 100644 index 0000000..a1a37db --- /dev/null +++ b/tests/MenuMakeCommandTest.php @@ -0,0 +1,34 @@ +cleanDirectory(); + + $result = Artisan::call('make:menu', ['name' => 'UserMenu']); + + $this->assertDirectoryExists(app_path('Menus')); + $this->assertFileExists(app_path('Menus/UserMenu.php')); + } + + protected function rmFile($filename) + { + if (file_exists($filename)) { + unlink($filename); + } + } + + protected function cleanDirectory() + { + $this->rmFile(app_path('Menus/UserMenu.php')); + if (is_dir(app_path('Http/Menus'))) { + rmdir(app_path('Http/Menus')); + } + } +} From 24c44136696e07e060391575f3dc93f54128a773 Mon Sep 17 00:00:00 2001 From: Dimitri Acosta Date: Tue, 22 Jan 2019 06:55:14 -0700 Subject: [PATCH 130/217] Format helpers examples --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 23ae56c..09a66a4 100644 --- a/README.md +++ b/README.md @@ -224,7 +224,9 @@ In addition of using the facade methods `Alert::message` and `Menu::make`, you c alert('this is the message', 'type-of-message') ``` -`menu($items, $classes)` +```php +menu($items, $classes) +``` ## Access handler From 644b218f60ce9e5b439e357a89ac6b47b2861610 Mon Sep 17 00:00:00 2001 From: Dimitri Acosta Date: Tue, 22 Jan 2019 07:25:29 -0700 Subject: [PATCH 131/217] Fix ES docs files --- LEEME.md | 6 +- docs/es/access-handler.md | 38 ++++--- docs/es/alert-messages.md | 75 ++++++++------ docs/es/field-builder.md | 174 ++++++++++++++++++-------------- docs/es/form-builder.md | 18 ++-- docs/es/internationalization.md | 36 ++++--- docs/es/menu-generator.md | 84 +++++++++------ docs/es/themes.md | 19 +++- 8 files changed, 273 insertions(+), 177 deletions(-) diff --git a/LEEME.md b/LEEME.md index 9f4697a..96cf5cb 100644 --- a/LEEME.md +++ b/LEEME.md @@ -202,7 +202,7 @@ Este paquete extiende la funcionalidad del HTML Builder de the Laravel Collectiv Hay un solo método adicional _por ahora_, pero ¡es muy útil! -####Generar clases de CSS: +#### Generar clases de CSS: ```blade {!! Html::classes(['home' => true, 'main', 'dont-use-this' => false]) !!} @@ -220,7 +220,9 @@ Además de utilizar los métodos facade `Alert::message` y `Menu::make`, puedes alert('this is the message', 'type-of-message') ``` -`menu($items, $clases)` +```php +menu($items, $clases) +``` ## Access handler diff --git a/docs/es/access-handler.md b/docs/es/access-handler.md index d22d5f3..a50842e 100644 --- a/docs/es/access-handler.md +++ b/docs/es/access-handler.md @@ -20,13 +20,15 @@ Sólo pasa una de las siguientes opciones como un atributo de campo o valor del Uso: -####Form fields +#### Form fields -`{!! Field::select('user_id', null, ['roles' => 'admin'])` +```blade +{!! Field::select('user_id', null, ['roles' => 'admin']) +``` -####Menu items +#### Menu items -``` +```php // config/menu.php return [ @@ -44,7 +46,9 @@ return [ ]; ``` -`{!! Menu::make('menu.items') !}}` +```blade +{!! Menu::make('menu.items') !}} +``` ## Autorización y políticas de acceso @@ -56,14 +60,19 @@ Si es un array, la primera posición del array será el nombre de la habilidad y Ejemplos: -`{!! Field::text('change-password', ['allows' => 'change-password']) !!}` -`{!! Field::select('category', $options, ['allows' => ['change-post-category', $category]]) !!}` +```blade +{!! Field::text('change-password', ['allows' => 'change-password']) !!} +``` + +```blade +{!! Field::select('category', $options, ['allows' => ['change-post-category', $category]]) !!} +``` Si estás contruyendo menús puedes usar parámetros dinámicos para pasar valores a la autorización. En el siguiente ejemplo definiremos un parámetro dinámico 'post' y pasarlo usando setParam cuando se construya el menú: -``` +```php // config/menu.php return [ @@ -76,7 +85,9 @@ return [ ]; ``` -`{!! Menu::make('menu.items')->setParam('post', $post)->render() !}}` +```blade +{!! Menu::make('menu.items')->setParam('post', $post)->render() !}} +``` ## Personalización @@ -86,7 +97,7 @@ Si estás trabajando en un proyecto complejo con muchas reglas de acceso diferen Si se quiere usar la clase del access handler como un componente independiente, por favor agrega este alias global en `config/app.php`. -``` +```php 'aliases' => [ // ... 'Access' => Styde\Html\Facades\Access, @@ -96,7 +107,7 @@ Si se quiere usar la clase del access handler como un componente independiente, Luego se puede utilizar la facade donde se quiera: -``` +```blade @if (Access:check(['roles' => ['admin, 'editor']]))

    id]) }}'> @@ -106,12 +117,13 @@ Luego se puede utilizar la facade donde se quiera: @endif ``` -##Desactivar el access handler +## Desactivar el access handler Se puede desactivar este componente en la configuración: -``` +```php //config/html.php + return [ //.. 'control_access' => false, diff --git a/docs/es/alert-messages.md b/docs/es/alert-messages.md index e84faab..48a17b8 100644 --- a/docs/es/alert-messages.md +++ b/docs/es/alert-messages.md @@ -2,38 +2,42 @@ Este componente permitirá generar mensajes de alerta complejos. - ``` - Alert::info('Su cuenta está a punto de caducar') - ->details('Renueva ahora para aprender acerca de:') - ->items([ - 'Laravel', - 'PHP, - 'y más', - ]) - ->button('¡Renueva ahora!', '#', 'primary'); + ```php +Alert::info('Su cuenta está a punto de caducar') + ->details('Renueva ahora para aprender acerca de:') + ->items([ + 'Laravel', + 'PHP, + 'y más', + ]) + ->button('¡Renueva ahora!', '#', 'primary'); ``` Los mensajes serán persistentes en el sesión hasta que sean presentados al usuario con: -`{!! Alert::render() !!}` +```blade +{!! Alert::render() !!} +``` ## Crear un nuevo mensaje de alerta Se puede generar un nuevo mensaje de alerta con: -`{!! Alert::message('Este es el mensaje', 'tipo-alerta') !!}` +```blade +{!! Alert::message('Este es el mensaje', 'tipo-alerta') !!} +``` El primer argumento es el texto del mensaje y el segundo es el tipo de alerta. Por ejemplo: -``` +```php Alert::message('El final está cerca', 'danger'); ``` También se puede usar métodos mágicos, el nombre del método se convierte en el tipo de alerta: -``` +```php Alert::success("Está todo bien ahora"); ``` @@ -41,43 +45,53 @@ Alert::success("Está todo bien ahora"); Se puede especificar más opciones por encadenamiento de métodos: -###details +### Details Se puede pasar uno o más mensajes detallados encadenando el método details(): -`{!! Alert::info('Algo de información')->details('Una explicación más detallada va aquí') !!}` +```blade +{!! Alert::info('Algo de información')->details('Una explicación más detallada va aquí') !!} +``` -###Llamadas de acción +### Llamadas de acción Se puede asignar botones a un mensaje de alerta: -`{!! Alert::info()->button('Llamada de acción', 'alguna-url', 'primary') !!}` +```blade +{!! Alert::info()->button('Llamada de acción', 'alguna-url', 'primary') !!} +``` -###html +### Html Se puede directamente pasar HTML a un mensaje de alerta: -`{!! Alert::info()->html('El HTML va aquí') !!}` +```blade +{!! Alert::info()->html('El HTML va aquí') !!} +``` Tenga cuidado ya que esto no será escapado. -###view +### View Se puede incluso renderizar una vista dentro de un mensaje de alerta: -`{!! Alert::info()->view('partials/alerts/partial') !!}` +```blade +{!! Alert::info()->view('partials/alerts/partial') !!} +``` -###items +### Items Se puede pasar un array de items (tal vez una lista de errores): -`{!! Alert::danger('Por favor corrija los siguientes errores')->items($errors) !!}` +```blade +{!! Alert::danger('Por favor corrija los siguientes errores')->items($errors) !!} +``` -##Persistir los mensajes de alerta +## Persistir los mensajes de alerta Agrega el siguiente middleware al array `$middleware` en `app/Http/Kernel.php` **ANTES** de `\App\Http\Middleware\EncryptCookies`: -``` +```php protected $middleware = [ //... \Styde\Html\Alert\Middleware::class @@ -94,8 +108,9 @@ Si la opción `'translate_texts'` está definida como true en la configuración Si no se necesita utilizar el componente Traductor, sólo define translate_texts como false en la configuración: -``` +```php //config/html.php + return [ //... 'translate_texts' => false @@ -105,10 +120,10 @@ return [ ## Themes -Por defecto, los mensajes de alerta serán renderizados con la plantilla predeterminada, localizada en themes/[theme]/alert, por ejemplo, para el tema de Bootstrap theme que sería: - -`vendor/styde/html/themes/bootstrap/alert.blade.php` +Por defecto, los mensajes de alerta serán renderizados con la plantilla predeterminada, localizada en themes/[theme]/alert, por ejemplo, para el tema de Bootstrap theme que sería: `vendor/styde/html/themes/bootstrap/alert.blade.php` Se puede pasar un plantilla personalizada como el primer argumento del método render(), es decir: -`{!! Alert::render('partials/custom-alert-template') !!}` \ No newline at end of file +```blade +{!! Alert::render('partials/custom-alert-template') !!} +``` \ No newline at end of file diff --git a/docs/es/field-builder.md b/docs/es/field-builder.md index 994c2ff..208e8a1 100644 --- a/docs/es/field-builder.md +++ b/docs/es/field-builder.md @@ -4,11 +4,15 @@ Este componente permitirá generar el markup completo para los campos del formul Si has usado antes el componente HTML de Laravel Collective, ya sabes cómo utilizar los conceptos básicos de este componente; simplemente reemplaza el alias “Form” por “Field”, por ejemplo, sustituye: -`{!! Form::text(‘name’, ‘value’, $attributes) !!}` +```blade +{!! Form::text(‘name’, ‘value’, $attributes) !!} +``` Por esto: -`{!! Field::text(‘name’, ‘value’, $attributes) !!}` +```blade +{!! Field::text(‘name’, ‘value’, $attributes) !!} +``` Esto generará un campo incluyendo el container, el label, el control y cualquier error asociado con él. @@ -16,22 +20,28 @@ Los campos serán generado con el markup predeterminado de **Twitter Bootstrap** Igualmente hay un montón de opciones convenientes: -##Omitir el argumento value +## Omitir el argumento value Si no quieres pasar un argumento value (`null`) pero quiere pasar el array de `$attributes`, se puede saltar el segundo argumento, es decir, esto: -`{!! Field::text(‘name’, $attributes) !!}` +```blade +{!! Field::text(‘name’, $attributes) !!} +``` Es lo mismo que: -`{!! Field::text(‘name’, null, $attributes) !!}` +```blade +{!! Field::text(‘name’, null, $attributes) !!} +``` -##Labels: +## Labels: Se puede explicítamente pasar un label a un campo como parte del array de atributos, es decir: -`{!! Field::text(’name’, [‘label’ => ‘Full name’]) !!}` +```blade +{!! Field::text(’name’, [‘label’ => ‘Full name’]) !!} +``` Como una segunda opción, se puede almacenar los labels en la carpeta lang/ con la misma convención usada para almacenar los nombres de los atributos para los errores de validación: @@ -43,28 +53,26 @@ Si saltas ambas opciones, entonces FieldBuilder generará un label basado en el `full_name` se mostrará "Full name" como el label predeterminado. -##Templates - -Por defecto, los campos serán renderizados con la plantilla predeterminada, ubicada en la carpeta [theme]/fields, por ejemplo, para el tema Bootstrap sería: +## Templates -`vendor/styde/html/themes/bootstrap/fields/default.blade.php` +Por defecto, los campos serán renderizados con la plantilla predeterminada, ubicada en la carpeta [theme]/fields, por ejemplo, para el tema Bootstrap sería: `vendor/styde/html/themes/bootstrap/fields/default.blade.php` Pero se tiene la opción de personalizar la plantilla usada para un tipo o campo particular: -###Personalizar por tipo +### Personalizar por tipo Algunos frameworks de CSS (como Bootstrap) necesitan diferentes markups para distintas tipos de campos, así que para utilizar la configuración que asigna una plantilla diferente a un tipo de campo determinado, se hace algo como esto: -``` - 'themes' => [ - 'bootstrap' => [ - 'field_templates' => [ - 'checkbox' => 'checkbox', - 'checkboxes' => 'collection', - 'radios' => 'collection' - ], - //... - //... +```php +'themes' => [ + 'bootstrap' => [ + 'field_templates' => [ + 'checkbox' => 'checkbox', + 'checkboxes' => 'collection', + 'radios' => 'collection' + ], + //... +//... ``` Con esta configuración los campos "checkbox" usarán la plantilla `vendor/styde/html/themes/bootstrap/fields/checkbox.blade.php` por defecto, mientras que los campos "checkboxes" y "radios" utilizará la plantilla `vendor/styde/html/themes/bootstrap/fields/collection.blade.php`. @@ -73,49 +81,57 @@ Como puedes ver, la configuración es para este theme en específico, ya que cad Nota: sólo tienes que preocuparte por el theme que realmente necesitas, por lo que si no planeas usar Bootstrap, puedes borrar/omitir la configuración `bootstrap` -###Personalizar un campo determinado +### Personalizar un campo determinado Puedes especificar una `template` personalizada para un solo campo a través de `'template key'` del array `$attributes`, así: -`{!! Field::text(’name’, [’template’ => ’templates/my_template’]) !!}` +```blade +{!! Field::text(’name’, [’template’ => ’templates/my_template’]) !!} +``` La ruta será relativa al directorio resources/views/ -###Personalización de plantillas predeterminadas +### Personalización de plantillas predeterminadas Si quieres personalizar las plantillas predeterminadas, sólo ejecuta `php artisan vendor:publish` en la consola y todas las plantillas serán copiadas a la carpeta `resources/views/themes/[theme]/fields/` De otra manera, el paquete usará las plantillas predeterminadas (almacenadas en `/vendor/styde/html/themes/`) y no será necesario copiar archivos adicionales dentro del proyecto. -##Atributo name +## Atributo name Puedes usar la notación de punto como nombre del campo, por ejemplo: `profile.twitter` y se transformará a `profile[twitter]` -##Atributo id +## Atributo id Éste se asignará automáticamente para control de cada input, si utilizas la notación de punto (ejemplo: user.name) los puntos serán reemplazados por guiones bajos (ejemplo: user_name) -##Atributo required +## Atributo required Puedes especificar un valor 'required' en el array de atributos: -`{!! Field::text(’name’, [’required’]) !!}` +```blade +{!! Field::text(’name’, [’required’]) !!} +``` O como un par llave => valor (el campo será marcado como `required` si el valor se evalúa como true, es decir: -`$required = true;` +```php +$required = true; +``` -`{!! Field::text(’name’, null, [’required’ => $required]) !!}` +```blade +{!! Field::text(’name’, null, [’required’ => $required]) !!} +``` Las plantillas de campo siempre tendrán una variable `required` por lo que pueda ser usado para imprimir clases de CSS adicionales o badges, para indicar si un campo es necesario u opcional, es decir: -``` - @if ($required) - Required - @endif +```blade +@if ($required) + Required +@endif ``` -##Errores: +## Errores: Cualquier error de sesión será cargado en el FieldBuilder a través de `HtmlServiceProvider` y se tendrá `$errors` específicos por cada campo disponible en la plantilla, también se tendrá una variable `$hasErrors` en caso que el campo tenga algún error y se necesite imprimir una clase de CSS adicional, etc. @@ -123,31 +139,35 @@ Por ejemplo, con Twitter Bootstrap se necesitará una clase `has-error` en caso Este es un extracto de una plantilla personalizada para el theme Bootstrap: -` $hasErrors]) !!}>` +```blade + $hasErrors]) !!}> +``` Los inputs, selects, textareas, etc. con errores también tendrán una clase de CSS adicional que se puede configurar de esta manera: -``` - 'themes' => [ - 'bootstrap' => [ - //... - 'field_classes' => [ - //... - 'error' => 'input-with-feedback' +```php +'themes' => [ + 'bootstrap' => [ + //... + 'field_classes' => [ //... + 'error' => 'input-with-feedback' + //... ``` Una vez más, si estás usando Twitter Bootstrap cualquier campo con errores tendrá la clase `input-with-feedback`. Esto también es necesario para mostrar el input en color rojo. -##Options +## Options Para selects, radios and checkboxes, se puede omitir los argumentos de las opciones o pasar `null`: -`{!! Field::select('os') !!}` or `{!! Field::select('os', null) !!}` +```blade +{!! Field::select('os') !!}` or `{!! Field::select('os', null) !!} +``` Si existe un modelo vinculado al formulario, entonces el FieldBuilder verificará si hay un método llamado: `get[fieldName]Options`, en ese caso, será llamado y devolverá los valores a ser utilizados como las opciones, es decir: -``` +```php class Product extends Model //... @@ -160,11 +180,13 @@ class Product extends Model //... ``` -##Opción empty +## Opción empty Los campos select frecuentemente necesitan una opción empty (como "Selecciona una opción, por favor") que se puede pasar con el atributo `'empty'` de esta manera: -`{!! Field::select('os', null, ['empty' => 'Selecciona tu sistema operativo favorito']) !!}` +```blade +{!! Field::select('os', null, ['empty' => 'Selecciona tu sistema operativo favorito']) !!} +``` Si no se pasa el atributo `'empty'`, entonces el componente buscará uno usando el componente traslator. @@ -174,53 +196,55 @@ Si no se encuentra ninguno, se buscará la opción empty por defecto: `"validati En último caso, si ninguna de las opciones es encontrada, se usará un string vacío como opción empty. -##Abreviaturas +## Abreviaturas Para ahorrar algunas pulsaciones de teclas, puedes utilizar abreviaturas en lugar del nombre completo de los atributos, pasándolos en la configuración: -``` - /* - * Especifica las abreviaturas para los atributos del campo del formulario - */ - 'abbreviations' => [ - 'ph' => 'placeholder', - 'max' => 'maxlength', - 'tpl' => 'template' - ], +```php +/* +* Especifica las abreviaturas para los atributos del campo del formulario +*/ +'abbreviations' => [ + 'ph' => 'placeholder', + 'max' => 'maxlength', + 'tpl' => 'template' +], ``` Después se podrán hacer cosas como éstas: -`{!! Field::text('name', ['ph' => 'Esto será el placeholder]) !!}` +```blade +{!! Field::text('name', ['ph' => 'Esto será el placeholder]) !!} +``` -##Clases de CSS +## Clases de CSS Se puede pasar clases de CSS personalizadas para cada campo usando la llave 'class' del array de atributos, también se pueden agregar clases adicionales: -###Clases predeterminadas (por tipo) +### Clases predeterminadas (por tipo) Utilizando la configuración, se puede asignar clases de CSS predeterminadas para cada campo según su tipo: -``` - 'themes' => [ - //... - 'bootstrap' => [ - //... - 'field_classes' => [ - // tipo => clase o clases de CSS - 'default' => 'form-control', - 'checkbox' => '', - 'error' => 'input-with-feedback' - ], +```php +'themes' => [ + //... + 'bootstrap' => [ + //... + 'field_classes' => [ + // tipo => clase o clases de CSS + 'default' => 'form-control', + 'checkbox' => '', + 'error' => 'input-with-feedback' + ], ``` Por supuesto, esto es para cada theme en específico, debido a que es imposible convencer a todos los autores de frameworks de CSS de usar las mismas clases. -###Clases de CSS para controls con errores +### Clases de CSS para controls con errores Si un input tiene errores, una clase de CSS adicional llamada `error` se agregará, también puede ser configurada para cada theme (véase más arriba). -##Control de acceso +## Control de acceso Es posible que desees ocultar algunos campos para ciertos usuarios, esto se puede hacer usando el Access Handler incluído con este componente: diff --git a/docs/es/form-builder.md b/docs/es/form-builder.md index 89d05f4..2fa8b25 100644 --- a/docs/es/form-builder.md +++ b/docs/es/form-builder.md @@ -1,10 +1,10 @@ # Form Builder -## novalidate +## Novalidate Permite a los usuarios establecer la opción `novalidate` para cada formulario generado con el método `Form::open` o el método `Form::model` así los desarrolladores pueden saltar la validación de HTML5 para evaluar la validación backend en los entornos local o de desarrollo: -``` +```php return [ //.. 'novalidate' => true @@ -12,23 +12,25 @@ return [ ]; ``` -## radios +## Radios Crea una lista de radios. Esta función es similar a `Form::select` pero genera una colección de radios en vez de opciones. Es decir: -`Form::radios('status', ['a' => 'Active', 'i' => 'Inactive'])` +```php +Form::radios('status', ['a' => 'Active', 'i' => 'Inactive']) +``` Se puede pasar 'inline' como un valor en el arreglo de atributos para establecer los radios en línea (ellos se renderizarán con la plantilla 'radios-inline'). -## checkboxes +## Checkboxes Crea una lista de checkboxes. Esta función es similar a `Form::select` pero genera una colección de checkboxes en vez de opciones, es decir: -``` +```php $tags = [ 'php' => 'PHP', 'python' => 'Python', @@ -39,6 +41,8 @@ $tags = [ $checked = ['php', 'js']; ``` -`{!! Form::checkboxes('tags', $tags, $checked) !!}` +```blade +{!! Form::checkboxes('tags', $tags, $checked) !!} +``` Se puede pasar 'inline' como un valor en el arreglo de atributos para establecer los checkboxes en línea (ellos serán renderizados usando la plantilla 'checkboxes-inline'). \ No newline at end of file diff --git a/docs/es/internationalization.md b/docs/es/internationalization.md index ddb6812..43b6a3b 100644 --- a/docs/es/internationalization.md +++ b/docs/es/internationalization.md @@ -2,8 +2,9 @@ Puedes configurar si quieres usar este paquete para tratar de traducir los textos o no, por ejemplo si tu proyecto sólo necesita ser implementado en un idioma y prefieres simplemente escribir los textos donde los necesites en lugar de utilizar el componente Translator, desactiva las traducciones en la configuración: -``` +```php //config/html.php + return [ //... 'translate_texts' => false @@ -19,16 +20,19 @@ Pero si tu proyecto necesita ser implementado en más de un idioma o quieres org Si quieres tener un label específico en un campo, puedes hacerlo pasándolo como parte del array de atributos: - `{!! Field::text('name', ['label' => 'Nombre completo']) !!}` +```blade +{!! Field::text('name', ['label' => 'Nombre completo']) !!} +``` También puedes definirlo como parte del array `attributes`en el archivo `resources/lang/en/validation.php`: - ``` - //resources/lang/en/validation.php - //.. - 'attributes' => [ - 'name' => 'Nombre completo' - ], + ```php +//resources/lang/en/validation.php + +//.. +'attributes' => [ + 'name' => 'Nombre completo' +], ``` Toma en cuenta que esto también es una convención usada por el componente Laravel Validator, de esta manera puedes tener todos los textos de los labels en un mismo lugar. @@ -39,9 +43,9 @@ Toma en cuenta que esto también es una convención usada por el componente Lara Si `'translate_texts'` es definido como `true`, este componente asumirá que todos los mensajes de alerta son de hecho llaves de idioma e intentará traducirlas. Es decir, puedes hacer cosas como: -``` +```php Alert::success('messages.users.updated') - ->button('messages.users.go_to_profile', url('users/profile')) + ->button('messages.users.go_to_profile', url('users/profile')) ``` Por supuesto, si la llave de idioma no es encontrada, éste devolverá el string literal (también puesdes pasar el mensaje completo en lugar de una llave de idioma). @@ -52,15 +56,17 @@ Por supuesto, si la llave de idioma no es encontrada, éste devolverá el string Si `'translate_texts'` es definido como `true`, pero no específicas un título explícito para un menu item; el componente buscará un llave de idioma en: `menu.[llave_menu_item]` si la llave no es encontrada, el paquete intentará convertir la llave del menu item en un formato de título. Por ejemplo: -``` +```php //resources/lang/en/menu.php + return [ 'home' => 'Homepage' ]; ``` -``` +```php //config/menu.php + return [ 'items' => [ 'home' => [], @@ -70,11 +76,13 @@ return [ ]; ``` -`{!! Menu::make('menu.items') !!}` +```blade +{!! Menu::make('menu.items') !!} +``` Devolverá algo así: -``` +```html

    • Homepage
    • Who we are
    • diff --git a/docs/es/menu-generator.md b/docs/es/menu-generator.md index 79d7b43..678f953 100644 --- a/docs/es/menu-generator.md +++ b/docs/es/menu-generator.md @@ -6,11 +6,13 @@ Así que en lugar de agregar una gran cantidad de código boilerplate HTML y Bla Para generar un menú simplemente agrega el siguiente código en la plantilla de tu diseño: -`{!! Menu::make('items.aqui', 'clases css opcionales') !!}` +```blade +{!! Menu::make('items.aqui', 'clases css opcionales') !!} +``` `'items.aqui'` puede ser un array o una llave de configuración (que contiene un array), donde se especificarán los menu items, por ejemplo: -``` +```php [ 'home' => ['url' => ''], 'about' => ['title' => 'Who we are', 'url' => 'about-us'], @@ -26,73 +28,91 @@ Puedes especificar las siguientes opciones para cada menu item: Por supuesto, esta opción es la parte más importante de cada menu item y por tanto, tiene varias opciones para especificar una URL: -###full_url +### full_url Si pasas la llave 'full_url' dentro de la configuración del item, éste lo devolverá como la URL sin ninguna acción adicional, es decir: -`['full_url' => 'https://styde.net']` +```php +['full_url' => 'https://styde.net'] +``` -###url +### url Puedes pasar una URL relativa usando la llave 'url'. El URL resultante será generado usando el método `UrlGenerator::to`, es decir: -`['url' => 'contact-us']` +```php +['url' => 'contact-us'] +``` También puedes pasar una llave 'secure' para indicar si ese URL particular debe utilizar https o no. Igualmente puedes especificar un valor secure predeterminado usando el método `setDefaultSecure` (false por defecto). -`['url' => 'login', 'secure' => 'true']` +```php +['url' => 'login', 'secure' => 'true'] +``` -###route +### route Especifica el nombre de una ruta para un menu item: -`['route' => 'home']` +```php +['route' => 'home'] +``` -###route con parámetros +### route con parámetros Puedes establecer una ruta con parámetros pasando un array en vez de un string como el valor de la llave 'route'. El primer valor será tomado como el nombre de la ruta y los otros serán los parámetros de la ruta. -`['route' => ['profile', 'sileence']]` +```php +['route' => ['profile', 'sileence']] +``` -###action +### action Especifica una acción para un menu item. -###action con parámetros +### action con parámetros Puedes establecer una acción con parámetros pasando un array en vez de un string como valor de la llave 'action'. El primer valor será la acción y los otros serán los parámetros de la acción. -###default placeholder +### default placeholder Si ninguna de las opciones anteriores es encontrada, entonces el URL será simplemente un placeholder "#". -###Parámetros dinámicos +### Parámetros dinámicos Algunas veces necesitarás utilizar parámetros dinámicos para construir rutas y acciones, para ese caso, en vez de un valor se pasa un nombre precedido por :, por ejemplo: -`['route' => ['profile', ':username']]` +```php +['route' => ['profile', ':username']] +``` Después puedes asignar un valor usando los métodos `setParam` or `setParams`, así: -`{!! Menu::make('config.items')->setParam('username', 'sileence') !!}` +```blade +{!! Menu::make('config.items')->setParam('username', 'sileence') !!} +``` o de esta manera: -`{!! Menu::make('config.items')->setParams(['username' => 'sileence']) !!}` +```blade +{!! Menu::make('config.items')->setParams(['username' => 'sileence']) !!} +``` -##title +## title Especifica un título para un menu item usando la llave 'title' en el array de opciones, es decir: -`['title' => 'Contact me']` +```php +['title' => 'Contact me'] +``` Si el título no es definido y estás usando la opción `translate_texts`, buscará la llave de idioma para el menu item, siguiendo esta convención: `menu.[key]`, por ejemplo: -``` +```php [ 'home' => ['url' => '/'] ] @@ -104,15 +124,15 @@ Si no se encuentra ni la opción title o la llave de idioma, el componente gener [Aprender más sobre traducir textos](internationalization.md) -##id +## id La llave del menu item será utilizada por defecto como atributo id del elemento HTML del menú. En caso de necesitar sustituir este comportamiento se puede pasar la opción 'id'. -##submenu +## submenu Se puede especificar una llave submenu y pasar otro array de menu items, así: -``` +```php [ 'home' => [], 'pages' => [ @@ -126,13 +146,13 @@ Se puede especificar una llave submenu y pasar otro array de menu items, así: Los items del sub-menu serán renderizados con las mismas opciones y fallbacks que el menu item. -##active +## active Todos los menu items tendrán establecido el valor active en false por defecto, a menos que la URL de un menu item o sub-menu tenga el mismo o parcial valor que la URL actual. Por ejemplo: -``` +```php [ 'news' => ['url' => 'news/'] ] @@ -140,7 +160,7 @@ Por ejemplo: Será considerada la URL activa si el actual URL es news/ o news/algun-slug -##CSS classes +## CSS classes Puedes pasar clases de CSS para un menu item determinado usando la opción 'class'. @@ -148,19 +168,21 @@ El item activo también tendrá la clase 'active' y los items con sub-menus tend Puedes personalizar estas clases usando: -``` +```blade {!! Menu::make('items') ->setActiveClass('Active') ->setDropDownClass('dropdown') !!} ``` -##Renderizar menús and plantillas personalizadas +## Renderizar menús and plantillas personalizadas Si tratas `Menu::make` como un string, el menú será renderizado automáticamente, pero también se puede llamar el método `render`, el cual acepta como argumento opcional una plantilla personalizada, así: -`{!! Menu::make('menu.items')->render('custom-template') !!}` +```blade +{!! Menu::make('menu.items')->render('custom-template') !!} +``` -##Access handler +## Access handler Muchas veces es útil mostrar o esconder opciones para usuarios invitados o registrados con ciertos roles, para ello se puede usar el Access Handler incluído en este componente: diff --git a/docs/es/themes.md b/docs/es/themes.md index 8a9d69f..b16bae4 100644 --- a/docs/es/themes.md +++ b/docs/es/themes.md @@ -4,12 +4,15 @@ Este paquete fue creado teniendo en cuenta que hay un montón de frameworks de C Para cambiar o personalizar un theme, simplemente ejecuta: -`php artisan vendor:publish` +```bash +php artisan vendor:publish +``` Luego ir a `config/html.php` y cambiar el valor de theme: -``` +```php //config/html.php + return [ 'theme' => 'custom-theme' ]; @@ -23,11 +26,17 @@ Si es necesario puedes cambiar todas las plantillas dentro de ese directorio o a Quizás no necesites crear o usar un nuevo theme y simplemente necesitas sustituir una plantilla determinada; esto se puede hacer también, debido a que la mayoría de los métodos lo soporta, por ejemplo: -`{!! Menu::make('menu.items')->render('custom-template') !!}` +```blade +{!! Menu::make('menu.items')->render('custom-template') !!} +``` -`{!! Alert::render('custom-template') !!}` +```blade +{!! Alert::render('custom-template') !!} +``` -`{!! Field::email('email', ['template' => 'custom-template'])` +```blade +{!! Field::email('email', ['template' => 'custom-template']) +``` ## Personalizar plantillas por tipo de campo (field builder) From 660417004bd32de232235ddcc2f7d340d87f7e90 Mon Sep 17 00:00:00 2001 From: Dimitri Acosta Date: Wed, 23 Jan 2019 07:12:41 -0700 Subject: [PATCH 132/217] Fix en doc files --- docs/access-handler.md | 34 ++++++--- docs/alert-messages.md | 63 ++++++++++------ docs/field-builder.md | 140 +++++++++++++++++++++-------------- docs/form-builder.md | 14 ++-- docs/html-builder.md | 14 +++- docs/internationalization.md | 37 +++++---- docs/menu-generator.md | 56 +++++++++----- docs/themes.md | 21 ++++-- 8 files changed, 244 insertions(+), 135 deletions(-) diff --git a/docs/access-handler.md b/docs/access-handler.md index b582585..c74ac51 100644 --- a/docs/access-handler.md +++ b/docs/access-handler.md @@ -22,11 +22,13 @@ Just pass one of the following options as a field attribute or menu item value: #### Form fields -`{!! Field::select('user_id', null, ['role' => 'admin'])` +```blade +{!! Field::select('user_id', null, ['role' => 'admin']) +``` #### Menu items -``` +```php // config/menu.php return [ @@ -44,7 +46,9 @@ return [ ]; ``` -`{!! Menu::make('menu.items') !}}` +```blade +{!! Menu::make('menu.items') !}} +``` ## Gate authorization @@ -56,14 +60,19 @@ If it is an array, the first position of the array will be the name of the abili Examples: -`{!! Field::text('change-password', ['allows' => 'change-password']) !!}` -`{!! Field::select('category', $options, ['allows' => ['change-post-category', $category]]) !!}` +```blade +{!! Field::text('change-password', ['allows' => 'change-password']) !!} +``` + +```blade +{!! Field::select('category', $options, ['allows' => ['change-post-category', $category]]) !!} +``` If you are building menus, you can use dynamic parameters to pass values to the gate. In the following example we will define a dynamic 'post' parameter, and pass it using setParam when building the menu: -``` +```php // config/menu.php return [ @@ -76,7 +85,9 @@ return [ ]; ``` -`{!! Menu::make('menu.items')->setParam('post', $post)->render() !}}` +```blade +{!! Menu::make('menu.items')->setParam('post', $post)->render() !}} +``` ## Customization @@ -86,7 +97,7 @@ If you are working on a complex project with lots of different access rules, etc If you want to use the access handler class as a standalone component, please add this global alias in `config/app.php` -``` +```php 'aliases' => [ // ... 'Access' => Styde\Html\Facades\Access, @@ -96,7 +107,7 @@ If you want to use the access handler class as a standalone component, please ad Then you can use the facade wherever you want: -``` +```php @if (Access:check(['roles' => ['admin, 'editor']]))

      id]) }}'> @@ -110,7 +121,7 @@ Then you can use the facade wherever you want: You can deactivate this component in the configuration: -``` +```php //config/html.php return [ //.. @@ -118,4 +129,5 @@ return [ //.. ]; ``` -By doing this, the callback, logged and roles attributes will simply be ignored and all users will be able to see all items. \ No newline at end of file + +By doing this, the callback, logged and roles attributes will simply be ignored and all users will be able to see all items. diff --git a/docs/alert-messages.md b/docs/alert-messages.md index c2d2679..aa592fa 100644 --- a/docs/alert-messages.md +++ b/docs/alert-messages.md @@ -2,38 +2,42 @@ This component will allow you to generate complex alert messages. - ``` - Alert::info('Your account is about to expire') - ->details('Renew now to learn about:') - ->items([ - 'Laravel', - 'PHP, - 'And more', - ]) - ->button('Renew now!', '#', 'primary'); + ```php +Alert::info('Your account is about to expire') + ->details('Renew now to learn about:') + ->items([ + 'Laravel', + 'PHP, + 'And more', + ]) + ->button('Renew now!', '#', 'primary'); ``` The messages will persist in the session until they are presented to the user with: -`{!! Alert::render() !!}` +```blade +{!! Alert::render() !!} +``` ## Create new alert messages You can generate new alert messages with: -`{!! Alert::message('This is a message', 'alert-type') !!}` +```blade +{!! Alert::message('This is a message', 'alert-type') !!} +``` The first argument is the text of the message, and the second one is the type of alert. For example: -``` +```php Alert::message('The end is near', 'danger'); ``` You can also use magic methods, the name of the method then becomes the alert type: -``` +```php Alert::success("It's all good now"); ``` @@ -45,19 +49,25 @@ You can specify more options by method chaining: You can pass a more detailed message chaining the details() method: -`{!! Alert::info('Some info')->details('A more detailed explanation goes here') !!}` +```blade +{!! Alert::info('Some info')->details('A more detailed explanation goes here') !!} +``` ### call to actions You can assign buttons to an alert message: -`{!! Alert::info()->button('Call to action', 'some-url', 'primary') !!}` +```blade +{!! Alert::info()->button('Call to action', 'some-url', 'primary') !!} +``` ### html You can directly pass HTML to the alert message -`{!! Alert::info()->html('HTML goes here') !!}` +```blade +{!! Alert::info()->html('HTML goes here') !!} +``` Be careful since this won't be escaped @@ -65,19 +75,23 @@ Be careful since this won't be escaped You can even render a partial inside an alert message: -`{!! Alert::info()->view('partials/alerts/partial') !!}` +```blade +{!! Alert::info()->view('partials/alerts/partial') !!} +``` ### items You can pass an array of items (maybe an error list): -`{!! Alert::danger('Please fix these errors')->items($errors) !!}` +```blade +{!! Alert::danger('Please fix these errors')->items($errors) !!} +``` ## Persist alert messages Add the following middleware to the `$middleware` array in `app/Http/Kernel.php` **BEFORE** the `\App\Http\Middleware\EncryptCookies`: -``` +```php protected $middleware = [ //... \Styde\Html\Alert\Middleware::class @@ -95,8 +109,9 @@ If the `'translate_texts'` options is set to true in the configuration (it's tru If you don't need to use the translator component, just set translate_texts to false in the configuration: -``` +```php //config/html.php + return [ //... 'translate_texts' => false @@ -108,8 +123,12 @@ return [ By default, the alert messages will be rendered with the default template, located in themes/[theme]/alert, for example, for the Bootstrap theme that would be: -`vendor/styde/html/themes/bootstrap/alert.blade.php` +``` +vendor/styde/html/themes/bootstrap/alert.blade.php +``` You can pass a custom template as the first argument of the render() method, i.e.: -`{!! Alert::render('partials/custom-alert-template') !!}` \ No newline at end of file +```blade +{!! Alert::render('partials/custom-alert-template') !!} +``` diff --git a/docs/field-builder.md b/docs/field-builder.md index f40f5e9..16c6f6d 100644 --- a/docs/field-builder.md +++ b/docs/field-builder.md @@ -4,11 +4,15 @@ This component will allow you to generate the entire markup for form fields with If you have used the Laravel Collective HTML component before, you already know how to use the basics of this component; simply replace the alias “Form” with “Field”, for example, replace: -`{!! Form::text(‘name’, ‘value’, $attributes) !!}` +```blade +{!! Form::text(‘name’, ‘value’, $attributes) !!} +``` With this: -`{!! Field::text(‘name’, ‘value’, $attributes) !!}` +```blade +{!! Field::text(‘name’, ‘value’, $attributes) !!} +``` This will generate a field including the container, the label, the control and any errors associated with it. @@ -20,18 +24,24 @@ There are also other convenient options and handy fallbacks: If you don't want to pass a value argument (`null`) but you want to pass the array of `$attributes`, you can skip the second argument. i.e. this: -`{!! Field::text(‘name’, $attributes) !!}` +```blade +{!! Field::text(‘name’, $attributes) !!} +``` Is the same as: -`{!! Field::text(‘name’, null, $attributes) !!}` +```blade +{!! Field::text(‘name’, null, $attributes) !!} +``` ## Labels: You can explicitly pass a label for a field as part of the attributes array, i.e.: -`{!! Field::text(’name’, [‘label’ => ‘Full name’]) !!}` +```blade +{!! Field::text(’name’, [‘label’ => ‘Full name’]) !!} +``` As a second option, you can store the labels in the lang/ folder with the same convention used to store the attribute's names for the validation errors: @@ -47,7 +57,9 @@ If you skip both options, then the FieldBuilder will generate a label based on t By default, the fields will be rendered with the default template, located in the [theme]/fields folder, for example, for the Bootstrap theme that would be: -`vendor/styde/html/themes/bootstrap/fields/default.blade.php` +``` +vendor/styde/html/themes/bootstrap/fields/default.blade.php +``` But you have options to customize the template used for a particular type or field: @@ -55,16 +67,16 @@ But you have options to customize the template used for a particular type or fie Some CSS frameworks (like Bootstrap) need different markups for different form field types, so you can use the configuration to assign a different template to a particular field type, like this: -``` - 'themes' => [ - 'bootstrap' => [ - 'field_templates' => [ - 'checkbox' => 'checkbox', - 'checkboxes' => 'collection', - 'radios' => 'collection' - ], - //... - //... +```php +'themes' => [ + 'bootstrap' => [ + 'field_templates' => [ + 'checkbox' => 'checkbox', + 'checkboxes' => 'collection', + 'radios' => 'collection' + ], + //... +//... ``` With this configuration, the "checkbox" fields will use the `vendor/styde/html/themes/bootstrap/fields/checkbox.blade.php` template by default, while the "checkboxes" and "radios" fields will use the `vendor/styde/html/themes/bootstrap/fields/collection.blade.php` template. @@ -77,7 +89,9 @@ Note: you'd only have to worry about the theme you actually need, so if you don' You can specify a custom `template` for a single field through the `'template key'` of the `$attributes` array, like this: -`{!! Field::text(’name’, [’template’ => ’templates/my_template’]) !!}` +```blade +{!! Field::text(’name’, [’template’ => ’templates/my_template’]) !!} +``` The path will be relative to the resources/views/ directory. @@ -99,20 +113,26 @@ It will be assigned automatically to each input control, if you use dot syntax ( You can specify a 'required' value in the attributes array: -`{!! Field::text(’name’, [’required’]) !!}` +```blade +{!! Field::text(’name’, [’required’]) !!} +``` Or as a key => value pair (the field will be marked as required if the value evaluates to true, i.e.: -`$required = true;` +```php +$required = true; +``` -`{!! Field::text(’name’, null, [’required’ => $required]) !!}` +```blade +{!! Field::text(’name’, null, [’required’ => $required]) !!} +``` The field templates will always have a ‘required’ variable so you can use it to print extra CSS classes or badges, to indicate whether a field is required or optional, i.e.: -``` - @if ($required) - Required - @endif +```blade +@if ($required) + Required +@endif ``` ## Errors: @@ -123,19 +143,21 @@ For example, with Twitter Bootstrap you will need a `has-error` class in case yo This is an extract of the fields/default template for the Bootstrap theme: -` $hasErrors]) !!}>` +```blade + $hasErrors]) !!}> +``` Inputs, selects, textareas, etc. with errors will also get an extra CSS class, you can configure this in: -``` - 'themes' => [ - 'bootstrap' => [ - //... - 'field_classes' => [ - //... - 'error' => 'input-with-feedback' +```php +'themes' => [ + 'bootstrap' => [ + //... + 'field_classes' => [ //... + 'error' => 'input-with-feedback' + //... ``` And once again, if you are using Twitter Bootstrap, any field with errors will get the `input-with-feedback` class. This is also required for showing the input in red color. @@ -144,11 +166,13 @@ And once again, if you are using Twitter Bootstrap, any field with errors will g For selects, radios and checkboxes, you can skip the options argument or pass null: -`{!! Field::select('os') !!}` or `{!! Field::select('os', null) !!}` +```blade +{!! Field::select('os') !!}` or `{!! Field::select('os', null) !!} +``` If there is a model bound to the form, then the FieldBuilder will check if there is a method called: `get[fieldName]Options` in that case, it will be called and the returned value will be used as the options, i.e.: -``` +```php class Product extends Model //... @@ -165,7 +189,9 @@ class Product extends Model Select fields usually need an empty option, (like "Select something please"), you can pass it as the `'empty'` attribute, like this: -`{!! Field::select('os', null, ['empty' => 'Select your favorite operative system']) !!}` +```blade +{!! Field::select('os', null, ['empty' => 'Select your favorite operative system']) !!} +``` If you set the empty attribute to `false`, i.e. `['empty' => false]` , the empty option won't be rendered. @@ -181,20 +207,22 @@ At last if none of these options is not found, it will use an empty string as th To save some key strokes, you can use abbreviations instead of the full name of the attributes, pass them in the configuration: -``` - /* - * Specify abbreviations for the form field attributes - */ - 'abbreviations' => [ - 'ph' => 'placeholder', - 'max' => 'maxlength', - 'tpl' => 'template' - ], +```php +/* + * Specify abbreviations for the form field attributes + */ +'abbreviations' => [ + 'ph' => 'placeholder', + 'max' => 'maxlength', + 'tpl' => 'template' +], ``` Then you can do things like this: -`{!! Field::text('name', ['ph' => 'This will be the placeholder]) !!}` +```blade +{!! Field::text('name', ['ph' => 'This will be the placeholder]) !!} +``` ## CSS classes @@ -204,17 +232,17 @@ You can pass custom CSS classes for each field using the 'class' key of the attr Using the configuration, you can assign default CSS class for every field according to its type: -``` - 'themes' => [ - //... - 'bootstrap' => [ - //... - 'field_classes' => [ - // type => CSS class or classes - 'default' => 'form-control', - 'checkbox' => '', - 'error' => 'input-with-feedback' - ], +```php +'themes' => [ + //... + 'bootstrap' => [ + //... + 'field_classes' => [ + // type => CSS class or classes + 'default' => 'form-control', + 'checkbox' => '', + 'error' => 'input-with-feedback' + ], ``` Of course this is theme specific, since it would be impossible to convince all CSS framework authors to use the same classes... diff --git a/docs/form-builder.md b/docs/form-builder.md index d0e374f..75c8991 100644 --- a/docs/form-builder.md +++ b/docs/form-builder.md @@ -4,7 +4,7 @@ Allow users to set the novalidate option for every form generated with the `Form::open` or `Form::model` method, so developers can skip HTML5 validation in order to test backend validation in local or development environments: -``` +```php return [ //.. 'novalidate' => true @@ -18,7 +18,9 @@ Create a list of radios. This function is similar to `Form::select` but it generates a collection of radios instead of options. i.e.: -`Form::radios('status', ['a' => 'Active', 'i' => 'Inactive'])` +```blade +Form::radios('status', ['a' => 'Active', 'i' => 'Inactive']) +``` You can pass 'inline' as a value in the attributes array, to set the radios as inline (they'll be rendered with the 'radios-inline' template). @@ -28,7 +30,7 @@ Create a list of checkboxes. This function is similar to Form::select, but it generates a collection of checkboxes instead of options, i.e.: -``` +```php $tags = [ 'php' => 'PHP', 'python' => 'Python', @@ -39,6 +41,8 @@ $tags = [ $checked = ['php', 'js']; ``` -`{!! Form::checkboxes('tags', $tags, $checked) !!}` +```blade +{!! Form::checkboxes('tags', $tags, $checked) !!} +``` -You can pass 'inline' as a value of the attribute's array, to set the checkboxes as inline (they'll be rendered using the 'checkboxes-inline' template). \ No newline at end of file +You can pass 'inline' as a value of the attribute's array, to set the checkboxes as inline (they'll be rendered using the 'checkboxes-inline' template). diff --git a/docs/html-builder.md b/docs/html-builder.md index 53e21d4..349de3b 100644 --- a/docs/html-builder.md +++ b/docs/html-builder.md @@ -10,16 +10,22 @@ You can specify one or more CSS classes as a key and a condition as a value. If Example: -`{!! Html::classes(['home' => true, 'main', 'dont-use-this' => false]) !!}` +```blade +{!! Html::classes(['home' => true, 'main', 'dont-use-this' => false]) !!} +``` Returns: ` class="home main"`. Note that this function returns an empty space before the class attribute. So don't add another one, in other words use it like this: -`` +```blade + +``` instead of this: -`

      ` +```blade +

      +``` -If no classes are evaluated as TRUE then this function will return an empty string. \ No newline at end of file +If no classes are evaluated as TRUE then this function will return an empty string. diff --git a/docs/internationalization.md b/docs/internationalization.md index 800d528..b398bf7 100644 --- a/docs/internationalization.md +++ b/docs/internationalization.md @@ -2,8 +2,9 @@ You can configure whether you want this package to attempt to translate texts or not, for example if your project only needs to implement one language and you prefer to simply write texts wherever you need them instead of using the Translator component, please deactivate translations in the configuration: -``` +```php //config/html.php + return [ //... 'translate_texts' => false @@ -19,16 +20,19 @@ But if your project needs to implement more than one language or you want to org If you want to have a specific label in a field, you can do so by passing it as part of the attribute array: - `{!! Field::text('name', ['label' => 'Full name']) !!}` +```blade +{!! Field::text('name', ['label' => 'Full name']) !!} +``` But you can also define it as part of the `attributes` array in the `resources/lang/en/validation.php` file: - ``` - //resources/lang/en/validation.php - //.. - 'attributes' => [ - 'name' => 'Full name' - ], +```php + //resources/lang/en/validation.php + + //.. + 'attributes' => [ + 'name' => 'Full name' + ], ``` Note that this is also the convention used by the Laravel Validator component, so this way you can have all your label texts in one place. @@ -39,10 +43,11 @@ Note that this is also the convention used by the Laravel Validator component, s If the `'translate_texts'` is set to `true`, this component will assume that all the alert messages are in fact language keys and will try to translate them. This means you can do things like this: -``` +```php Alert::success('messages.users.updated') ->button('messages.users.go_to_profile', url('users/profile')) ``` + Of course if the lang key is not found, it will return the literal string (so you can also pass the full message instead of a lang key). [Learn more about the alert component](alert-messages.md) @@ -51,15 +56,17 @@ Of course if the lang key is not found, it will return the literal string (so yo If the `'translate_texts'` is set to `true`, and you don't specify an explicit title for a menu item; the component will search for a lang key in: `menu.[menu_item_key]` if the key is not found, the package will attempt to convert the menu item key in a title format. For example: -``` +```php //resources/lang/en/menu.php + return [ 'home' => 'Homepage' ]; ``` -``` +```php //config/menu.php + return [ 'items' => [ 'home' => [], @@ -69,11 +76,13 @@ return [ ]; ``` -`{!! Menu::make('menu.items') !!}` +```blade +{!! Menu::make('menu.items') !!} +``` Will return something like: -``` +```html

      • Homepage
      • Who we are
      • @@ -87,4 +96,4 @@ Note that: * "Who we are" is explicit defined (no translation is attempted) * "Contact us" is generated from the key "contact-us" (since no lang key is provided) -[Learn more about the menu generator](menu-generator.md) \ No newline at end of file +[Learn more about the menu generator](menu-generator.md) diff --git a/docs/menu-generator.md b/docs/menu-generator.md index 0588166..94e93b1 100644 --- a/docs/menu-generator.md +++ b/docs/menu-generator.md @@ -6,11 +6,13 @@ So instead of adding too much extra HTML and Blade boilerplate code, you can use To generate a menu simply add the following code in your layout's template: -`{!! Menu::make('items.here', 'optional css classes') !!}` +```blade +{!! Menu::make('items.here', 'optional css classes') !!} +``` `'items.here'` can be an array or a configuration key (that contains an array), there you will specify the menu items, for example: -``` +```php [ 'home' => ['url' => ''], 'about' => ['title' => 'Who we are', 'url' => 'about-us'], @@ -30,23 +32,31 @@ Of course this is the most important part of each menu item, and therefore you h If you pass a 'full_url' key within the item configuration, it will return it as the URL with no additional action, i.e.: -`['full_url' => 'https://styde.net']` +```php +['full_url' => 'https://styde.net'] +``` ### url You can pass a relative URL, using the 'url' key. The final URL will be generated using the method `UrlGenerator::to`, i.e.: -`['url' => 'contact-us']` +```php +['url' => 'contact-us'] +``` You can also pass a 'secure' key to indicate whether this particular URL should use https or not. You can also specify a default secure value using the `setDefaultSecure` method (false by default). -`['url' => 'login', 'secure' => 'true']` +```php +['url' => 'login', 'secure' => 'true'] +``` ### route You can specify a route's name for a menu item. -`['route' => 'home']` +```php +['route' => 'home'] +``` ### route with parameters @@ -54,7 +64,9 @@ You can specify a route with parameters if you pass an array instead of a string The first value will be taken as the route's name, and the others will be the route's parameters. -`['route' => ['profile', 'sileence']]` +```php +['route' => ['profile', 'sileence']] +``` ### action @@ -74,25 +86,33 @@ If none of above options is found, then the URL will simply be a placeholder "#" Sometimes you will need to use dynamic parameters to build routes and actions, in that case, instead of a value, pass a name precede with :, for example: -`['route' => ['profile', ':username']]` +```php +['route' => ['profile', ':username']] +``` Then you can assign a value using the setParams or setParam method, like this: -`{!! Menu::make('config.items')->setParam('username', 'sileence') !!}` +```blade +{!! Menu::make('config.items')->setParam('username', 'sileence') !!} +``` Or this: -`{!! Menu::make('config.items')->setParams(['username' => 'sileence']) !!}` +```blade +{!! Menu::make('config.items')->setParams(['username' => 'sileence']) !!} +``` ## title You can specify a title for a menu item using the 'title' key in the options array, i.e.: -`['title' => 'Contact me']` +```php +['title' => 'Contact me'] +``` If no title is set and you are using the translate texts option, it will search for a lang key for the menu item, following this convention: `menu.[key]`, for example: -``` +```php [ 'home' => ['url' => '/'] ] @@ -112,7 +132,7 @@ The menu's item key will be used as the menu's item HTML id attribute by default You can specify a sub-menu key and pass another array of menu items, like this: -``` +```php [ 'home' => [], 'pages' => [ @@ -132,7 +152,7 @@ All menu items will have the active value set to false as default, unless the UR For example: -``` +```php [ 'news' => ['url' => 'news/'] ] @@ -148,7 +168,7 @@ The active item will also get the 'active' class, and the items with sub-menus w You can customize these classes using: -``` +```php {!! Menu::make('items') ->setActiveClass('Active') ->setDropDownClass('dropdown') !!} @@ -158,10 +178,12 @@ You can customize these classes using: The menu will be rendered automatically if you treat `Menu::make` as a string, but you can also call the render method which accepts an optional custom template as an argument, like this: -`{!! Menu::make('menu.items')->render('custom-template') !!}` +```blade +{!! Menu::make('menu.items')->render('custom-template') !!} +``` ## Access handler It is often useful to show or hide options for guest or logged users with certain roles, you can do this using the Access Handler included in this component: -[Learn more about the access handler](access-handler.md) \ No newline at end of file +[Learn more about the access handler](access-handler.md) diff --git a/docs/themes.md b/docs/themes.md index 58491f6..a13b152 100644 --- a/docs/themes.md +++ b/docs/themes.md @@ -4,12 +4,15 @@ There are a lot of CSS (and all kind of) frameworks out there, this package was To change or customize a theme, simply run: -`php artisan vendor:publish` +```bash +php artisan vendor:publish +``` Then go to `config/html.php` and change the theme value: -``` +```php //config/html.php + return [ 'theme' => 'custom-theme' ]; @@ -23,11 +26,17 @@ Then you can change all the templates within that directory or add new ones if y Maybe you don't need to create or use a new theme and you just simply need to override a particular template, you can do this too, since most methods support that, for example: -`{!! Menu::make('menu.items')->render('custom-template') !!}` +```blade +{!! Menu::make('menu.items')->render('custom-template') !!} +``` -`{!! Alert::render('custom-template') !!}` +```blade +{!! Alert::render('custom-template') !!} +``` -`{!! Field::email('email', ['template' => 'custom-template'])` +```blade +{!! Field::email('email', ['template' => 'custom-template']) +``` ## Customize templates by field type (field builder) @@ -35,4 +44,4 @@ Are you using a CSS framework that requires a different markup for a particular ## Pull requests -If you create a theme for a popular CSS framework, you can collaborate by forking this repository, and creating a pull request, remember to store the templates in the `themes/` folder. Thank you. \ No newline at end of file +If you create a theme for a popular CSS framework, you can collaborate by forking this repository, and creating a pull request, remember to store the templates in the `themes/` folder. Thank you. From 8cddb4c0414a219935c446c46d0f4b035de1b703 Mon Sep 17 00:00:00 2001 From: Dimitri Acosta Date: Wed, 23 Jan 2019 07:19:47 -0700 Subject: [PATCH 133/217] Fix title style --- docs/access-handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/access-handler.md b/docs/access-handler.md index c74ac51..9fde2ef 100644 --- a/docs/access-handler.md +++ b/docs/access-handler.md @@ -18,7 +18,7 @@ Just pass one of the following options as a field attribute or menu item value: *WARNING*: note this package will only prevents the elements from appearing in the front end, you still need to protect the backend access using middleware, etc. -##Usage +## Usage #### Form fields From 851bde2c4ecf7253750906b7e4e4853895e6d0af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Tue, 12 Feb 2019 12:00:34 -0400 Subject: [PATCH 134/217] Move setAttribute method to HasAttributes trait --- src/FormModel/Field.php | 17 ----------------- src/FormModel/HasAttributes.php | 19 ++++++++++++++++++- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/FormModel/Field.php b/src/FormModel/Field.php index 7f6a9d0..a2a1f5d 100644 --- a/src/FormModel/Field.php +++ b/src/FormModel/Field.php @@ -237,23 +237,6 @@ public function placeholder($value) return $this; } - /** - * Set a new attribute in the field - * - * @param $name - * @param null $value - */ - protected function setAttribute($name, $value = null) - { - if (! in_array($name, $this->attributes)) { - if (! $value) { - $this->attributes[] = $name; - } else { - $this->attributes[$name] = $value; - } - } - } - /** * Render field with all attributes * diff --git a/src/FormModel/HasAttributes.php b/src/FormModel/HasAttributes.php index 1b7769e..bc99dca 100644 --- a/src/FormModel/HasAttributes.php +++ b/src/FormModel/HasAttributes.php @@ -36,6 +36,23 @@ public function classes($classes) return $this->attributes('class', $classes); } + /** + * Set a new attribute in the field + * + * @param $name + * @param null $value + */ + protected function setAttribute($name, $value = null) + { + if (! in_array($name, $this->attributes)) { + if (! $value) { + $this->attributes[] = $name; + } else { + $this->attributes[$name] = $value; + } + } + } + /** * Set a new attribute in the field and set all rules of the attributes * @@ -48,7 +65,7 @@ public function attr($attributes, $value = null) if (is_array($attributes)) { $this->attributes = array_merge($this->attributes, $attributes); } else { - $this->attributes[$attributes] = $value; + $this->setAttribute($attributes, $value); } $this->addRulesOfAttributes(); From 8a24ea7baaca5cd9b584d82773fdc7ebf8f5502b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Fri, 15 Feb 2019 20:43:31 -0400 Subject: [PATCH 135/217] Add Bulma Theme --- config.php | 35 ++++++++++++---- src/HtmlServiceProvider.php | 14 +++++-- tests/snapshots/bulma/alert/alert.html | 10 +++++ tests/snapshots/bulma/alert/complex.html | 22 ++++++++++ .../bulma/alert/custom-template.html | 8 ++++ tests/snapshots/bulma/alert/magic.html | 20 +++++++++ .../bulma/alert/with-partial-view.html | 11 +++++ tests/snapshots/bulma/bulma/alert.blade.php | 32 ++++++++++++++ .../bulma/bulma/fields/checkbox.blade.php | 16 +++++++ .../bulma/bulma/fields/collections.blade.php | 20 +++++++++ .../bulma/bulma/fields/default.blade.php | 20 +++++++++ .../bulma/bulma/fields/selects.blade.php | 20 +++++++++ tests/snapshots/bulma/bulma/form.blade.php | 4 ++ .../bulma/forms/checkboxes-inline.blade.php | 13 ++++++ .../bulma/bulma/forms/checkboxes.blade.php | 8 ++++ .../bulma/bulma/forms/radios-inline.blade.php | 14 +++++++ .../bulma/bulma/forms/radios.blade.php | 10 +++++ tests/snapshots/bulma/bulma/menu.blade.php | 22 ++++++++++ .../bulma/field-collection/fields.html | 28 +++++++++++++ tests/snapshots/bulma/field/checkbox.html | 10 +++++ tests/snapshots/bulma/field/checkboxes.html | 31 ++++++++++++++ .../bulma/field/checkboxes_help_text.html | 32 ++++++++++++++ tests/snapshots/bulma/field/email.html | 14 +++++++ tests/snapshots/bulma/field/file.html | 14 +++++++ tests/snapshots/bulma/field/hidden.html | 1 + tests/snapshots/bulma/field/input.html | 14 +++++++ .../bulma/field/password-required.html | 15 +++++++ tests/snapshots/bulma/field/radios.html | 25 +++++++++++ tests/snapshots/bulma/field/select-empty.html | 14 +++++++ .../bulma/field/select-from-model.html | 14 +++++++ tests/snapshots/bulma/field/select-group.html | 14 +++++++ .../bulma/field/select-multiple.html | 14 +++++++ .../select-without-options-from-model.html | 14 +++++++ tests/snapshots/bulma/field/select.html | 14 +++++++ .../snapshots/bulma/field/text-custom-id.html | 14 +++++++ .../bulma/field/text-custom-label.html | 14 +++++++ .../bulma/field/text-custom-template.html | 7 ++++ .../snapshots/bulma/field/text-dots-name.html | 14 +++++++ .../snapshots/bulma/field/text-help-text.html | 14 +++++++ .../bulma/field/text-not-required.html | 14 +++++++ .../bulma/field/text-raw-help-text.html | 14 +++++++ .../snapshots/bulma/field/text-required.html | 15 +++++++ .../bulma/field/text-with-raw-label.html | 14 +++++++ tests/snapshots/bulma/field/text.html | 14 +++++++ .../bulma/field/text_with_errors.html | 15 +++++++ tests/snapshots/bulma/field/textarea.html | 14 +++++++ tests/snapshots/bulma/field/url.html | 14 +++++++ .../form-model/form-with-novalidate.html | 42 +++++++++++++++++++ .../bulma/form-model/login-form.html | 42 +++++++++++++++++++ .../form-model/user-form-for-update.html | 32 ++++++++++++++ tests/snapshots/bulma/form/checkboxes.html | 18 ++++++++ tests/snapshots/bulma/form/csrf-field.html | 1 + tests/snapshots/bulma/form/get-method.html | 1 + tests/snapshots/bulma/form/put-method.html | 1 + tests/snapshots/bulma/form/radios.html | 12 ++++++ tests/snapshots/bulma/form/route.html | 1 + .../snapshots/bulma/menu/access-handler.html | 14 +++++++ .../snapshots/bulma/menu/custom-template.html | 9 ++++ tests/snapshots/bulma/menu/menu-classes.html | 8 ++++ tests/snapshots/bulma/menu/menu.html | 14 +++++++ tests/snapshots/bulma/menu/parameters.html | 8 ++++ tests/snapshots/bulma/menu/raw-urls.html | 8 ++++ tests/snapshots/bulma/menu/routes.html | 8 ++++ tests/snapshots/bulma/menu/secure-urls.html | 8 ++++ .../snapshots/bulma/menu/submenu-active.html | 18 ++++++++ tests/snapshots/bulma/menu/submenu.html | 18 ++++++++ themes/bulma/alert.blade.php | 29 +++++++++++++ themes/bulma/fields/checkbox.blade.php | 16 +++++++ themes/bulma/fields/collections.blade.php | 20 +++++++++ themes/bulma/fields/default.blade.php | 20 +++++++++ themes/bulma/fields/selects.blade.php | 20 +++++++++ themes/bulma/form.blade.php | 4 ++ .../bulma/forms/checkboxes-inline.blade.php | 13 ++++++ themes/bulma/forms/checkboxes.blade.php | 8 ++++ themes/bulma/forms/radios-inline.blade.php | 14 +++++++ themes/bulma/forms/radios.blade.php | 10 +++++ themes/bulma/menu.blade.php | 18 ++++++++ 77 files changed, 1167 insertions(+), 12 deletions(-) create mode 100644 tests/snapshots/bulma/alert/alert.html create mode 100644 tests/snapshots/bulma/alert/complex.html create mode 100644 tests/snapshots/bulma/alert/custom-template.html create mode 100644 tests/snapshots/bulma/alert/magic.html create mode 100644 tests/snapshots/bulma/alert/with-partial-view.html create mode 100644 tests/snapshots/bulma/bulma/alert.blade.php create mode 100644 tests/snapshots/bulma/bulma/fields/checkbox.blade.php create mode 100644 tests/snapshots/bulma/bulma/fields/collections.blade.php create mode 100644 tests/snapshots/bulma/bulma/fields/default.blade.php create mode 100644 tests/snapshots/bulma/bulma/fields/selects.blade.php create mode 100644 tests/snapshots/bulma/bulma/form.blade.php create mode 100644 tests/snapshots/bulma/bulma/forms/checkboxes-inline.blade.php create mode 100644 tests/snapshots/bulma/bulma/forms/checkboxes.blade.php create mode 100644 tests/snapshots/bulma/bulma/forms/radios-inline.blade.php create mode 100644 tests/snapshots/bulma/bulma/forms/radios.blade.php create mode 100644 tests/snapshots/bulma/bulma/menu.blade.php create mode 100644 tests/snapshots/bulma/field-collection/fields.html create mode 100644 tests/snapshots/bulma/field/checkbox.html create mode 100644 tests/snapshots/bulma/field/checkboxes.html create mode 100644 tests/snapshots/bulma/field/checkboxes_help_text.html create mode 100644 tests/snapshots/bulma/field/email.html create mode 100644 tests/snapshots/bulma/field/file.html create mode 100644 tests/snapshots/bulma/field/hidden.html create mode 100644 tests/snapshots/bulma/field/input.html create mode 100644 tests/snapshots/bulma/field/password-required.html create mode 100644 tests/snapshots/bulma/field/radios.html create mode 100644 tests/snapshots/bulma/field/select-empty.html create mode 100644 tests/snapshots/bulma/field/select-from-model.html create mode 100644 tests/snapshots/bulma/field/select-group.html create mode 100644 tests/snapshots/bulma/field/select-multiple.html create mode 100644 tests/snapshots/bulma/field/select-without-options-from-model.html create mode 100644 tests/snapshots/bulma/field/select.html create mode 100644 tests/snapshots/bulma/field/text-custom-id.html create mode 100644 tests/snapshots/bulma/field/text-custom-label.html create mode 100644 tests/snapshots/bulma/field/text-custom-template.html create mode 100644 tests/snapshots/bulma/field/text-dots-name.html create mode 100644 tests/snapshots/bulma/field/text-help-text.html create mode 100644 tests/snapshots/bulma/field/text-not-required.html create mode 100644 tests/snapshots/bulma/field/text-raw-help-text.html create mode 100644 tests/snapshots/bulma/field/text-required.html create mode 100644 tests/snapshots/bulma/field/text-with-raw-label.html create mode 100644 tests/snapshots/bulma/field/text.html create mode 100644 tests/snapshots/bulma/field/text_with_errors.html create mode 100644 tests/snapshots/bulma/field/textarea.html create mode 100644 tests/snapshots/bulma/field/url.html create mode 100644 tests/snapshots/bulma/form-model/form-with-novalidate.html create mode 100644 tests/snapshots/bulma/form-model/login-form.html create mode 100644 tests/snapshots/bulma/form-model/user-form-for-update.html create mode 100644 tests/snapshots/bulma/form/checkboxes.html create mode 100644 tests/snapshots/bulma/form/csrf-field.html create mode 100644 tests/snapshots/bulma/form/get-method.html create mode 100644 tests/snapshots/bulma/form/put-method.html create mode 100644 tests/snapshots/bulma/form/radios.html create mode 100644 tests/snapshots/bulma/form/route.html create mode 100644 tests/snapshots/bulma/menu/access-handler.html create mode 100644 tests/snapshots/bulma/menu/custom-template.html create mode 100644 tests/snapshots/bulma/menu/menu-classes.html create mode 100644 tests/snapshots/bulma/menu/menu.html create mode 100644 tests/snapshots/bulma/menu/parameters.html create mode 100644 tests/snapshots/bulma/menu/raw-urls.html create mode 100644 tests/snapshots/bulma/menu/routes.html create mode 100644 tests/snapshots/bulma/menu/secure-urls.html create mode 100644 tests/snapshots/bulma/menu/submenu-active.html create mode 100644 tests/snapshots/bulma/menu/submenu.html create mode 100644 themes/bulma/alert.blade.php create mode 100644 themes/bulma/fields/checkbox.blade.php create mode 100644 themes/bulma/fields/collections.blade.php create mode 100644 themes/bulma/fields/default.blade.php create mode 100644 themes/bulma/fields/selects.blade.php create mode 100644 themes/bulma/form.blade.php create mode 100644 themes/bulma/forms/checkboxes-inline.blade.php create mode 100644 themes/bulma/forms/checkboxes.blade.php create mode 100644 themes/bulma/forms/radios-inline.blade.php create mode 100644 themes/bulma/forms/radios.blade.php create mode 100644 themes/bulma/menu.blade.php diff --git a/config.php b/config.php index 1afbe08..dab8f0e 100644 --- a/config.php +++ b/config.php @@ -13,13 +13,6 @@ */ 'custom' => 'themes', - /* - * Set to false to deactivate the AccessHandler component - * Doing so the component will run slightly faster but - * the logged and roles checkers won't be available - */ - 'control_access' => true, - /* * Set to false to deactivate the Translator for the alert and menu * components, doing so they will run slightly faster but won't @@ -55,7 +48,33 @@ 'checkboxes' => 'collections', 'radios' => 'collections' ] + ], + /** + * Configuration for Bulma CSS v1.7.2 + */ + 'bulma' => [ + /* + * Set a specific HTML template for a field type if the + * type is not set, the default template will be used + */ + 'field_templates' => [ + // type => template + 'checkbox' => 'checkbox', + 'checkboxes' => 'collections', + 'radios' => 'collections', + 'select'=>'selects', + + ], + /* + * Set the default classes for each field type + */ + 'field_classes' => [ + // type => class or classes + 'textarea' => 'textarea', + 'default' => 'input', + 'checkbox' => 'checkbox', + 'error' => 'is-danger' + ], ] ] - ]; diff --git a/src/HtmlServiceProvider.php b/src/HtmlServiceProvider.php index bb3a32d..191d979 100644 --- a/src/HtmlServiceProvider.php +++ b/src/HtmlServiceProvider.php @@ -82,14 +82,14 @@ public function register() */ protected function loadConfigurationOptions() { - if ( ! empty($this->options)) return; + if (! empty($this->options)) return; $this->mergeDefaultConfiguration(); $this->options = $this->app->make('config')->get('html'); $this->options['theme_values'] = $this->options['themes'][$this->options['theme']]; - unset ($this->options['themes']); + unset($this->options['themes']); } /** @@ -99,7 +99,7 @@ protected function loadConfigurationOptions() */ protected function registerThemeClass() { - $this->app->singleton(Theme::class, function ($app) { + $this->app->singleton(Theme::class, function ($app) { return new Theme($this->app['view'], $this->options['theme'], $this->options['custom']); }); } @@ -147,12 +147,18 @@ protected function registerFieldBuilder() $app['form'], $app->make(Theme::class), $app['translator'] ); - if (isset ($this->options['theme_values']['field_templates'])) { + if (isset($this->options['theme_values']['field_templates'])) { $fieldBuilder->setTemplates( $this->options['theme_values']['field_templates'] ); } + if (isset($this->options['theme_values']['field_classes'])) { + $fieldBuilder->classes( + $this->options['theme_values']['field_classes'] + ); + } + $fieldBuilder->setSessionStore($app['session.store']); return $fieldBuilder; diff --git a/tests/snapshots/bulma/alert/alert.html b/tests/snapshots/bulma/alert/alert.html new file mode 100644 index 0000000..689fe33 --- /dev/null +++ b/tests/snapshots/bulma/alert/alert.html @@ -0,0 +1,10 @@ +
        +
        +

        Alert!

        + +
        +
        +

        This is a message

        + +
        +
        diff --git a/tests/snapshots/bulma/alert/complex.html b/tests/snapshots/bulma/alert/complex.html new file mode 100644 index 0000000..f6b096f --- /dev/null +++ b/tests/snapshots/bulma/alert/complex.html @@ -0,0 +1,22 @@ +
        +
        +

        Alert!

        + +
        +
        +

        Your account is about to expire

        + A lot of knowledge still waits for you: + +
          +
        • Laravel courses
        • +
        • OOP classes
        • +
        • Access to real projects
        • +
        • Support
        • +
        • And more
        • +
        + +
        +
        diff --git a/tests/snapshots/bulma/alert/custom-template.html b/tests/snapshots/bulma/alert/custom-template.html new file mode 100644 index 0000000..5d1150d --- /dev/null +++ b/tests/snapshots/bulma/alert/custom-template.html @@ -0,0 +1,8 @@ +

        Custom alert template

        + diff --git a/tests/snapshots/bulma/alert/magic.html b/tests/snapshots/bulma/alert/magic.html new file mode 100644 index 0000000..ba7bd96 --- /dev/null +++ b/tests/snapshots/bulma/alert/magic.html @@ -0,0 +1,20 @@ +
        +
        +

        Alert!

        + +
        +
        +

        Success!

        + +
        +
        +
        +
        +

        Alert!

        + +
        +
        +

        Some information

        + +
        +
        diff --git a/tests/snapshots/bulma/alert/with-partial-view.html b/tests/snapshots/bulma/alert/with-partial-view.html new file mode 100644 index 0000000..894be0c --- /dev/null +++ b/tests/snapshots/bulma/alert/with-partial-view.html @@ -0,0 +1,11 @@ +
        +
        +

        Alert!

        + +
        +
        +

        +

        Alert partial

        + +
        +
        diff --git a/tests/snapshots/bulma/bulma/alert.blade.php b/tests/snapshots/bulma/bulma/alert.blade.php new file mode 100644 index 0000000..5c6ea9d --- /dev/null +++ b/tests/snapshots/bulma/bulma/alert.blade.php @@ -0,0 +1,32 @@ +@foreach ($messages as $msg) +
        +
        +

        Alert!

        + +
        +
        +

        {{ $msg['message'] }}

        + @if (!empty ($msg['details'])) + {{ $msg['details'] }} + @endif + {!! $msg['html'] !!} + @if (!empty ($msg['items'])) +
          + @foreach ($msg['items'] as $item) +
        • {{ $item }}
        • + @endforeach +
        + @endif + @if ( ! empty ($msg['buttons'])) +
        + @foreach ($msg['buttons'] as $btn) + {{ $btn['text'] }} + @endforeach +
        + @endif +
        +
        +@endforeach + + + diff --git a/tests/snapshots/bulma/bulma/fields/checkbox.blade.php b/tests/snapshots/bulma/bulma/fields/checkbox.blade.php new file mode 100644 index 0000000..6150af4 --- /dev/null +++ b/tests/snapshots/bulma/bulma/fields/checkbox.blade.php @@ -0,0 +1,16 @@ +
        +
        + {!! $input !!} + +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        diff --git a/tests/snapshots/bulma/bulma/fields/collections.blade.php b/tests/snapshots/bulma/bulma/fields/collections.blade.php new file mode 100644 index 0000000..b38d46f --- /dev/null +++ b/tests/snapshots/bulma/bulma/fields/collections.blade.php @@ -0,0 +1,20 @@ +
        + +
        +
        +
        + {!! $input !!} +
        +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/bulma/fields/default.blade.php b/tests/snapshots/bulma/bulma/fields/default.blade.php new file mode 100644 index 0000000..64c7ecb --- /dev/null +++ b/tests/snapshots/bulma/bulma/fields/default.blade.php @@ -0,0 +1,20 @@ +
        + +
        +
        +
        + {!! $input !!} +
        +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/bulma/fields/selects.blade.php b/tests/snapshots/bulma/bulma/fields/selects.blade.php new file mode 100644 index 0000000..10fe44c --- /dev/null +++ b/tests/snapshots/bulma/bulma/fields/selects.blade.php @@ -0,0 +1,20 @@ +
        + +
        +
        +
        + {!! $input !!} +
        +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/bulma/form.blade.php b/tests/snapshots/bulma/bulma/form.blade.php new file mode 100644 index 0000000..a185069 --- /dev/null +++ b/tests/snapshots/bulma/bulma/form.blade.php @@ -0,0 +1,4 @@ +{{ $form->open() }} +{{ $fields->render() }} +{{ $buttons->render() }} +{{ $form->close() }} diff --git a/tests/snapshots/bulma/bulma/forms/checkboxes-inline.blade.php b/tests/snapshots/bulma/bulma/forms/checkboxes-inline.blade.php new file mode 100644 index 0000000..4b78465 --- /dev/null +++ b/tests/snapshots/bulma/bulma/forms/checkboxes-inline.blade.php @@ -0,0 +1,13 @@ +
        +@foreach($checkboxes as $checkbox) + {!! Form::checkbox( + $checkbox['name'], + $checkbox['value'], + $checkbox['checked'], + ['class' => $classes, 'id' => $checkbox['id']] + ) !!} + +@endforeach +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/bulma/forms/checkboxes.blade.php b/tests/snapshots/bulma/bulma/forms/checkboxes.blade.php new file mode 100644 index 0000000..e099568 --- /dev/null +++ b/tests/snapshots/bulma/bulma/forms/checkboxes.blade.php @@ -0,0 +1,8 @@ +
        + @foreach($checkboxes as [$checkbox, $label]) +
        + {{ $checkbox->render() }} + {{ $label->class('checkbox')->render() }} +
        + @endforeach +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/bulma/forms/radios-inline.blade.php b/tests/snapshots/bulma/bulma/forms/radios-inline.blade.php new file mode 100644 index 0000000..8de3108 --- /dev/null +++ b/tests/snapshots/bulma/bulma/forms/radios-inline.blade.php @@ -0,0 +1,14 @@ +
        +@foreach($radios as $radio) + + {!! Form::radio( + $radio['name'], + $radio['value'], + $radio['selected'], + ['class' => $classes, 'id' => $radio['id']]) !!} + + +@endforeach +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/bulma/forms/radios.blade.php b/tests/snapshots/bulma/bulma/forms/radios.blade.php new file mode 100644 index 0000000..07d58b0 --- /dev/null +++ b/tests/snapshots/bulma/bulma/forms/radios.blade.php @@ -0,0 +1,10 @@ +
        +
        +@foreach($radios as [$radio, $label]) + + {{ $radio->render() }} + {{ $label->class('radio')->render() }} + +@endforeach +
        +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/bulma/menu.blade.php b/tests/snapshots/bulma/bulma/menu.blade.php new file mode 100644 index 0000000..1c0612b --- /dev/null +++ b/tests/snapshots/bulma/bulma/menu.blade.php @@ -0,0 +1,22 @@ + diff --git a/tests/snapshots/bulma/field-collection/fields.html b/tests/snapshots/bulma/field-collection/fields.html new file mode 100644 index 0000000..6da23f8 --- /dev/null +++ b/tests/snapshots/bulma/field-collection/fields.html @@ -0,0 +1,28 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/checkbox.html b/tests/snapshots/bulma/field/checkbox.html new file mode 100644 index 0000000..7b32e44 --- /dev/null +++ b/tests/snapshots/bulma/field/checkbox.html @@ -0,0 +1,10 @@ +
        +
        + + +

        +

        +
        +
        diff --git a/tests/snapshots/bulma/field/checkboxes.html b/tests/snapshots/bulma/field/checkboxes.html new file mode 100644 index 0000000..7ea715b --- /dev/null +++ b/tests/snapshots/bulma/field/checkboxes.html @@ -0,0 +1,31 @@ +
        + +
        +
        +
        +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/checkboxes_help_text.html b/tests/snapshots/bulma/field/checkboxes_help_text.html new file mode 100644 index 0000000..9f6390d --- /dev/null +++ b/tests/snapshots/bulma/field/checkboxes_help_text.html @@ -0,0 +1,32 @@ +
        + +
        +
        +
        +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/email.html b/tests/snapshots/bulma/field/email.html new file mode 100644 index 0000000..e6ef50c --- /dev/null +++ b/tests/snapshots/bulma/field/email.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/file.html b/tests/snapshots/bulma/field/file.html new file mode 100644 index 0000000..b6ed687 --- /dev/null +++ b/tests/snapshots/bulma/field/file.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/hidden.html b/tests/snapshots/bulma/field/hidden.html new file mode 100644 index 0000000..72ba172 --- /dev/null +++ b/tests/snapshots/bulma/field/hidden.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/snapshots/bulma/field/input.html b/tests/snapshots/bulma/field/input.html new file mode 100644 index 0000000..4c2e448 --- /dev/null +++ b/tests/snapshots/bulma/field/input.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/password-required.html b/tests/snapshots/bulma/field/password-required.html new file mode 100644 index 0000000..36d2145 --- /dev/null +++ b/tests/snapshots/bulma/field/password-required.html @@ -0,0 +1,15 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/radios.html b/tests/snapshots/bulma/field/radios.html new file mode 100644 index 0000000..b31f2d8 --- /dev/null +++ b/tests/snapshots/bulma/field/radios.html @@ -0,0 +1,25 @@ +
        + +
        +
        +
        +
        +
        + + + + + + + + +
        +
        +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/select-empty.html b/tests/snapshots/bulma/field/select-empty.html new file mode 100644 index 0000000..ac83c5f --- /dev/null +++ b/tests/snapshots/bulma/field/select-empty.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/select-from-model.html b/tests/snapshots/bulma/field/select-from-model.html new file mode 100644 index 0000000..cc95ef2 --- /dev/null +++ b/tests/snapshots/bulma/field/select-from-model.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/select-group.html b/tests/snapshots/bulma/field/select-group.html new file mode 100644 index 0000000..01aabd0 --- /dev/null +++ b/tests/snapshots/bulma/field/select-group.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/select-multiple.html b/tests/snapshots/bulma/field/select-multiple.html new file mode 100644 index 0000000..041ea6b --- /dev/null +++ b/tests/snapshots/bulma/field/select-multiple.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/select-without-options-from-model.html b/tests/snapshots/bulma/field/select-without-options-from-model.html new file mode 100644 index 0000000..1db5575 --- /dev/null +++ b/tests/snapshots/bulma/field/select-without-options-from-model.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/select.html b/tests/snapshots/bulma/field/select.html new file mode 100644 index 0000000..58ff9e5 --- /dev/null +++ b/tests/snapshots/bulma/field/select.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-custom-id.html b/tests/snapshots/bulma/field/text-custom-id.html new file mode 100644 index 0000000..527d6b5 --- /dev/null +++ b/tests/snapshots/bulma/field/text-custom-id.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-custom-label.html b/tests/snapshots/bulma/field/text-custom-label.html new file mode 100644 index 0000000..2afd885 --- /dev/null +++ b/tests/snapshots/bulma/field/text-custom-label.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-custom-template.html b/tests/snapshots/bulma/field/text-custom-template.html new file mode 100644 index 0000000..2c6d94c --- /dev/null +++ b/tests/snapshots/bulma/field/text-custom-template.html @@ -0,0 +1,7 @@ +

        Custom field template

        +
        + + +
        diff --git a/tests/snapshots/bulma/field/text-dots-name.html b/tests/snapshots/bulma/field/text-dots-name.html new file mode 100644 index 0000000..66a16e7 --- /dev/null +++ b/tests/snapshots/bulma/field/text-dots-name.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-help-text.html b/tests/snapshots/bulma/field/text-help-text.html new file mode 100644 index 0000000..d0f8367 --- /dev/null +++ b/tests/snapshots/bulma/field/text-help-text.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-not-required.html b/tests/snapshots/bulma/field/text-not-required.html new file mode 100644 index 0000000..0027b52 --- /dev/null +++ b/tests/snapshots/bulma/field/text-not-required.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-raw-help-text.html b/tests/snapshots/bulma/field/text-raw-help-text.html new file mode 100644 index 0000000..d0f8367 --- /dev/null +++ b/tests/snapshots/bulma/field/text-raw-help-text.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-required.html b/tests/snapshots/bulma/field/text-required.html new file mode 100644 index 0000000..d5676c7 --- /dev/null +++ b/tests/snapshots/bulma/field/text-required.html @@ -0,0 +1,15 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text-with-raw-label.html b/tests/snapshots/bulma/field/text-with-raw-label.html new file mode 100644 index 0000000..7433d08 --- /dev/null +++ b/tests/snapshots/bulma/field/text-with-raw-label.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text.html b/tests/snapshots/bulma/field/text.html new file mode 100644 index 0000000..d0f8367 --- /dev/null +++ b/tests/snapshots/bulma/field/text.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/text_with_errors.html b/tests/snapshots/bulma/field/text_with_errors.html new file mode 100644 index 0000000..a02b21d --- /dev/null +++ b/tests/snapshots/bulma/field/text_with_errors.html @@ -0,0 +1,15 @@ +
        + +
        +
        +
        + +
        +

        + This is really wrong +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/textarea.html b/tests/snapshots/bulma/field/textarea.html new file mode 100644 index 0000000..02c111d --- /dev/null +++ b/tests/snapshots/bulma/field/textarea.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/field/url.html b/tests/snapshots/bulma/field/url.html new file mode 100644 index 0000000..1e05b30 --- /dev/null +++ b/tests/snapshots/bulma/field/url.html @@ -0,0 +1,14 @@ +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        diff --git a/tests/snapshots/bulma/form-model/form-with-novalidate.html b/tests/snapshots/bulma/form-model/form-with-novalidate.html new file mode 100644 index 0000000..96fee6e --- /dev/null +++ b/tests/snapshots/bulma/form-model/form-with-novalidate.html @@ -0,0 +1,42 @@ +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        +
        +
        + + +

        +

        +
        +
        + +auth.forgot_link +
        diff --git a/tests/snapshots/bulma/form-model/login-form.html b/tests/snapshots/bulma/form-model/login-form.html new file mode 100644 index 0000000..b1fb35d --- /dev/null +++ b/tests/snapshots/bulma/form-model/login-form.html @@ -0,0 +1,42 @@ +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        +
        +
        + + +

        +

        +
        +
        + +auth.forgot_link +
        diff --git a/tests/snapshots/bulma/form-model/user-form-for-update.html b/tests/snapshots/bulma/form-model/user-form-for-update.html new file mode 100644 index 0000000..fdef64f --- /dev/null +++ b/tests/snapshots/bulma/form-model/user-form-for-update.html @@ -0,0 +1,32 @@ +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        +
        + +
        +
        +
        + +
        +

        +

        +
        +
        +
        + + +
        diff --git a/tests/snapshots/bulma/form/checkboxes.html b/tests/snapshots/bulma/form/checkboxes.html new file mode 100644 index 0000000..ac5f7e8 --- /dev/null +++ b/tests/snapshots/bulma/form/checkboxes.html @@ -0,0 +1,18 @@ +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        + + +
        +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/form/csrf-field.html b/tests/snapshots/bulma/form/csrf-field.html new file mode 100644 index 0000000..735f593 --- /dev/null +++ b/tests/snapshots/bulma/form/csrf-field.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/form/get-method.html b/tests/snapshots/bulma/form/get-method.html new file mode 100644 index 0000000..0b94f22 --- /dev/null +++ b/tests/snapshots/bulma/form/get-method.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/form/put-method.html b/tests/snapshots/bulma/form/put-method.html new file mode 100644 index 0000000..fe565a2 --- /dev/null +++ b/tests/snapshots/bulma/form/put-method.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/form/radios.html b/tests/snapshots/bulma/form/radios.html new file mode 100644 index 0000000..c548f11 --- /dev/null +++ b/tests/snapshots/bulma/form/radios.html @@ -0,0 +1,12 @@ +
        +
        + + + + + + + + +
        +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/form/route.html b/tests/snapshots/bulma/form/route.html new file mode 100644 index 0000000..21fe797 --- /dev/null +++ b/tests/snapshots/bulma/form/route.html @@ -0,0 +1 @@ +
        \ No newline at end of file diff --git a/tests/snapshots/bulma/menu/access-handler.html b/tests/snapshots/bulma/menu/access-handler.html new file mode 100644 index 0000000..6d1c646 --- /dev/null +++ b/tests/snapshots/bulma/menu/access-handler.html @@ -0,0 +1,14 @@ + diff --git a/tests/snapshots/bulma/menu/custom-template.html b/tests/snapshots/bulma/menu/custom-template.html new file mode 100644 index 0000000..ffd9f8b --- /dev/null +++ b/tests/snapshots/bulma/menu/custom-template.html @@ -0,0 +1,9 @@ +

        Custom menu template

        + + diff --git a/tests/snapshots/bulma/menu/menu-classes.html b/tests/snapshots/bulma/menu/menu-classes.html new file mode 100644 index 0000000..1e83a32 --- /dev/null +++ b/tests/snapshots/bulma/menu/menu-classes.html @@ -0,0 +1,8 @@ + diff --git a/tests/snapshots/bulma/menu/menu.html b/tests/snapshots/bulma/menu/menu.html new file mode 100644 index 0000000..1cc6a9c --- /dev/null +++ b/tests/snapshots/bulma/menu/menu.html @@ -0,0 +1,14 @@ + diff --git a/tests/snapshots/bulma/menu/parameters.html b/tests/snapshots/bulma/menu/parameters.html new file mode 100644 index 0000000..dbf49d2 --- /dev/null +++ b/tests/snapshots/bulma/menu/parameters.html @@ -0,0 +1,8 @@ + diff --git a/tests/snapshots/bulma/menu/raw-urls.html b/tests/snapshots/bulma/menu/raw-urls.html new file mode 100644 index 0000000..f4da856 --- /dev/null +++ b/tests/snapshots/bulma/menu/raw-urls.html @@ -0,0 +1,8 @@ + diff --git a/tests/snapshots/bulma/menu/routes.html b/tests/snapshots/bulma/menu/routes.html new file mode 100644 index 0000000..741ed4e --- /dev/null +++ b/tests/snapshots/bulma/menu/routes.html @@ -0,0 +1,8 @@ + diff --git a/tests/snapshots/bulma/menu/secure-urls.html b/tests/snapshots/bulma/menu/secure-urls.html new file mode 100644 index 0000000..deeeddb --- /dev/null +++ b/tests/snapshots/bulma/menu/secure-urls.html @@ -0,0 +1,8 @@ + diff --git a/tests/snapshots/bulma/menu/submenu-active.html b/tests/snapshots/bulma/menu/submenu-active.html new file mode 100644 index 0000000..3c5e520 --- /dev/null +++ b/tests/snapshots/bulma/menu/submenu-active.html @@ -0,0 +1,18 @@ + diff --git a/tests/snapshots/bulma/menu/submenu.html b/tests/snapshots/bulma/menu/submenu.html new file mode 100644 index 0000000..e8b191a --- /dev/null +++ b/tests/snapshots/bulma/menu/submenu.html @@ -0,0 +1,18 @@ + diff --git a/themes/bulma/alert.blade.php b/themes/bulma/alert.blade.php new file mode 100644 index 0000000..cc7973f --- /dev/null +++ b/themes/bulma/alert.blade.php @@ -0,0 +1,29 @@ +@foreach ($messages as $msg) +
        +
        +

        Alert!

        + +
        +
        +

        {{ $msg['message'] }}

        +@if (!empty ($msg['details'])) + {{ $msg['details'] }} +@endif + {!! $msg['html'] !!} +@if (!empty ($msg['items'])) +
          +@foreach ($msg['items'] as $item) +
        • {{ $item }}
        • +@endforeach +
        +@endif +@if ( ! empty ($msg['buttons'])) +
        +@foreach ($msg['buttons'] as $btn) + {{ $btn['text'] }} +@endforeach +
        +@endif +
        +
        +@endforeach diff --git a/themes/bulma/fields/checkbox.blade.php b/themes/bulma/fields/checkbox.blade.php new file mode 100644 index 0000000..6150af4 --- /dev/null +++ b/themes/bulma/fields/checkbox.blade.php @@ -0,0 +1,16 @@ +
        +
        + {!! $input !!} + +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        diff --git a/themes/bulma/fields/collections.blade.php b/themes/bulma/fields/collections.blade.php new file mode 100644 index 0000000..b38d46f --- /dev/null +++ b/themes/bulma/fields/collections.blade.php @@ -0,0 +1,20 @@ +
        + +
        +
        +
        + {!! $input !!} +
        +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        +
        diff --git a/themes/bulma/fields/default.blade.php b/themes/bulma/fields/default.blade.php new file mode 100644 index 0000000..64c7ecb --- /dev/null +++ b/themes/bulma/fields/default.blade.php @@ -0,0 +1,20 @@ +
        + +
        +
        +
        + {!! $input !!} +
        +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        +
        diff --git a/themes/bulma/fields/selects.blade.php b/themes/bulma/fields/selects.blade.php new file mode 100644 index 0000000..10fe44c --- /dev/null +++ b/themes/bulma/fields/selects.blade.php @@ -0,0 +1,20 @@ +
        + +
        +
        +
        + {!! $input !!} +
        +

        + @foreach ($errors as $error) + {{ $error }} + @endforeach +

        +
        +
        +
        diff --git a/themes/bulma/form.blade.php b/themes/bulma/form.blade.php new file mode 100644 index 0000000..a185069 --- /dev/null +++ b/themes/bulma/form.blade.php @@ -0,0 +1,4 @@ +{{ $form->open() }} +{{ $fields->render() }} +{{ $buttons->render() }} +{{ $form->close() }} diff --git a/themes/bulma/forms/checkboxes-inline.blade.php b/themes/bulma/forms/checkboxes-inline.blade.php new file mode 100644 index 0000000..4b78465 --- /dev/null +++ b/themes/bulma/forms/checkboxes-inline.blade.php @@ -0,0 +1,13 @@ +
        +@foreach($checkboxes as $checkbox) + {!! Form::checkbox( + $checkbox['name'], + $checkbox['value'], + $checkbox['checked'], + ['class' => $classes, 'id' => $checkbox['id']] + ) !!} + +@endforeach +
        \ No newline at end of file diff --git a/themes/bulma/forms/checkboxes.blade.php b/themes/bulma/forms/checkboxes.blade.php new file mode 100644 index 0000000..e099568 --- /dev/null +++ b/themes/bulma/forms/checkboxes.blade.php @@ -0,0 +1,8 @@ +
        + @foreach($checkboxes as [$checkbox, $label]) +
        + {{ $checkbox->render() }} + {{ $label->class('checkbox')->render() }} +
        + @endforeach +
        \ No newline at end of file diff --git a/themes/bulma/forms/radios-inline.blade.php b/themes/bulma/forms/radios-inline.blade.php new file mode 100644 index 0000000..8de3108 --- /dev/null +++ b/themes/bulma/forms/radios-inline.blade.php @@ -0,0 +1,14 @@ +
        +@foreach($radios as $radio) + + {!! Form::radio( + $radio['name'], + $radio['value'], + $radio['selected'], + ['class' => $classes, 'id' => $radio['id']]) !!} + + +@endforeach +
        \ No newline at end of file diff --git a/themes/bulma/forms/radios.blade.php b/themes/bulma/forms/radios.blade.php new file mode 100644 index 0000000..07d58b0 --- /dev/null +++ b/themes/bulma/forms/radios.blade.php @@ -0,0 +1,10 @@ +
        +
        +@foreach($radios as [$radio, $label]) + + {{ $radio->render() }} + {{ $label->class('radio')->render() }} + +@endforeach +
        +
        \ No newline at end of file diff --git a/themes/bulma/menu.blade.php b/themes/bulma/menu.blade.php new file mode 100644 index 0000000..bc30a1e --- /dev/null +++ b/themes/bulma/menu.blade.php @@ -0,0 +1,18 @@ + From 59abbb43f15e8b8916fec1abfa89f6fff5b9a8b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Fri, 15 Feb 2019 22:50:58 -0400 Subject: [PATCH 136/217] Add test when themes has custom folder name --- src/Theme.php | 2 +- tests/AlertTest.php | 13 +++++++++++++ .../alert/custom-folder-in-resources-views.html | 1 + .../alert/custom-folder-in-resources-views.html | 1 + .../views/custom-folder/bootstrap4/alert.blade.php | 1 + tests/views/custom-folder/bulma/alert.blade.php | 1 + 6 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/snapshots/bootstrap4/alert/custom-folder-in-resources-views.html create mode 100644 tests/snapshots/bulma/alert/custom-folder-in-resources-views.html create mode 100644 tests/views/custom-folder/bootstrap4/alert.blade.php create mode 100644 tests/views/custom-folder/bulma/alert.blade.php diff --git a/src/Theme.php b/src/Theme.php index 433408b..ac8def2 100644 --- a/src/Theme.php +++ b/src/Theme.php @@ -78,7 +78,7 @@ public function render($custom = null, $data = array(), $template = null) return $this->view->make($custom, $data)->render(); } - $template = $this->theme.'/'.$template; + $template = $this->getName().'/'.$template; if ($this->view->exists($this->custom.'/'.$template)) { return $this->view->make($this->custom.'/'.$template, $data)->render(); diff --git a/tests/AlertTest.php b/tests/AlertTest.php index 7b2ff31..c3d4625 100644 --- a/tests/AlertTest.php +++ b/tests/AlertTest.php @@ -4,6 +4,7 @@ use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\View; use Styde\Html\Alert\Middleware as AlertMiddleware; use Styde\Html\Facades\Alert; @@ -55,6 +56,18 @@ function can_customize_the_template() $this->assertTemplateMatches('alert/custom-template', Alert::render('custom-templates.alert')); } + /** @test */ + function can_customize_the_themes_folder_name_in_resources_views() + { + View::addLocation(__DIR__.'/views'); + + Config::set('html.custom', 'custom-folder'); + + Alert::message('This is a message', 'info'); + + $this->assertTemplateMatches('alert/custom-folder-in-resources-views', Alert::render()); + } + /** @test */ function it_renders_a_view_inside_the_alert() { diff --git a/tests/snapshots/bootstrap4/alert/custom-folder-in-resources-views.html b/tests/snapshots/bootstrap4/alert/custom-folder-in-resources-views.html new file mode 100644 index 0000000..f2fd27f --- /dev/null +++ b/tests/snapshots/bootstrap4/alert/custom-folder-in-resources-views.html @@ -0,0 +1 @@ +

        Custom alert template for Bootstrap 4 in resources/views/custom-folder/bootstrap4

        \ No newline at end of file diff --git a/tests/snapshots/bulma/alert/custom-folder-in-resources-views.html b/tests/snapshots/bulma/alert/custom-folder-in-resources-views.html new file mode 100644 index 0000000..d370888 --- /dev/null +++ b/tests/snapshots/bulma/alert/custom-folder-in-resources-views.html @@ -0,0 +1 @@ +

        Custom alert template for Bulma in resources/views/custom-folder/bulma

        \ No newline at end of file diff --git a/tests/views/custom-folder/bootstrap4/alert.blade.php b/tests/views/custom-folder/bootstrap4/alert.blade.php new file mode 100644 index 0000000..f2fd27f --- /dev/null +++ b/tests/views/custom-folder/bootstrap4/alert.blade.php @@ -0,0 +1 @@ +

        Custom alert template for Bootstrap 4 in resources/views/custom-folder/bootstrap4

        \ No newline at end of file diff --git a/tests/views/custom-folder/bulma/alert.blade.php b/tests/views/custom-folder/bulma/alert.blade.php new file mode 100644 index 0000000..d370888 --- /dev/null +++ b/tests/views/custom-folder/bulma/alert.blade.php @@ -0,0 +1 @@ +

        Custom alert template for Bulma in resources/views/custom-folder/bulma

        \ No newline at end of file From 364932daf14d18e1fd90d9a9a02b619cb8bda954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Mon, 18 Feb 2019 09:51:10 -0400 Subject: [PATCH 137/217] Fix Htmltag::add method and add some tests --- src/BaseTag.php | 23 +++++++++++ src/Htmltag.php | 39 +++++++++---------- src/TextElement.php | 23 +++++++++++ src/VoidTag.php | 3 +- tests/HtmlBuilderTest.php | 8 ---- tests/HtmltagTest.php | 82 +++++++++++++++++++++++++++++++++++++++ tests/StrTest.php | 3 ++ 7 files changed, 151 insertions(+), 30 deletions(-) create mode 100644 src/TextElement.php create mode 100644 tests/HtmltagTest.php diff --git a/src/BaseTag.php b/src/BaseTag.php index 865f314..a97d42f 100644 --- a/src/BaseTag.php +++ b/src/BaseTag.php @@ -73,6 +73,16 @@ public function renderAttributes() return $result; } + /** + * Render the open tag with the attributes + * + * @return string + */ + public function renderOpenTag() + { + return '<'.$this->tag.$this->renderAttributes().'>'; + } + /** * @param string $name * @return mixed|string @@ -139,4 +149,17 @@ public function __toString() { return $this->toHtml(); } + + /** + * @param string $name + * @return mixed + */ + public function __get($name) + { + if (isset($this->attributes[$name])) { + return $this->attributes[$name]; + } + + throw new \InvalidArgumentException("The property $name does not exist in this [{$this->tag}] element"); + } } diff --git a/src/Htmltag.php b/src/Htmltag.php index 19f218c..de53366 100644 --- a/src/Htmltag.php +++ b/src/Htmltag.php @@ -9,7 +9,7 @@ class Htmltag extends BaseTag { /** - * Attributes of the tag + * Content of the tag * * @var array|string */ @@ -22,10 +22,14 @@ class Htmltag extends BaseTag * @param string $content * @param array $attributes */ - public function __construct($tag, $content = '', array $attributes = []) + public function __construct($tag, $content = null, array $attributes = []) { parent::__construct($tag, $attributes); + if (is_string($content)) { + $content = [new TextElement($content)]; + } + $this->content = $content; } @@ -46,9 +50,9 @@ public function render() { if ($this->included) { return new HtmlString( - '<'.$this->tag.$this->renderAttributes().'>' + $this->renderOpenTag() .$this->renderContent() - .'tag.'>' + .$this->renderCloseTag() ); } } @@ -61,7 +65,7 @@ public function render() public function open() { if ($this->included) { - return new HtmlString('<'.$this->tag.$this->renderAttributes().'>'); + return new HtmlString($this->renderOpenTag()); } } @@ -73,34 +77,29 @@ public function open() public function close() { if ($this->included) { - return new HtmlString('tag.'>'); + return new HtmlString($this->renderCloseTag()); } } /** * @return string */ - public function renderContent() + public function renderCloseTag() { - $result = ''; - - foreach ((array) $this->content as $content) { - $result .= e($content); - } - - return $result; + return 'tag.'>'; } /** - * @param string $name - * @return mixed + * @return string */ - public function __get($name) + public function renderContent() { - if (isset($this->content[$name])) { - return $this->content[$name]; + $result = ''; + + foreach ((array) $this->content as $content) { + $result .= $content->render(); } - throw new \InvalidArgumentException("The property $name does not exist in this [{$this->tag}] element"); + return $result; } } diff --git a/src/TextElement.php b/src/TextElement.php new file mode 100644 index 0000000..8701e0d --- /dev/null +++ b/src/TextElement.php @@ -0,0 +1,23 @@ +html; + } +} diff --git a/src/VoidTag.php b/src/VoidTag.php index 59d52a4..599851e 100644 --- a/src/VoidTag.php +++ b/src/VoidTag.php @@ -2,7 +2,6 @@ namespace Styde\Html; -use Illuminate\Support\Arr; use Illuminate\Support\HtmlString; use Illuminate\Contracts\Support\Htmlable; @@ -16,7 +15,7 @@ class VoidTag extends BaseTag implements Htmlable public function render() { if ($this->included) { - return new HtmlString('<'.$this->tag.$this->renderAttributes().'>'); + return new HtmlString($this->renderOpenTag()); } } } diff --git a/tests/HtmlBuilderTest.php b/tests/HtmlBuilderTest.php index 9ce0af9..f605443 100644 --- a/tests/HtmlBuilderTest.php +++ b/tests/HtmlBuilderTest.php @@ -22,14 +22,6 @@ function it_generates_html_tags() ); } - /** @test */ - function it_closes_html_tags() - { - $htmlElement = new Htmltag('span'); - - $this->assertEquals('', (string) $htmlElement->close()); - } - /** @test */ function it_escapes_the_attributes_of_generated_tags() { diff --git a/tests/HtmltagTest.php b/tests/HtmltagTest.php new file mode 100644 index 0000000..51b7615 --- /dev/null +++ b/tests/HtmltagTest.php @@ -0,0 +1,82 @@ + 'my-span']); + + $this->assertEquals( + 'This is a span', + $tag->render() + ); + + $this->assertEquals( + 'This is a span', + $tag + ); + } + + /** @test */ + function it_generates_an_nested_html_tags() + { + $tag = new Htmltag('div', null, ['id' => 'div-id']); + + $text1 = new Htmltag('p', 'this is the first paragragh'); + + $voidtag = new VoidTag('hr'); + + $text2 = new Htmltag('p', 'this is the second paragragh'); + + $tag->add($text1); + $tag->add($voidtag); + $tag->add($text2); + + $this->assertEquals( + '

        this is the first paragragh


        this is the second paragragh

        ', + (string) $tag + ); + } + + /** @test */ + function it_opens_html_tags() + { + $htmlElement = new Htmltag('div', null, ['class' => 'form-control']); + + $this->assertEquals('
        ', (string) $htmlElement->open()); + } + + /** @test */ + function it_does_not_open_an_html_tag() + { + $tag = new Htmltag('span', 'This is a span', ['id' => 'my-span']); + $tag->includeIf(false); + + $this->assertEquals('', $tag->open()); + } + + /** @test */ + function it_closes_html_tags() + { + $htmlElement = new Htmltag('span'); + + $this->assertEquals('', (string) $htmlElement->close()); + } + + /** @test */ + function it_does_not_close_html_tag() + { + $htmlElement = new Htmltag('span'); + $htmlElement->includeIf(false); + + $this->assertEquals('', (string) $htmlElement->close()); + } +} diff --git a/tests/StrTest.php b/tests/StrTest.php index 68deabb..32175ba 100644 --- a/tests/StrTest.php +++ b/tests/StrTest.php @@ -58,5 +58,8 @@ function it_resumes_a_string() $text = 'You know nothing, John Snow'; $this->assertSame($text, Str::teaser($text, 28)); + + // if it is a empty string + $this->assertSame('', Str::teaser('', 28, '...')); } } \ No newline at end of file From 1d02eab62003fca34ea9298f59ea2145d6925539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Mon, 18 Feb 2019 12:39:50 -0400 Subject: [PATCH 138/217] Add tests for FormModel::validate and FormModel::creationSetup --- tests/FormModelTest.php | 54 +++++++++++++++++-- .../form-model/user-form-for-creation.html | 16 ++++++ 2 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/snapshots/bootstrap4/form-model/user-form-for-creation.html diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index d63272e..0a81578 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -2,8 +2,10 @@ namespace Styde\Html\Tests; -use Styde\Html\FormModel; +use Illuminate\Http\Request; +use Illuminate\Http\Testing\FileFactory; use Illuminate\Support\Facades\{View, Route}; +use Styde\Html\FormModel; class FormModelTest extends TestCase { @@ -46,7 +48,7 @@ function it_returns_the_rules_from_all_fields() $expect = [ 'name' => [], - 'email' => ['email', 'unique:users,NULL,NULL,id', 'required'], + 'email' => ['email', 'required'], 'password' => ['confirmed', 'min:6', 'max:12', 'required'], 'password_confirmation' => ['min:6', 'max:12', 'required'], 'photo' => ['file', 'required', 'image', 'dimensions:ratio=3/2'], @@ -56,6 +58,34 @@ function it_returns_the_rules_from_all_fields() $this->assertSame($expect, $rules); } + /** @test */ + function it_return_all_validated_fields_of_a_form() + { + $files = [ + 'photo' => $image = (new FileFactory)->image('foo.jpg', 30, 20), + ]; + $request = Request::create('/', 'GET', [ + 'name' => 'Clemir', + 'email' => 'clemir@styde.net', + 'password' => 'secret', + 'password_confirmation' => 'secret', + "remember_me" => 1, + ], [], $files); + + $result = app(RegisterForm::class)->validate($request); + + $expect = [ + 'name' => 'Clemir', + 'email' => 'clemir@styde.net', + 'password' => 'secret', + 'password_confirmation' => 'secret', + 'photo' => $image, + "remember_me" => 1, + ]; + + $this->assertSame($expect, $result); + } + /** @test */ function it_returns_the_rules_only_for_fields() { @@ -84,6 +114,19 @@ function it_builds_a_update_form() $this->assertTemplateMatches('form-model/user-form-for-update', $userForm); } + /** @test */ + function it_builds_a_create_form_with_forCreation_method() + { + $userModel = $this->aUserWithData([ + 'name' => 'Clemir', + 'email' => 'clemir@styde.net' + ]); + + $userForm = app(UserForm::class)->forCreation(); + + $this->assertTemplateMatches('form-model/user-form-for-creation', $userForm); + } + /** @test */ function it_set_a_novalidate_attribute() { @@ -134,7 +177,7 @@ class RegisterForm extends FormModel public function setup() { $this->text('name')->required()->disableRules(); - $this->email('email')->unique('users')->required(); + $this->email('email')->required(); $this->password('password')->confirmed()->min(6)->max(12)->required(); $this->password('password_confirmation')->min(6)->max(12)->required(); $this->file('photo')->required()->image()->dimensions(['ratio' => '3/2']); @@ -150,6 +193,11 @@ public function setup() $this->email('email'); } + public function creationSetup() + { + $this->submit('Create user'); + } + public function updateSetup() { $this->submit('Update user'); diff --git a/tests/snapshots/bootstrap4/form-model/user-form-for-creation.html b/tests/snapshots/bootstrap4/form-model/user-form-for-creation.html new file mode 100644 index 0000000..7cc1b43 --- /dev/null +++ b/tests/snapshots/bootstrap4/form-model/user-form-for-creation.html @@ -0,0 +1,16 @@ +
        +
        + + +
        +
        + + +
        + + +
        From 1b88ca591fd92a3f19e8409ec1532f4ae0a4391e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Mon, 18 Feb 2019 14:03:00 -0400 Subject: [PATCH 139/217] Add tests for get attributes and content of Htmltag dynamically --- src/Htmltag.php | 12 ++++++++++++ tests/HtmltagTest.php | 30 ++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/Htmltag.php b/src/Htmltag.php index de53366..3a975c6 100644 --- a/src/Htmltag.php +++ b/src/Htmltag.php @@ -102,4 +102,16 @@ public function renderContent() return $result; } + + /** + * @param string $name + * @return mixed + */ + public function __get($name) + { + if (isset($this->content[$name])) { + return $this->content[$name]; + } + return parent::__get($name); + } } diff --git a/tests/HtmltagTest.php b/tests/HtmltagTest.php index 51b7615..2a63048 100644 --- a/tests/HtmltagTest.php +++ b/tests/HtmltagTest.php @@ -79,4 +79,34 @@ function it_does_not_close_html_tag() $this->assertEquals('', (string) $htmlElement->close()); } + + /** @test */ + function it_returns_the_tag_attributes_dynamically() + { + $tag = new Htmltag('div', null, ['id' => 'my-div', 'class' => 'classA']); + + $this->assertEquals('my-div', $tag->id); + $this->assertEquals('classA', $tag->class); + } + + /** @test */ + function it_returns_the_tag_content_dynamically() + { + $children['title'] = new Htmltag('h1', 'Title'); + $children['summary'] = new Htmltag('p', 'This is the content'); + $tag = new Htmltag('div', $children); + + $this->assertEquals('

        Title

        ', $tag->title->toHtml()); + $this->assertEquals('

        This is the content

        ', $tag->summary->toHtml()); + } + + /** @test */ + function it_thrown_an_exception_when_call_a_missing_attribute() + { + $this->expectException('InvalidArgumentException'); + $this->expectExceptionMessage('The property content does not exist in this [div] element'); + + $tag = new Htmltag('div', 'This is a content'); + $tag->content; + } } From df83a2bbca5637a75ce91ae00aa5d63b3135de28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Clemir=20Rond=C3=B3n?= Date: Sat, 30 Mar 2019 11:14:28 -0400 Subject: [PATCH 140/217] Fix thrown exception when dynamically handle calls to the form model. --- src/FormModel.php | 9 +-------- src/FormModel/FieldCollection.php | 14 ++++++++++---- tests/FormModelTest.php | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/FormModel.php b/src/FormModel.php index 8fb09e2..6c83001 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -9,8 +9,6 @@ class FormModel implements Htmlable { - use ForwardsCalls; - /** * @var \Styde\Html\FormBuilder */ @@ -260,12 +258,7 @@ public function __call($method, $parameters = []) if (method_exists($this->buttons, $method)) { return $this->buttons->$method(...$parameters); } - - if (method_exists($this->fields, $method)) { - return $this->fields->$method(...$parameters); - } - - static::throwBadMethodCallException($method); + return $this->fields->$method(...$parameters); } /** diff --git a/src/FormModel/FieldCollection.php b/src/FormModel/FieldCollection.php index ba04756..1df2161 100644 --- a/src/FormModel/FieldCollection.php +++ b/src/FormModel/FieldCollection.php @@ -2,14 +2,15 @@ namespace Styde\Html\FormModel; -use Styde\Html\Facades\Html; -use Styde\Html\FieldBuilder; use Illuminate\Support\HtmlString; +use Illuminate\Support\Traits\ForwardsCalls; use Illuminate\Support\Traits\Macroable; +use Styde\Html\Facades\Html; +use Styde\Html\FieldBuilder; class FieldCollection { - use Macroable { + use ForwardsCalls, Macroable { Macroable::__call as macroCall; } @@ -52,6 +53,7 @@ public function isEmpty() * @param array $params * * @return \Styde\Html\FieldBuilder + * @throws \BadMethodCallException */ public function __call($method, $params) { @@ -59,7 +61,11 @@ public function __call($method, $params) return $this->macroCall($method, $params); } - return $this->add($params[0], $method); + if (!empty($params)) { + return $this->add($params[0], $method); + } + + static::throwBadMethodCallException($method); } /** diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index 0a81578..2db9950 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -151,7 +151,7 @@ function it_set_a_novalidate_attribute_as_false() function it_thrown_an_exception_when_call_a_bad_method() { $this->expectException('BadMethodCallException'); - $this->expectExceptionMessage('Call to undefined method Styde\Html\Tests\LoginForm::badMethod()'); + $this->expectExceptionMessage('Call to undefined method Styde\Html\FormModel\FieldCollection::badMethod()'); $form = app(LoginForm::class)->badMethod(); } From 71d73ce354d11ac8f4b4f7956f3d048662e820cc Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Sat, 13 Apr 2019 12:37:50 +0100 Subject: [PATCH 141/217] Inline code and delete ForwardsCalls trait since thats not really a 'trait' of the field collection class --- src/FormModel/FieldCollection.php | 12 +++++++----- tests/FormModelTest.php | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/FormModel/FieldCollection.php b/src/FormModel/FieldCollection.php index 1df2161..d704cc3 100644 --- a/src/FormModel/FieldCollection.php +++ b/src/FormModel/FieldCollection.php @@ -2,15 +2,15 @@ namespace Styde\Html\FormModel; -use Illuminate\Support\HtmlString; -use Illuminate\Support\Traits\ForwardsCalls; -use Illuminate\Support\Traits\Macroable; use Styde\Html\Facades\Html; use Styde\Html\FieldBuilder; +use Illuminate\Support\HtmlString; +use Illuminate\Support\Traits\Macroable; +use Mockery\Exception\BadMethodCallException; class FieldCollection { - use ForwardsCalls, Macroable { + use Macroable { Macroable::__call as macroCall; } @@ -65,7 +65,9 @@ public function __call($method, $params) return $this->add($params[0], $method); } - static::throwBadMethodCallException($method); + throw new BadMethodCallException(sprintf( + 'Call to undefined method %s::%s()', static::class, $method + )); } /** diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index 2db9950..c9032b8 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -148,12 +148,12 @@ function it_set_a_novalidate_attribute_as_false() } /** @test */ - function it_thrown_an_exception_when_call_a_bad_method() + function it_throws_a_bad_method_call_exception_when_calling_a_non_existing_method() { $this->expectException('BadMethodCallException'); $this->expectExceptionMessage('Call to undefined method Styde\Html\FormModel\FieldCollection::badMethod()'); - $form = app(LoginForm::class)->badMethod(); + app(LoginForm::class)->badMethod(); } } From 722fd4078722564aeb45a77b89deab4e84f291ee Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Sat, 13 Apr 2019 13:15:59 +0100 Subject: [PATCH 142/217] Change HtmlTag constructor, protect against invalid arguments for the tag content --- src/Htmltag.php | 12 +++++++----- tests/HtmltagTest.php | 11 ----------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/Htmltag.php b/src/Htmltag.php index 3a975c6..222a1c5 100644 --- a/src/Htmltag.php +++ b/src/Htmltag.php @@ -2,12 +2,12 @@ namespace Styde\Html; +use InvalidArgumentException; use Illuminate\Support\HtmlString; use Illuminate\Contracts\Support\Htmlable; class Htmltag extends BaseTag { - /** * Content of the tag * @@ -26,11 +26,13 @@ public function __construct($tag, $content = null, array $attributes = []) { parent::__construct($tag, $attributes); - if (is_string($content)) { - $content = [new TextElement($content)]; + if (is_array($content)) { + array_walk($content, [$this, 'add']); + } elseif (is_string($content)) { + $this->add(new TextElement($content)); + } elseif ($content) { + $this->add($content); } - - $this->content = $content; } /** diff --git a/tests/HtmltagTest.php b/tests/HtmltagTest.php index 2a63048..bde7708 100644 --- a/tests/HtmltagTest.php +++ b/tests/HtmltagTest.php @@ -89,17 +89,6 @@ function it_returns_the_tag_attributes_dynamically() $this->assertEquals('classA', $tag->class); } - /** @test */ - function it_returns_the_tag_content_dynamically() - { - $children['title'] = new Htmltag('h1', 'Title'); - $children['summary'] = new Htmltag('p', 'This is the content'); - $tag = new Htmltag('div', $children); - - $this->assertEquals('

        Title

        ', $tag->title->toHtml()); - $this->assertEquals('

        This is the content

        ', $tag->summary->toHtml()); - } - /** @test */ function it_thrown_an_exception_when_call_a_missing_attribute() { From b89a905c63ef455a732fcdc75bc0b2ebfdb33bef Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Sat, 13 Apr 2019 13:38:28 +0100 Subject: [PATCH 143/217] Make sure only hidden inputs are rendered when opening a form, replace loop with array_reduce --- src/Form.php | 10 +++----- tests/FormBuilderTest.php | 25 ++++++++++++++++++- .../bootstrap4/form/post-with-fields.html | 1 + 3 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 tests/snapshots/bootstrap4/form/post-with-fields.html diff --git a/src/Form.php b/src/Form.php index 64a534a..1add36e 100644 --- a/src/Form.php +++ b/src/Form.php @@ -96,14 +96,12 @@ public function close() */ protected function renderHiddenFields() { - $html = ''; - - foreach ($this->content as $child) { + return array_reduce($this->content, function ($result, $child) { if ($child instanceof HiddenInput) { - $html .= $child->render(); + $result .= $child->render(); } - } - return $html; + return $result; + }, ''); } } diff --git a/tests/FormBuilderTest.php b/tests/FormBuilderTest.php index cc73532..8fb513a 100644 --- a/tests/FormBuilderTest.php +++ b/tests/FormBuilderTest.php @@ -5,6 +5,7 @@ use Styde\Html\Facades\Form; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\{Route, Session}; +use Styde\Html\Form\Input; class FormBuilderTest extends TestCase { @@ -49,12 +50,34 @@ function it_opens_a_delete_form() } /** @test */ - function it_renders_forms() + function opening_a_form_does_not_render_visible_fields() + { + $form = Form::get(); + + $form->add(Form::text('name', 'Duilio')); + + $expected = '
        '; + + $this->assertHtmlEquals($expected, $form->open()); + } + + /** @test */ + function it_renders_a_form() { $this->assertTemplateMatches( 'form/get-method', Form::get()->render() ); } + + /** @test */ + function it_renders_a_form_with_fields() + { + $form = Form::post(); + + $form->add(Form::text('name', 'Duilio')); + + $this->assertTemplateMatches('form/post-with-fields', $form->render()); + } /** @test */ function it_can_accept_files() diff --git a/tests/snapshots/bootstrap4/form/post-with-fields.html b/tests/snapshots/bootstrap4/form/post-with-fields.html new file mode 100644 index 0000000..2adf4f5 --- /dev/null +++ b/tests/snapshots/bootstrap4/form/post-with-fields.html @@ -0,0 +1 @@ +
        \ No newline at end of file From d35ca58e3cf385986c6f884b2444b36eece8199c Mon Sep 17 00:00:00 2001 From: Duilio Palacios Date: Sat, 13 Apr 2019 13:46:40 +0100 Subject: [PATCH 144/217] Setting novalidate in a form model doesnt change the global novalidate config --- src/FormModel.php | 4 +++- tests/FormModelTest.php | 14 ++++++++++++-- .../form-model/form-with-novalidate.html | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/FormModel.php b/src/FormModel.php index 6c83001..05117de 100644 --- a/src/FormModel.php +++ b/src/FormModel.php @@ -179,7 +179,9 @@ public function model($model) */ public function novalidate($value = true) { - $this->formBuilder->novalidate($value); + $this->runSetup(); + + $this->form->novalidate($value); return $this; } diff --git a/tests/FormModelTest.php b/tests/FormModelTest.php index c9032b8..4eb0ece 100644 --- a/tests/FormModelTest.php +++ b/tests/FormModelTest.php @@ -5,6 +5,8 @@ use Illuminate\Http\Request; use Illuminate\Http\Testing\FileFactory; use Illuminate\Support\Facades\{View, Route}; +use Styde\Html\Facades\Form; +use Styde\Html\FormBuilder; use Styde\Html\FormModel; class FormModelTest extends TestCase @@ -128,15 +130,23 @@ function it_builds_a_create_form_with_forCreation_method() } /** @test */ - function it_set_a_novalidate_attribute() + function it_sets_the_novalidate_attribute() { Route::post('login', ['as' => 'login']); - $form = app(LoginForm::class)->novalidate(); $this->assertTemplateMatches('form-model/form-with-novalidate', $form); } + /** @test */ + function setting_novalidate_in_a_form_model_doesnt_change_the_global_novalidate_config() + { + Route::post('login', ['as' => 'login']); + app(LoginForm::class)->novalidate(); + + $this->assertHtmlEquals('
        ', Form::get()); + } + /** @test */ function it_set_a_novalidate_attribute_as_false() { diff --git a/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html b/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html index bac1f0a..5952394 100644 --- a/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html +++ b/tests/snapshots/bootstrap4/form-model/form-with-novalidate.html @@ -1,4 +1,4 @@ -
        +