diff --git a/CHANGELOG.md b/CHANGELOG.md index 60dab789..d1573b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [5.0.0](https://github.com/Okipa/laravel-bootstrap-components/compare/4.0.0...5.0.0) + +2020-12-02 + +* Added Livewire support +* Added named validation bag support +* Templates updated +* Methods signature update +* Components default config update + +:point_right: [See the upgrade guide](/docs/upgrade-guides/from-v4-to-v5.md) + ## [4.0.0](https://github.com/Okipa/laravel-bootstrap-components/compare/3.0.3...4.0.0) 2020-11-14 @@ -40,6 +52,8 @@ * Dropped Laravel 5.8 and 6.0 support * Dropped PHP 7.2 and 7.3 support +:point_right: [See the upgrade guide](/docs/upgrade-guides/from-v2-to-v3.md) + ## [2.2.3](https://github.com/Okipa/laravel-bootstrap-components/compare/2.2.2...2.2.3) 2020-07-16 diff --git a/README.md b/README.md index 30da35df..bb8488ac 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@

-Save time and take advantage of an extended set of ready-to-use and fully customizable bootstrap form components. +Save time and take advantage of a set of dynamical, ready-to-use and fully customizable bootstrap form components. Found this package helpful? Please consider supporting my work! @@ -35,7 +35,9 @@ Found this package helpful? Please consider supporting my work! ## Upgrade guide +* [From V4 to V5](/docs/upgrade-guides/from-v4-to-v5.md) * [From V3 to V4](/docs/upgrade-guides/from-v3-to-v4.md) +* [From V2 to V3](/docs/upgrade-guides/from-v2-to-v3.md) * [From V1 to V2](/docs/upgrade-guides/from-v1-to-v2.md) ## Usage @@ -82,10 +84,10 @@ And get this HTML generated for you: Call this component in your view: ```blade -{{-- helper style --}} +{{-- Helper style --}} {{ inputText()->name('title')->localized(['fr', 'en']) }} -{{-- facade style --}} +{{-- Facade style --}} {{ InputText::name('title')->localized(['fr', 'en']) }} ``` diff --git a/composer.json b/composer.json index de529853..d4d11375 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "require-dev": { "nunomaduro/larastan": "^0.6", "orchestra/testbench": "^5.0||^6.0", - "phpmd/phpmd": "^2.8", + "phpmd/phpmd": "^2.9", "squizlabs/php_codesniffer": "^3.5" }, "autoload": { diff --git a/docs/api/components.md b/docs/api/components.md index 69cc56ae..e1bdec9f 100644 --- a/docs/api/components.md +++ b/docs/api/components.md @@ -2,7 +2,7 @@ * [Form components](#form-components) * [Input text](#input-text) - * [Input e-mail](#input-e-mail) + * [Input email](#input-email) * [Input password](#input-password) * [Input URL](#input-url) * [Input tel](#input-tel) @@ -49,7 +49,7 @@ * Display success : `config('bootstrap-components.form.formValidation.displaySuccess')` * Display failure : `config('bootstrap-components.form.formValidation.displayFailure')` -## Input e-mail +## Input email **Type :** [FormAbstract](./types.md#formabstract) @@ -61,6 +61,7 @@ * Prepend : `` * Label positioned above : `config('bootstrap-components.form.labelPositionedAbove')` +* Component HTML attributes: `autocomplete="on"` * Container classes : : `form-group` * Display success : `config('bootstrap-components.form.formValidation.displaySuccess')` * Display failure : `config('bootstrap-components.form.formValidation.displayFailure')` @@ -77,6 +78,7 @@ * Prepend : `` * Label positioned above : `config('bootstrap-components.form.labelPositionedAbove')` +* Component HTML attributes: `autocomplete="on"` * Container classes : : `form-group` * Display success : `config('bootstrap-components.form.formValidation.displaySuccess')` * Display failure : `config('bootstrap-components.form.formValidation.displayFailure')` @@ -109,6 +111,7 @@ * Prepend : `` * Label positioned above : `config('bootstrap-components.form.labelPositionedAbove')` +* Component HTML attributes: `autocomplete="on"` * Container classes : : `form-group` * Display success : `config('bootstrap-components.form.formValidation.displaySuccess')` * Display failure : `config('bootstrap-components.form.formValidation.displayFailure')` diff --git a/docs/api/types.md b/docs/api/types.md index f9bb15b9..b0a53905 100644 --- a/docs/api/types.md +++ b/docs/api/types.md @@ -21,20 +21,20 @@ |---|---|---| | containerId(string $containerId): self | No | Set the component container id. | | componentId(string $componentId): self | No | Set the component id. | -| containerClasses(array $containerClasses): self | No | Set the component container classes. | -| componentClasses(array $componentClasses): self | No | Set the component classes. | -| containerHtmlAttributes(array $containerHtmlAttributes): self | No | Set the component container HTML attributes. | -| componentHtmlAttributes(array $componentHtmlAttributes): self | No | Set the component HTML attributes. | +| componentClasses(array $componentClasses): self | No | The given HTML classes will replace the component default ones. You can activate the merge mode in order to add the given classes to the default ones by setting the second argument to `true`. | +| containerClasses(array $containerClasses): self | No | The given HTML classes will replace the component default ones. You can activate the merge mode in order to add the given classes to the default ones by setting the second argument to `true`. | +| componentHtmlAttributes(array $componentHtmlAttributes): self | No | The given HTML attributes will replace the component default ones. You can activate the merge mode in order to add the given attributes to the default ones by setting the second argument to `true`. | +| containerHtmlAttributes(array $containerHtmlAttributes): self | No | The given HTML attributes will replace the component default ones. You can activate the merge mode in order to add the given attributes to the default ones by setting the second argument to `true`. | **Usage** ```php ->containerId('container-id') - ->componentId('component-id') ->containerClasses(['container', 'classes']) - ->componentClasses(['component', 'classes']) ->containerHtmlAttributes(['container', 'html', 'attributes']) + ->componentId('component-id') + ->componentClasses(['component', 'classes']) ->componentHtmlAttributes(['component', 'html', 'attributes']); ``` @@ -57,6 +57,7 @@ | caption(?string $caption): self | No | Set the component caption. | | displaySuccess(?bool $displaySuccess = true): self | No | Set the component input validation success display status. | | displayFailure(?bool $displayFailure = true): self | No | Set the component input validation failure display status. | +| errorBag(string $errorBag): self | No | Define the name of the error bag that will contain the error related to this input. By default, the Laravel error bag is `default`. | **Usage** @@ -70,15 +71,16 @@ ->append('') ->label('Email') ->labelPositionedAbove() - ->placeholder('Set your e-mail') - ->caption('Set your caption here.') + ->placeholder('Set your email') + ->caption('Your email will tested against to RFC, DNS and spoof checks.') ->displaySuccess(false) - ->displayFailure(false); + ->displayFailure(false) + ->errorBag('profileUpdate'); ``` **Components** -* [Input e-mail](./components.md#input-e-mail) +* [Input email](./components.md#input-email) * [Input password](./components.md#input-password) * [Input URL](./components.md#input-url) * [Input tel](./components.md#input-tel) @@ -120,15 +122,9 @@ // inherits FormAbstract methods ->locales(['fr', 'en']) - ->value(function(string $locale){ - return $name[$locale]; - }); - ->prepend(function(string $locale){ - return 'prepend-' . $locale; - }) - ->append(function(string $locale){ - return 'append-' . $locale; - }) + ->value(fn(string $locale) => $name[$locale]); + ->prepend(fn(string $locale) => 'prepend-' . $locale) + ->append(fn(string $locale) => 'append-' . $locale) ``` **Components** @@ -176,9 +172,7 @@ ```php // inherits FormAbstract methods - ->uploadedFile(function(){ - return '
Some HTML
'; - }) + ->uploadedFile(fn() => '
Some HTML
') ->showRemoveCheckbox(true, 'Remove this file'); ``` @@ -242,9 +236,7 @@ ]), 'id', 'title') ->selected('id', 1) // or ->selected('id', [1]) in multiple mode - ->disabled(function(array $option){ - return ! $option['active']; - }) + ->disabled(fn(array $option) => ! $option['active']) ->multiple(); ``` diff --git a/docs/upgrade-guides/from-v2-to-v3.md b/docs/upgrade-guides/from-v2-to-v3.md new file mode 100644 index 00000000..ca7f6fa9 --- /dev/null +++ b/docs/upgrade-guides/from-v2-to-v3.md @@ -0,0 +1,19 @@ +# Upgrade from v2 to v3 + +Follow the steps below to upgrade the package. + +## Laravel and PHP old version support drop + +Whereas Laravel 8 is now supported by this package, support for any version before Laravel 7 has been dropped. + +Support for any version before PHP 7.4 has also been dropped. + +As so, you should upgrade your Laravel and PHP versions in order upgrade to V3. + +## See all changes + +See all change with the [comparison tool](https://github.com/Okipa/laravel-bootstrap-components/compare/2.2.3...3.0.0). + +## Undocumented changes + +If you see any forgotten and undocumented change, please submit a PR to add them to this upgrade guide. diff --git a/docs/upgrade-guides/from-v4-to-v5.md b/docs/upgrade-guides/from-v4-to-v5.md new file mode 100644 index 00000000..18b03a1e --- /dev/null +++ b/docs/upgrade-guides/from-v4-to-v5.md @@ -0,0 +1,67 @@ +# Upgrade from v4 to v5 + +Follow the steps below to upgrade the package. + +## Livewire support + +There was an issue preventing the error message and the validation class to be displayed on form components when they were used into a livewire component. + +This was related to the fact the session was used to detect errors, which can't work with livewire as the `$errors` variable is passed in the blade view on re-rendering. + +This has been fixed and the error message + the validation class are now generated from the `$errors` variable given in the view instead of the session. + +## Templates updated + +Related to the previous point (Livewire support added), the view templates provided with this package have been updated. + +If you have published the views in order to make some customizations, you will have to [re-publish them](../../README.md#templates) and to redo your customizations. + +## Named validation bag support + +All form components can now correctly display validation class and error message when using a named validation bag. + +If you validate an email this way: + +```php +Validator::make( + ['email' => 'spoof@email.test'] + ['email' => ['required', 'string', 'email:rfc,dns,spoof']], +])->validateWithBag('profileUpdate'); +``` + +The `is-invalid` validation class and the input related error message will correctly will be displayed when the form will be submitted with: + +```blade +{{ inputEmail()->name('email')->errorBag('profileUpdate') }} +``` + +## Methods signature update + +The following methods have gained the ability to merge given HTML classes or HTML attributes to the component default ones instead of replacing them. + +To use this new behaviour, you'll just have to set the second `$mergeMode` boolean attribute to `true`. + +* All components: `componentClasses` +* All components: `containerClasses` +* All components: `componentHtmlAttributes` +* All components: `containerHtmlAttributes` +* Image component: `linkClasses` +* Image component: `linkHtmlAttributes` + +For example, using `inputText()->name('name')->componentClasses(['merged', 'classes'], true)` will append the `merged classes` classes to the ones defined by default on the input text component. + +## Components default config update + +The `autocomplete="on"` HTML attribute has been added in the default configuration of the following components, in order to improve the default behavior (see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete): + +* Input email component +* Input password component +* Input url component + +## See all changes + +See all change with the [comparison tool](https://github.com/Okipa/laravel-bootstrap-components/compare/4.0.0...5.0.0). + +## Undocumented changes + +If you see any forgotten and undocumented change, please submit a PR to add them to this upgrade guide. diff --git a/resources/views/bootstrap-components/form/checkbox.blade.php b/resources/views/bootstrap-components/form/checkbox.blade.php index 264034c7..ca08dc3f 100644 --- a/resources/views/bootstrap-components/form/checkbox.blade.php +++ b/resources/views/bootstrap-components/form/checkbox.blade.php @@ -1,5 +1,5 @@ $containerId] : null) }}{{ html_classes('component-container', 'custom-control', 'custom-' . $type, $containerClasses) }}{{ html_attributes($containerHtmlAttributes) }}> - + @include('bootstrap-components::bootstrap-components.partials.validation-feedback') @include('bootstrap-components::bootstrap-components.partials.caption') diff --git a/resources/views/bootstrap-components/form/file.blade.php b/resources/views/bootstrap-components/form/file.blade.php index fa45ef09..9e085351 100644 --- a/resources/views/bootstrap-components/form/file.blade.php +++ b/resources/views/bootstrap-components/form/file.blade.php @@ -13,8 +13,8 @@ @endif @include('bootstrap-components::bootstrap-components.partials.prepend')
- - @if(($value = old($name, $value)) || $placeholder) + + @if($placeholder || ($value = old($name, $value))) @endif
diff --git a/resources/views/bootstrap-components/form/input.blade.php b/resources/views/bootstrap-components/form/input.blade.php index 800f0ba7..d158f4de 100644 --- a/resources/views/bootstrap-components/form/input.blade.php +++ b/resources/views/bootstrap-components/form/input.blade.php @@ -1,19 +1,19 @@ $containerId] : null) }}{{ html_classes('component-container', $containerClasses) }}{{ html_attributes($containerHtmlAttributes) }}> -@if($labelPositionedAbove) - @include('bootstrap-components::bootstrap-components.partials.label') -@endif -@if(! empty($prepend) || ! empty($append)) -
-@endif + @if($labelPositionedAbove) + @include('bootstrap-components::bootstrap-components.partials.label') + @endif + @if(! empty($prepend) || ! empty($append)) +
+ @endif @include('bootstrap-components::bootstrap-components.partials.prepend') - $placeholder] : null, $componentHtmlAttributes) }}> + $placeholder] : null, $componentHtmlAttributes) }}> @include('bootstrap-components::bootstrap-components.partials.append') @include('bootstrap-components::bootstrap-components.partials.validation-feedback') -@if(! empty($prepend) || ! empty($append)) -
-@endif -@unless($labelPositionedAbove) - @include('bootstrap-components::bootstrap-components.partials.label') -@endunless -@include('bootstrap-components::bootstrap-components.partials.caption') + @if(! empty($prepend) || ! empty($append)) +
+ @endif + @unless($labelPositionedAbove) + @include('bootstrap-components::bootstrap-components.partials.label') + @endunless + @include('bootstrap-components::bootstrap-components.partials.caption') diff --git a/resources/views/bootstrap-components/form/radio.blade.php b/resources/views/bootstrap-components/form/radio.blade.php index 7ad16059..687662f4 100644 --- a/resources/views/bootstrap-components/form/radio.blade.php +++ b/resources/views/bootstrap-components/form/radio.blade.php @@ -1,5 +1,5 @@ $containerId] : null) }}{{ html_classes('component-container', 'custom-control', 'custom-radio', $containerClasses) }}{{ html_attributes($containerHtmlAttributes) }}> - + @include('bootstrap-components::bootstrap-components.partials.validation-feedback') @include('bootstrap-components::bootstrap-components.partials.caption') diff --git a/resources/views/bootstrap-components/form/select.blade.php b/resources/views/bootstrap-components/form/select.blade.php index b17de6e1..97cbb127 100644 --- a/resources/views/bootstrap-components/form/select.blade.php +++ b/resources/views/bootstrap-components/form/select.blade.php @@ -6,7 +6,7 @@
@endif @include('bootstrap-components::bootstrap-components.partials.prepend') - @if($placeholder) @endif diff --git a/resources/views/bootstrap-components/form/textarea.blade.php b/resources/views/bootstrap-components/form/textarea.blade.php index fe63760b..b3da217c 100644 --- a/resources/views/bootstrap-components/form/textarea.blade.php +++ b/resources/views/bootstrap-components/form/textarea.blade.php @@ -6,7 +6,7 @@
@endif @include('bootstrap-components::bootstrap-components.partials.prepend') - + @include('bootstrap-components::bootstrap-components.partials.append') @include('bootstrap-components::bootstrap-components.partials.validation-feedback') @if(! empty($prepend) || ! empty($append)) diff --git a/resources/views/bootstrap-components/partials/validation-feedback.blade.php b/resources/views/bootstrap-components/partials/validation-feedback.blade.php index 5c005353..75873b32 100644 --- a/resources/views/bootstrap-components/partials/validation-feedback.blade.php +++ b/resources/views/bootstrap-components/partials/validation-feedback.blade.php @@ -1,11 +1,9 @@ -@isset($errors) - @if($errorMessage && $displayFailure) -
- {!! $errorMessage !!} -
- @elseif($displaySuccess) -
- @lang('Field correctly filled.') -
- @endif -@endisset +@if($errorMessage = $errorMessage($errors ?? null, $locale ?? null)) +
+ {!! $errorMessage !!} +
+@elseif($successMessage = $successMessage()) +
+ {!! $successMessage !!} +
+@endif diff --git a/src/Components/Buttons/Abstracts/ButtonAbstract.php b/src/Components/Buttons/Abstracts/ButtonAbstract.php index ef476d0c..5170342f 100644 --- a/src/Components/Buttons/Abstracts/ButtonAbstract.php +++ b/src/Components/Buttons/Abstracts/ButtonAbstract.php @@ -26,11 +26,9 @@ public function route(string $route, array $params = []): self return $this; } - protected function getParameters(): array + protected function getViewParams(): array { - $url = $this->getUrl(); - - return array_merge(parent::getParameters(), compact('url')); + return array_merge(parent::getViewParams(), ['url' => $this->getUrl()]); } protected function getUrl(): ?string diff --git a/src/Components/Buttons/Abstracts/SubmitAbstract.php b/src/Components/Buttons/Abstracts/SubmitAbstract.php index 32b49540..883d99c8 100644 --- a/src/Components/Buttons/Abstracts/SubmitAbstract.php +++ b/src/Components/Buttons/Abstracts/SubmitAbstract.php @@ -41,20 +41,15 @@ public function label(?string $label): self return $this; } - protected function getValues(): array + protected function getViewParams(): array { - return array_merge(parent::getValues(), $this->getParameters()); - } - - protected function getParameters(): array - { - $type = $this->getType(); - $url = null; - $prepend = $this->getPrepend(); - $append = $this->getAppend(); - $label = $this->getLabel(); - - return compact('type', 'url', 'prepend', 'append', 'label'); + return array_merge(parent::getViewParams(), [ + 'type' => $this->getType(), + 'url' => $this->getType(), + 'prepend' => $this->getPrepend(), + 'append' => $this->getAppend(), + 'label' => $this->getLabel(), + ]); } protected function getPrepend(): ?string diff --git a/src/Components/ComponentAbstract.php b/src/Components/ComponentAbstract.php index 4cadf70b..98770ff4 100644 --- a/src/Components/ComponentAbstract.php +++ b/src/Components/ComponentAbstract.php @@ -26,24 +26,36 @@ public function __construct() { $this->type = $this->setType(); $this->view = $this->setView(); - $this->componentClasses = $this->setComponentClasses(); - $this->componentHtmlAttributes = $this->setComponentHtmlAttributes(); $this->containerClasses = $this->setContainerClasses(); $this->containerHtmlAttributes = $this->setContainerHtmlAttributes(); + $this->componentClasses = $this->setComponentClasses(); + $this->componentHtmlAttributes = $this->setComponentHtmlAttributes(); } - public function componentId(string $componentId): self + /** + * @return string + * @throws \Throwable + */ + public function toHtml(): string { - $this->componentId = $componentId; - - return $this; + return $this->render(); } - public function componentClasses(array $componentClasses): self + /** + * @param array $extraData + * + * @return string + * @throws \Throwable + */ + public function render(array $extraData = []): string { - $this->componentClasses = $componentClasses; + $this->checkValuesValidity(); + $view = $this->getView(); + $html = $view + ? (string) view('bootstrap-components::' . $view, array_merge($this->getViewParams(), $extraData))->render() + : ''; - return $this; + return trim($html); } public function containerId(string $containerId): self @@ -53,58 +65,47 @@ public function containerId(string $containerId): self return $this; } - /** - * Set the component container classes. - * - * @param array $containerClasses - * - * @return $this - */ - public function containerClasses(array $containerClasses): self + public function componentId(string $componentId): self { - $this->containerClasses = $containerClasses; + $this->componentId = $componentId; return $this; } - public function componentHtmlAttributes(array $componentHtmlAttributes): self + public function componentClasses(array $componentClasses, bool $mergeMode = false): self { - $this->componentHtmlAttributes = $componentHtmlAttributes; + $this->componentClasses = $mergeMode + ? array_merge($this->componentClasses, $componentClasses) + : $componentClasses; return $this; } - public function containerHtmlAttributes(array $containerHtmlAttributes): self + public function containerClasses(array $containerClasses, bool $mergeMode = false): self { - $this->containerHtmlAttributes = $containerHtmlAttributes; + $this->containerClasses = $mergeMode + ? array_merge($this->containerClasses, $containerClasses) + : $containerClasses; return $this; } - /** - * @return string - * @throws \Throwable - */ - public function toHtml(): string + public function componentHtmlAttributes(array $componentHtmlAttributes, bool $mergeMode = false): self { - return $this->render(); + $this->componentHtmlAttributes = $mergeMode + ? array_merge($this->componentHtmlAttributes, $componentHtmlAttributes) + : $componentHtmlAttributes; + + return $this; } - /** - * @param array $extraData - * - * @return string - * @throws \Throwable - */ - public function render(array $extraData = []): string + public function containerHtmlAttributes(array $containerHtmlAttributes, bool $mergeMode = false): self { - $this->checkValuesValidity(); - $view = $this->getView(); - $html = $view - ? (string) view('bootstrap-components::' . $view, array_merge($this->getValues(), $extraData))->render() - : ''; + $this->containerHtmlAttributes = $mergeMode + ? array_merge($this->containerHtmlAttributes, $containerHtmlAttributes) + : $containerHtmlAttributes; - return trim($html); + return $this; } abstract protected function checkValuesValidity(): void; @@ -116,23 +117,21 @@ protected function getView(): string abstract protected function setView(): string; - protected function getValues(): array + protected function getViewParams(): array { - $componentId = $this->getComponentId(); - $containerId = $this->getContainerId(); - $componentClasses = $this->getComponentClasses(); - $containerClasses = $this->getContainerClasses(); - $componentHtmlAttributes = $this->getComponentHtmlAttributes(); - $containerHtmlAttributes = $this->getContainerHtmlAttributes(); + return [ + 'containerId' => $this->getContainerId(), + 'containerClasses' => $this->getContainerClasses(), + 'containerHtmlAttributes' => $this->getContainerHtmlAttributes(), + 'componentId' => $this->getComponentId(), + 'componentClasses' => $this->getComponentClasses(), + 'componentHtmlAttributes' => $this->getComponentHtmlAttributes(), + ]; + } - return compact( - 'componentId', - 'containerId', - 'componentClasses', - 'containerClasses', - 'componentHtmlAttributes', - 'containerHtmlAttributes' - ); + protected function getContainerId(): ?string + { + return $this->containerId; } protected function getComponentId(): ?string @@ -140,11 +139,13 @@ protected function getComponentId(): ?string return $this->componentId; } - protected function getContainerId(): ?string + protected function getContainerClasses(): array { - return $this->containerId; + return $this->containerClasses; } + abstract protected function setContainerClasses(): array; + protected function getComponentClasses(): array { return $this->componentClasses; @@ -152,12 +153,12 @@ protected function getComponentClasses(): array abstract protected function setComponentClasses(): array; - protected function getContainerClasses(): array + protected function getContainerHtmlAttributes(): array { - return $this->containerClasses; + return $this->containerHtmlAttributes; } - abstract protected function setContainerClasses(): array; + abstract protected function setContainerHtmlAttributes(): array; protected function getComponentHtmlAttributes(): array { @@ -166,13 +167,6 @@ protected function getComponentHtmlAttributes(): array abstract protected function setComponentHtmlAttributes(): array; - protected function getContainerHtmlAttributes(): array - { - return $this->containerHtmlAttributes; - } - - abstract protected function setContainerHtmlAttributes(): array; - protected function getType(): string { return $this->type; diff --git a/src/Components/Form/Abstracts/CheckableAbstract.php b/src/Components/Form/Abstracts/CheckableAbstract.php index b921d57a..30d7b782 100644 --- a/src/Components/Form/Abstracts/CheckableAbstract.php +++ b/src/Components/Form/Abstracts/CheckableAbstract.php @@ -15,18 +15,28 @@ public function checked(bool $checked = true): self protected function setLabelPositionedAbove(): bool { - return true; // Unused for checkable components. + // Setting `true` but this property will not be used for checkable components. + return true; } protected function getComponentHtmlAttributes(): array { - return array_merge(parent::getComponentHtmlAttributes(), $this->getChecked() ? ['checked' => 'checked'] : []); + return array_merge($this->componentHtmlAttributes, $this->getChecked() ? ['checked' => 'checked'] : []); } protected function getChecked(): bool { - $old = old($this->convertArrayNameInNotation()); - - return $old ?? $this->checked ?? (bool) (optional($this->model)->{$this->getName()} ?: $this->value); + $oldChecked = old($this->convertArrayNameInNotation()); + if (isset($oldChecked)) { + return $oldChecked; + } + if (isset($this->checked)) { + return $this->checked; + } + if (isset($this->value)) { + return (bool) $this->value; + } + + return (bool) optional($this->model)->{$this->getName()}; } } diff --git a/src/Components/Form/Abstracts/FormAbstract.php b/src/Components/Form/Abstracts/FormAbstract.php index 7afcd78a..e6eae8cb 100644 --- a/src/Components/Form/Abstracts/FormAbstract.php +++ b/src/Components/Form/Abstracts/FormAbstract.php @@ -4,7 +4,9 @@ use Closure; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\MessageBag; use Illuminate\Support\Str; +use Illuminate\Support\ViewErrorBag; use Okipa\LaravelBootstrapComponents\Components\ComponentAbstract; use Okipa\LaravelBootstrapComponents\Components\Form\Traits\FormValidityChecks; @@ -12,6 +14,9 @@ abstract class FormAbstract extends ComponentAbstract { use FormValidityChecks; + /** @property mixed $value */ + protected $value; + protected ?Model $model = null; protected string $name; @@ -30,15 +35,14 @@ abstract class FormAbstract extends ComponentAbstract protected bool $labelPositionedAbove; - /** @property mixed $value */ - protected $value; - - protected ?string $placeholder; + protected ?string $placeholder = null; protected bool $displaySuccess; protected bool $displayFailure; + protected string $errorBag = 'default'; + public function __construct() { parent::__construct(); @@ -102,6 +106,11 @@ public function placeholder(?string $placeholder): self return $this; } + /** + * @param mixed $value + * + * @return $this + */ public function value($value): self { $this->value = $value; @@ -138,93 +147,95 @@ public function displayFailure(bool $displayFailure = true): self return $this; } - protected function getValues(): array + public function errorBag(string $errorBag): self { - return array_merge(parent::getValues(), $this->getParameters()); + $this->errorBag = $errorBag; + + return $this; } - protected function getParameters(): array + protected function getViewParams(): array { - $model = $this->getModel(); - $type = $this->getType(); - $name = $this->getName(); - $prepend = $this->getPrepend(); - $append = $this->getAppend(); - $caption = $this->getCaption(); - $label = $this->getLabel(); - $labelPositionedAbove = $this->getLabelPositionedAbove(); - $value = $this->getValue(); - $placeholder = $this->getPlaceholder(); - $displaySuccess = $this->getDisplaySuccess(); - $displayFailure = $this->getDisplayFailure(); - $validationClass = $this->getValidationClass(); - $errorMessage = $this->getErrorMessage(); - - return compact( - 'model', - 'type', - 'name', - 'prepend', - 'append', - 'caption', - 'label', - 'labelPositionedAbove', - 'value', - 'placeholder', - 'displaySuccess', - 'displayFailure', - 'validationClass', - 'errorMessage' - ); + return array_merge(parent::getViewParams(), [ + 'validationClass' => fn(?ViewErrorBag $errors) => $this->getValidationClass($errors), + 'errorMessage' => fn(?ViewErrorBag $errors) => $this->getErrorMessage($errors), + 'successMessage' => fn() => $this->getSuccessMessage(), + 'labelPositionedAbove' => $this->getLabelPositionedAbove(), + 'label' => $this->getLabel(), + 'type' => $this->getType(), + 'name' => $this->getName(), + 'value' => $this->getValue(), + 'placeholder' => $this->getPlaceholder(), + 'prepend' => $this->getPrepend(), + 'append' => $this->getAppend(), + 'caption' => $this->getCaption(), + ]); } - protected function getModel(): ?Model + protected function getValidationClass(?ViewErrorBag $errors): ?string { - return $this->model; + if (! $errors) { + return null; + } + if ($this->getErrorMessageBag($errors)->isEmpty()) { + return null; + } + if ($this->getErrorMessageBag($errors)->has($this->convertArrayNameInNotation())) { + return $this->getDisplayFailure() ? 'is-invalid' : null; + } + + // Only highlight valid fields if there are invalid fields. + return $this->getDisplaySuccess() ? 'is-valid' : null; } - protected function getName(): string + protected function getErrorMessageBag(ViewErrorBag $errors): MessageBag { - return $this->name ?? ''; + return $errors->{$this->errorBag}; } - protected function getPrepend(): ?string + protected function convertArrayNameInNotation(string $notation = '.'): string { - $prepend = $this->prepend; - - // Fallback for usage of closure with multilingual disabled. - return $prepend instanceof Closure ? $prepend(app()->getLocale()) : $prepend; + return str_replace(['[', ']'], [$notation, ''], $this->getName()); } - abstract protected function setPrepend(): ?string; - - protected function getAppend(): ?string + protected function getName(): string { - $append = $this->append; + return $this->name ?? ''; + } - // Fallback for usage of closure with multilingual disabled. - return $append instanceof Closure ? $append(app()->getLocale()) : $append; + protected function getDisplayFailure(): bool + { + return $this->displayFailure; } - abstract protected function setAppend(): ?string; + abstract protected function setDisplayFailure(): bool; - protected function getCaption(): ?string + protected function getDisplaySuccess(): bool { - return $this->caption; + return $this->displaySuccess; } - abstract protected function setCaption(): ?string; + abstract protected function setDisplaySuccess(): bool; - protected function getLabel(): ?string + protected function getErrorMessage(?ViewErrorBag $errors): ?string { - $label = $this->label ?: (string) __('validation.attributes.' . $this->removeArrayCharactersFromName()); + if (! $errors) { + return null; + } + if (! $this->getDisplayFailure()) { + return null; + } - return $this->hideLabel ? null : $label; + return $this->getErrorMessageBag($errors)->first($this->convertArrayNameInNotation()); } - protected function removeArrayCharactersFromName(): string + protected function getSuccessMessage(): ?string { - return strstr($this->getName(), '[', true) ?: $this->getName(); + if ($this->getDisplaySuccess()) { + return (string) __('Field correctly filled.'); + } + + return null; } protected function getLabelPositionedAbove(): bool @@ -234,59 +245,96 @@ protected function getLabelPositionedAbove(): bool abstract protected function setLabelPositionedAbove(): bool; - protected function getValue() + protected function getLabel(): ?string { - $value = old($this->convertArrayNameInNotation()) ?: $this->value; - // Fallback for usage of closure with multilingual disabled. - $value = $value instanceof Closure ? $value(app()->getLocale()) : $value; + if ($this->hideLabel) { + return null; + } + if ($this->label) { + return $this->label; + } - return $value ?? optional($this->getModel())->{$this->convertArrayNameInNotation()}; + return (string) __('validation.attributes.' . $this->removeArrayCharactersFromName()); } - protected function convertArrayNameInNotation(string $notation = '.'): string + protected function removeArrayCharactersFromName(): string { - return str_replace(['[', ']'], [$notation, ''], $this->getName()); + return strstr($this->getName(), '[', true) ?: $this->getName(); } - protected function getPlaceholder(): ?string + /** @return mixed */ + protected function getValue() { - return $this->placeholder - ?? ($this->getLabel() ?: (string) __('validation.attributes.' . $this->removeArrayCharactersFromName())); + $oldValue = old($this->convertArrayNameInNotation()); + if ($oldValue) { + return $oldValue; + } + // Fallback for usage of closure with multilingual disabled. + if ($this->value instanceof Closure) { + return ($this->value)(app()->getLocale()); + } + if (isset($this->value)) { + return $this->value; + } + + return optional($this->model)->{$this->convertArrayNameInNotation()}; } - protected function getDisplaySuccess(): bool + protected function getPlaceholder(): ?string { - return $this->displaySuccess; - } + if (isset($this->placeholder)) { + return $this->placeholder; + } + $label = $this->getLabel(); + if ($label) { + return $label; + } - abstract protected function setDisplaySuccess(): bool; + return (string) __('validation.attributes.' . $this->removeArrayCharactersFromName()); + } - protected function getDisplayFailure(): bool + protected function getPrepend(): ?string { - return $this->displayFailure; + // Fallback for usage of closure with multilingual disabled. + if ($this->prepend instanceof Closure) { + return ($this->prepend)(app()->getLocale()); + } + + return $this->prepend; } - abstract protected function setDisplayFailure(): bool; + abstract protected function setPrepend(): ?string; - protected function getValidationClass(): ?string + protected function getAppend(): ?string { - if (session()->has('errors')) { - return session()->get('errors')->has($this->convertArrayNameInNotation()) - ? ($this->getDisplayFailure() ? 'is-invalid' : null) - : ($this->getDisplaySuccess() ? 'is-valid' : null); + // Fallback for usage of closure with multilingual disabled. + if ($this->append instanceof Closure) { + return ($this->append)(app()->getLocale()); } - return null; + return $this->append; } - protected function getErrorMessage(): ?string + abstract protected function setAppend(): ?string; + + protected function getCaption(): ?string { - return optional(session()->get('errors'))->first($this->convertArrayNameInNotation()); + return $this->caption; } + abstract protected function setCaption(): ?string; + protected function getComponentId(): string { - return parent::getComponentId() - ?? $this->getType() . '-' . Str::slug(Str::snake($this->convertArrayNameInNotation('-'), '-')); + if ($this->componentId) { + return $this->componentId; + } + + return $this->getType() . '-' . Str::slug(Str::snake($this->convertArrayNameInNotation('-'), '-')); + } + + protected function getModel(): ?Model + { + return $this->model; } } diff --git a/src/Components/Form/Abstracts/MultilingualAbstract.php b/src/Components/Form/Abstracts/MultilingualAbstract.php index 3b02cea6..fdc94983 100644 --- a/src/Components/Form/Abstracts/MultilingualAbstract.php +++ b/src/Components/Form/Abstracts/MultilingualAbstract.php @@ -3,13 +3,11 @@ namespace Okipa\LaravelBootstrapComponents\Components\Form\Abstracts; use Closure; +use Illuminate\Support\ViewErrorBag; use Okipa\LaravelBootstrapComponents\Components\Form\Multilingual\Resolver; -use Okipa\LaravelBootstrapComponents\Components\Form\Traits\MultilingualValidityChecks; abstract class MultilingualAbstract extends FormAbstract { - use MultilingualValidityChecks; - protected Resolver $multilingualResolver; protected array $locales; @@ -59,7 +57,7 @@ protected function multilingualRender(array $extraData = []): string $componentHtml = $view ? (string) view( 'bootstrap-components::' . $view, - array_merge($this->getLocalizedValues($locale), $extraData) + array_merge($this->getLocalizedViewParams($locale), $extraData) )->render() : ''; $html .= trim($componentHtml); @@ -68,39 +66,41 @@ protected function multilingualRender(array $extraData = []): string return $html; } - protected function getLocalizedValues(string $locale): array - { - return array_merge($this->getValues(), $this->getLocalizedParameters($locale)); + protected function getLocalizedViewParams(string $locale): array + { + return [ + 'component' => $this, + 'locale' => $locale, + 'containerId' => $this->getLocalizedContainerId($locale), + 'containerClasses' => $this->getContainerClasses(), + 'containerHtmlAttributes' => $this->getContainerHtmlAttributes(), + 'componentId' => $this->getLocalizedComponentId($locale), + 'componentClasses' => $this->getComponentClasses(), + 'componentHtmlAttributes' => $this->getLocalizedComponentHtmlAttributes($locale), + 'validationClass' => fn( + ?ViewErrorBag $errors, + ?string $locale + ) => $this->getLocalizedValidationClass($errors, $locale), + 'errorMessage' => fn( + ?ViewErrorBag $errors, + ?string $locale + ) => $this->getLocalizedErrorMessage($errors, $locale), + 'successMessage' => fn() => $this->getSuccessMessage(), + 'labelPositionedAbove' => $this->getLabelPositionedAbove(), + 'label' => $this->getLocalizedLabel($locale), + 'type' => $this->getType(), + 'name' => $this->getLocalizedName($locale), + 'value' => $this->getLocalizedValue($locale), + 'placeholder' => $this->getLocalizedPlaceholder($locale), + 'prepend' => $this->getLocalizedPrepend($locale), + 'append' => $this->getLocalizedAppend($locale), + 'caption' => $this->getCaption(), + ]; } - protected function getLocalizedParameters(string $locale): array + protected function getLocalizedContainerId(string $locale): ?string { - $parentParams = $this->getParameters(); - $componentId = $this->getLocalizedComponentId($locale); - $containerId = $this->getLocalizedContainerId($locale); - $componentHtmlAttributes = $this->getLocalizedComponentHtmlAttributes($locale); - $name = $this->getLocalizedName($locale); - $prepend = $this->getLocalizedPrepend($locale); - $append = $this->getLocalizedAppend($locale); - $label = $this->getLocalizedLabel($locale); - $value = $this->getLocalizedValue($locale); - $placeholder = $this->getLocalizedPlaceholder($locale); - $validationClass = $this->getLocalizedValidationClass($locale); - $errorMessage = $this->getLocalizedErrorMessage($locale); - - return array_merge($parentParams, compact( - 'componentId', - 'containerId', - 'componentHtmlAttributes', - 'name', - 'prepend', - 'append', - 'label', - 'value', - 'placeholder', - 'validationClass', - 'errorMessage' - )); + return $this->getContainerId() ? $this->getContainerId() . '-' . $locale : null; } protected function getLocalizedComponentId(string $locale): string @@ -108,35 +108,46 @@ protected function getLocalizedComponentId(string $locale): string return $this->getComponentId() . '-' . $locale; } - protected function getLocalizedContainerId(string $locale): ?string - { - return $this->getContainerId() ? $this->getContainerId() . '-' . $locale : null; - } - protected function getLocalizedComponentHtmlAttributes(string $locale): array { return array_merge(['data-locale' => $locale], $this->getComponentHtmlAttributes()); } - protected function getLocalizedName(string $locale): string + protected function getLocalizedValidationClass(?ViewErrorBag $errors, string $locale): ?string { - return $this->multilingualResolver->resolveLocalizedName($this->getName(), $locale); - } - - protected function getLocalizedPrepend(string $locale): ?string - { - $prepend = $this->prepend; + if (! $errors) { + return null; + } + if ($this->getErrorMessageBag($errors)->isEmpty()) { + return null; + } + if ( + $this->getErrorMessageBag($errors)->has($this->multilingualResolver->resolveErrorMessageBagKey( + $this->getName(), + $locale + )) + ) { + return $this->getDisplayFailure() ? 'is-invalid' : null; + } - // fallback for usage of closure with multilingual disabled - return $prepend instanceof Closure ? $prepend($locale) : $prepend; + // Only highlight valid fields if there are invalid fields. + return $this->getDisplaySuccess() ? 'is-valid' : null; } - protected function getLocalizedAppend(string $locale): ?string + protected function getLocalizedErrorMessage(?ViewErrorBag $errors, string $locale): ?string { - $append = $this->append; + if (! $errors) { + return null; + } + if (! $this->getDisplayFailure()) { + return null; + } - // fallback for usage of closure with multilingual disabled - return $append instanceof Closure ? $append($locale) : $append; + return $this->multilingualResolver->resolveErrorMessage( + $this->getName(), + $this->getErrorMessageBag($errors), + $locale + ); } protected function getLocalizedLabel(string $locale): ?string @@ -146,18 +157,30 @@ protected function getLocalizedLabel(string $locale): ?string return $label ? $label . ' (' . mb_strtoupper($locale) . ')' : null; } + protected function getLocalizedName(string $locale): string + { + return $this->multilingualResolver->resolveLocalizedName($this->getName(), $locale); + } + protected function getLocalizedValue(string $locale) { - $oldValue = $this->multilingualResolver->resolveLocalizedOldValue($this->getName(), $locale); - /** @var Closure|null $valueClosure */ - $valueClosure = $this->multilingualMode() ? $this->value : null; - $modelValue = $this->multilingualResolver->resolveLocalizedModelValue( + $oldLocalizedValue = $this->multilingualResolver->resolveLocalizedOldValue($this->getName(), $locale); + if ($oldLocalizedValue) { + return $oldLocalizedValue; + } + if ($this->value instanceof Closure) { + return ($this->value)($locale); + } + $localizedValue = $this->multilingualResolver->resolveLocalizedModelValue( $this->getName(), $locale, $this->getModel() ); + if ($localizedValue) { + return $localizedValue; + } - return $oldValue ?? ($valueClosure ? $valueClosure($locale) : $modelValue); + return $this->getValue(); } protected function getLocalizedPlaceholder(string $locale): ?string @@ -167,21 +190,21 @@ protected function getLocalizedPlaceholder(string $locale): ?string return $placeholder ? $placeholder . ' (' . mb_strtoupper($locale) . ')' : null; } - protected function getLocalizedValidationClass(string $locale): ?string + protected function getLocalizedPrepend(string $locale): ?string { - if (session()->has('errors')) { - $errorMessageBagKey = $this->multilingualResolver->resolveErrorMessageBagKey($this->getName(), $locale); - - return session()->get('errors')->has($errorMessageBagKey) - ? ($this->getDisplayFailure() ? 'is-invalid' : null) - : ($this->getDisplaySuccess() ? 'is-valid' : null); + if ($this->prepend instanceof Closure) { + return ($this->prepend)($locale); } - return null; + return $this->getPrepend(); } - protected function getLocalizedErrorMessage(string $locale): ?string + protected function getLocalizedAppend(string $locale): ?string { - return $this->multilingualResolver->resolveErrorMessage(parent::getName(), $locale); + if ($this->append instanceof Closure) { + return ($this->append)($locale); + } + + return $this->getAppend(); } } diff --git a/src/Components/Form/Abstracts/RadioAbstract.php b/src/Components/Form/Abstracts/RadioAbstract.php index 5f2253f6..e678ac31 100644 --- a/src/Components/Form/Abstracts/RadioAbstract.php +++ b/src/Components/Form/Abstracts/RadioAbstract.php @@ -11,8 +11,11 @@ abstract class RadioAbstract extends CheckableAbstract protected function getComponentId(): string { - return $this->componentId - ?? $this->getType() . '-' . Str::slug(Str::snake($this->convertArrayNameInNotation('-'), '-') + if ($this->componentId) { + return $this->componentId; + } + + return $this->getType() . '-' . Str::slug(Str::snake($this->convertArrayNameInNotation('-'), '-') . '-' . Str::snake($this->getValue(), '-')); } @@ -22,7 +25,10 @@ protected function getChecked(): bool if (isset($old) && $old !== '') { return $old === (string) $this->value; } + if (isset($this->checked)) { + return $this->checked; + } - return $this->checked ?? optional($this->model)->{$this->getName()} === $this->value; + return optional($this->model)->{$this->getName()} === $this->value; } } diff --git a/src/Components/Form/Abstracts/SelectableAbstract.php b/src/Components/Form/Abstracts/SelectableAbstract.php index 155ddf52..d0a06405 100644 --- a/src/Components/Form/Abstracts/SelectableAbstract.php +++ b/src/Components/Form/Abstracts/SelectableAbstract.php @@ -70,17 +70,14 @@ public function multiple(bool $multiple = true): self return $this; } - protected function getParameters(): array + protected function getViewParams(): array { - $options = $this->getOptions(); - $optionValueField = $this->getOptionValueField(); - $optionLabelField = $this->getOptionLabelField(); - $multiple = $this->getMultiple(); - - return array_merge( - parent::getParameters(), - compact('options', 'optionValueField', 'optionLabelField', 'multiple') - ); + return array_merge(parent::getViewParams(), [ + 'options' => $this->getOptions(), + 'optionValueField' => $this->getOptionValueField(), + 'optionLabelField' => $this->getOptionLabelField(), + 'multiple' => $this->getMultiple(), + ]); } protected function getOptions(): array diff --git a/src/Components/Form/Abstracts/TemporalAbstract.php b/src/Components/Form/Abstracts/TemporalAbstract.php index 3a067e3d..6b80a60e 100644 --- a/src/Components/Form/Abstracts/TemporalAbstract.php +++ b/src/Components/Form/Abstracts/TemporalAbstract.php @@ -3,6 +3,7 @@ namespace Okipa\LaravelBootstrapComponents\Components\Form\Abstracts; use Carbon\Carbon; +use DateTime; use Okipa\LaravelBootstrapComponents\Components\Form\Traits\TemporalValidityChecks; abstract class TemporalAbstract extends FormAbstract @@ -31,7 +32,7 @@ protected function getValue(): ?string return null; } - return is_a($value, 'DateTime') + return $value instanceof DateTime ? $value->format($this->getFormat()) : Carbon::parse($value)->format($this->getFormat()); } diff --git a/src/Components/Form/Abstracts/UploadableAbstract.php b/src/Components/Form/Abstracts/UploadableAbstract.php index b83adb31..dc7970e0 100644 --- a/src/Components/Form/Abstracts/UploadableAbstract.php +++ b/src/Components/Form/Abstracts/UploadableAbstract.php @@ -35,12 +35,14 @@ public function showRemoveCheckbox(bool $showRemoveCheckbox = true, ?string $rem return $this; } - protected function getValues(): array + protected function getViewParams(): array { - return array_merge(parent::getValues(), [ + $parentViewParams = parent::getViewParams(); + + return array_merge($parentViewParams, [ 'uploadedFileHtml' => $this->getUploadedFileHtml(), 'showRemoveCheckbox' => $this->getShowRemoveCheckbox(), - 'removeCheckboxLabel' => $this->getRemoveCheckboxLabel(parent::getValues()['label']), + 'removeCheckboxLabel' => $this->getRemoveCheckboxLabel($parentViewParams['label']), 'removeCheckboxName' => $this->getShowRemoveCheckboxName(), ]); } diff --git a/src/Components/Form/InputEmail.php b/src/Components/Form/InputEmail.php index f22bfd33..4a45dad4 100644 --- a/src/Components/Form/InputEmail.php +++ b/src/Components/Form/InputEmail.php @@ -48,7 +48,7 @@ protected function setContainerClasses(): array protected function setComponentHtmlAttributes(): array { - return []; + return ['autocomplete' => 'on']; } protected function setContainerHtmlAttributes(): array diff --git a/src/Components/Form/InputPassword.php b/src/Components/Form/InputPassword.php index 8f4ccc35..8645b3e3 100644 --- a/src/Components/Form/InputPassword.php +++ b/src/Components/Form/InputPassword.php @@ -53,7 +53,7 @@ protected function setComponentHtmlAttributes(): array protected function setContainerHtmlAttributes(): array { - return []; + return ['autocomplete' => 'on']; } protected function setDisplaySuccess(): bool diff --git a/src/Components/Form/InputTel.php b/src/Components/Form/InputTel.php index 996876d0..ffa20318 100644 --- a/src/Components/Form/InputTel.php +++ b/src/Components/Form/InputTel.php @@ -48,7 +48,7 @@ protected function setContainerClasses(): array protected function setComponentHtmlAttributes(): array { - return []; + return ['autocomplete' => 'on']; } protected function setContainerHtmlAttributes(): array diff --git a/src/Components/Form/Multilingual/Resolver.php b/src/Components/Form/Multilingual/Resolver.php index 52de947c..6f377729 100644 --- a/src/Components/Form/Multilingual/Resolver.php +++ b/src/Components/Form/Multilingual/Resolver.php @@ -3,6 +3,7 @@ namespace Okipa\LaravelBootstrapComponents\Components\Form\Multilingual; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\MessageBag; class Resolver { @@ -25,19 +26,19 @@ public function resolveLocalizedOldValue(string $name, string $locale): ?string public function resolveLocalizedModelValue(string $name, string $locale, ?Model $model): ?string { - return data_get(optional($model)->{$name}, $locale); + return data_get($model, "$name.$locale"); } - public function resolveErrorMessage(string $name, string $locale): ?string + public function resolveErrorMessage(string $name, MessageBag $errors, string $locale): ?string { $errorMessageBagKey = $this->resolveErrorMessageBagKey($name, $locale); - $errorMessage = optional(session()->get('errors'))->first($errorMessageBagKey); + $errorMessage = $errors->first($errorMessageBagKey); $errorMessage = $this->undoInputNameLaravelUnderscoreRemovalInErrorMessage($name, $locale, $errorMessage); return $errorMessage ? str_replace( $errorMessageBagKey, - ((string) __('validation.attributes.' . $name)) . ' (' . strtoupper($locale) . ')', + __('validation.attributes.' . $name) . ' (' . strtoupper($locale) . ')', $errorMessage ) : null; diff --git a/src/Components/Form/Traits/MultilingualValidityChecks.php b/src/Components/Form/Traits/MultilingualValidityChecks.php deleted file mode 100644 index 3cad83aa..00000000 --- a/src/Components/Form/Traits/MultilingualValidityChecks.php +++ /dev/null @@ -1,17 +0,0 @@ -value) && $this->value && $this->multilingualMode()) { - throw new InvalidArgumentException(get_class($this) . ' : A multilingual component value has to - be set from this callable result : « ->value(function($locale){}) ».'); - } - } -} diff --git a/src/Components/Form/Traits/RadioValidityChecks.php b/src/Components/Form/Traits/RadioValidityChecks.php index f10ffec8..c674e9e4 100644 --- a/src/Components/Form/Traits/RadioValidityChecks.php +++ b/src/Components/Form/Traits/RadioValidityChecks.php @@ -9,7 +9,8 @@ trait RadioValidityChecks protected function checkValuesValidity(): void { parent::checkValuesValidity(); - if (! isset($this->value) || $this->value === '') { + $value = $this->getValue(); + if (! isset($value) || $value === '') { throw new InvalidArgumentException( get_class($this) . ' : Missing $value property. Please use the value() method to set a value.' ); diff --git a/src/Components/Form/Traits/SelectValidityChecks.php b/src/Components/Form/Traits/SelectValidityChecks.php index 55a814cf..92edb957 100644 --- a/src/Components/Form/Traits/SelectValidityChecks.php +++ b/src/Components/Form/Traits/SelectValidityChecks.php @@ -57,12 +57,16 @@ protected function checkSelectedFieldToCompareExistenceInOption(array $option): protected function checkMultipleModeModelAttributeType(): void { - if ($this->model && isset($this->model->{$this->getName()}) && ! is_array($this->model->{$this->getName()})) { + if ( + $this->getModel() + && $this->getModel()->{$this->getName()} + && ! is_array($this->getModel()->{$this->getName()}) + ) { throw new InvalidArgumentException( get_class($this) . ' : The « ' . $this->getName() . ' » attribute from the given « ' - . $this->model->getMorphClass() + . $this->getModel()->getMorphClass() . ' » model has to be an array when the select() component is in multiple mode : « ' - . gettype($this->model->{$this->getName()}) . ' » type given.' + . gettype($this->getModel()->{$this->getName()}) . ' » type given.' ); } } @@ -83,7 +87,7 @@ protected function checkSingleModeSelectedValueToCompareType(): void if ( $this->selectedValueToCompare && ! is_string($this->selectedValueToCompare) - && ! is_integer($this->selectedValueToCompare) + && ! is_int($this->selectedValueToCompare) ) { throw new InvalidArgumentException( get_class($this) . ' : Invalid selected() second $valueToCompare argument. ' diff --git a/src/Components/Form/Traits/TemporalValidityChecks.php b/src/Components/Form/Traits/TemporalValidityChecks.php index 98f0c0d4..bb41410a 100644 --- a/src/Components/Form/Traits/TemporalValidityChecks.php +++ b/src/Components/Form/Traits/TemporalValidityChecks.php @@ -17,9 +17,9 @@ protected function checkValuesValidity(): void protected function checkFormat(): void { - if (! $this->format) { + if (! $this->getFormat()) { throw new RuntimeException(get_class($this) . ' : No config or custom format given for the input' - . ucfirst($this->type) . ' component.'); + . ucfirst($this->getType()) . ' component.'); } } @@ -28,7 +28,7 @@ protected function checkValue(): void try { Carbon::parse(parent::getValue()); } catch (Exception $exception) { - throw new RuntimeException(get_class($this) . ' : The value for the input' . ucfirst($this->type) + throw new RuntimeException(get_class($this) . ' : The value for the input' . ucfirst($this->getType()) . ' component must be a valid DateTime object or a formatted string, « ' . parent::getValue() . ' » given.'); } diff --git a/src/Components/Media/Abstracts/ImageAbstract.php b/src/Components/Media/Abstracts/ImageAbstract.php index e05575e0..da73779e 100644 --- a/src/Components/Media/Abstracts/ImageAbstract.php +++ b/src/Components/Media/Abstracts/ImageAbstract.php @@ -69,40 +69,49 @@ public function linkId(string $linkId): self return $this; } - public function linkClasses(array $linkClasses): self + public function linkClasses(array $linkClasses, bool $mergeMode = false): self { - $this->linkClasses = $linkClasses; + $this->linkClasses = $mergeMode + ? array_merge($this->linkClasses, $linkClasses) + : $linkClasses; return $this; } - public function linkHtmlAttributes(array $linkHtmlAttributes): self + public function linkHtmlAttributes(array $linkHtmlAttributes, bool $mergeMode = false): self { - $this->linkHtmlAttributes = $linkHtmlAttributes; + $this->linkHtmlAttributes = $mergeMode + ? array_merge($this->linkHtmlAttributes, $linkHtmlAttributes) + : $linkHtmlAttributes; return $this; } - protected function getValues(): array + protected function getViewParams(): array { - $alt = $this->getAlt(); - $width = $this->getWidth(); - $height = $this->getHeight(); - $linkId = $this->getLinkId(); - $linkClasses = $this->getLinkClasses(); - $linkUrl = $this->getLinkUrl(); - $linkTitle = $this->getLinkTitle(); - $linkHtmlAttributes = $this->getLinkHtmlAttributes(); - - return array_merge( - parent::getValues(), - compact('alt', 'width', 'height', 'linkId', 'linkClasses', 'linkUrl', 'linkTitle', 'linkHtmlAttributes') - ); + return array_merge(parent::getViewParams(), [ + 'alt' => $this->getAlt(), + 'width' => $this->getWidth(), + 'height' => $this->getHeight(), + 'linkId' => $this->getLinkId(), + 'linkClasses' => $this->getLinkClasses(), + 'linkUrl' => $this->getLinkUrl(), + 'linkTitle' => $this->getLinkTitle(), + 'linkHtmlAttributes' => $this->getLinkHtmlAttributes(), + ]); } protected function getAlt(): ?string { - return $this->alt ?: $this->getLabel() ?: $this->linkTitle; + if ($this->alt) { + return $this->alt; + } + $label = $this->getLabel(); + if ($label) { + return $label; + } + + return $this->linkTitle; } protected function getWidth(): ?int @@ -134,7 +143,15 @@ protected function getLinkUrl(): ?string protected function getLinkTitle(): ?string { - return $this->linkTitle ?: $this->getLabel() ?: $this->alt; + if ($this->linkTitle) { + return $this->linkTitle; + } + $label = $this->getLabel(); + if ($label) { + return $label; + } + + return $this->alt; } protected function getLinkHtmlAttributes(): array diff --git a/src/Components/Media/Abstracts/MediaAbstract.php b/src/Components/Media/Abstracts/MediaAbstract.php index 05009a1e..5901ce82 100644 --- a/src/Components/Media/Abstracts/MediaAbstract.php +++ b/src/Components/Media/Abstracts/MediaAbstract.php @@ -39,13 +39,13 @@ public function caption(?string $caption): self return $this; } - protected function getValues(): array + protected function getViewParams(): array { - $label = $this->getLabel(); - $src = $this->getSrc(); - $caption = $this->getCaption(); - - return array_merge(parent::getValues(), compact('label', 'src', 'caption')); + return array_merge(parent::getViewParams(), [ + 'label' => $this->getLabel(), + 'src' => $this->getSrc(), + 'caption' => $this->getCaption(), + ]); } protected function getLabel(): ?string diff --git a/src/Components/Media/Abstracts/VideoAbstract.php b/src/Components/Media/Abstracts/VideoAbstract.php index 9033cf49..9fa6af83 100644 --- a/src/Components/Media/Abstracts/VideoAbstract.php +++ b/src/Components/Media/Abstracts/VideoAbstract.php @@ -19,11 +19,9 @@ public function poster(string $poster): self return $this; } - protected function getValues(): array + protected function getViewParams(): array { - $poster = $this->getPoster(); - - return array_merge(parent::getValues(), compact('poster')); + return array_merge(parent::getViewParams(), ['poster' => $this->getPoster()]); } protected function getPoster(): ?string diff --git a/tests/Unit/Buttons/Abstracts/ButtonTestAbstract.php b/tests/Unit/Buttons/Abstracts/ButtonTestAbstract.php index 8b6e80f6..4898bc0e 100644 --- a/tests/Unit/Buttons/Abstracts/ButtonTestAbstract.php +++ b/tests/Unit/Buttons/Abstracts/ButtonTestAbstract.php @@ -8,13 +8,13 @@ abstract class ButtonTestAbstract extends SubmitTestAbstract { use RoutesFaker; - public function testType() + public function testType(): void { $html = $this->getComponent()->toHtml(); self::assertStringContainsString('set( 'bootstrap-components.components.' . $this->getComponentKey(), @@ -24,7 +24,7 @@ public function setCustomUrl() self::assertStringContainsString('href="default-url"', $html); } - public function testSetUrlOverridesDefault() + public function testSetUrlReplacesDefault(): void { config()->set( 'bootstrap-components.components.' . $this->getComponentKey(), @@ -36,7 +36,7 @@ public function testSetUrlOverridesDefault() self::assertStringNotContainsString('href="default-url"', $html); } - public function testSetRoute() + public function testSetRoute(): void { $this->setRoutes(); $customRoute = 'users.index'; @@ -44,7 +44,7 @@ public function testSetRoute() self::assertStringContainsString('href="' . route($customRoute) . '"', $html); } - public function testSetCustomLabel() + public function testSetCustomLabel(): void { config()->set( 'bootstrap-components.components.' . $this->getComponentKey(), @@ -55,7 +55,7 @@ public function testSetCustomLabel() self::assertStringContainsString('default-label', $html); } - public function testSetLabelOverridesDefault() + public function testSetLabelReplacesDefault(): void { config()->set( 'bootstrap-components.components.' . $this->getComponentKey(), @@ -69,14 +69,14 @@ public function testSetLabelOverridesDefault() self::assertStringNotContainsString('default-label', $html); } - public function testNoLabel() + public function testNoLabel(): void { $html = $this->getComponent()->label(null)->toHtml(); self::assertStringNotContainsString('title="', $html); self::assertStringNotContainsString('', $html); } - public function testHideLabel() + public function testHideLabel(): void { config()->set( 'bootstrap-components.components.' . $this->getComponentKey(), @@ -87,58 +87,16 @@ public function testHideLabel() self::assertStringNotContainsString('default-label', $html); } - public function testDefaultComponentId() + public function testDefaultComponentId(): void { $html = $this->getComponent()->toHtml(); self::assertStringNotContainsString('set( - 'bootstrap-components.components.' . $this->getComponentKey(), - get_class($this->getCustomComponent()) - ); - $html = $this->getComponent()->toHtml(); - self::assertStringContainsString('class="component-container default container classes"', $html); - } - - public function testSetContainerClassesOverridesDefault() - { - config()->set( - 'bootstrap-components.components.' . $this->getComponentKey(), - get_class($this->getCustomComponent()) - ); - $html = $this->getComponent()->containerClasses(['custom', 'container', 'classes'])->toHtml(); - self::assertStringContainsString('class="component-container custom container classes"', $html); - self::assertStringNotContainsString('class="component-container default container classes"', $html); - } - - public function testSetCustomComponentClasses() - { - config()->set( - 'bootstrap-components.components.' . $this->getComponentKey(), - get_class($this->getCustomComponent()) - ); - $html = $this->getComponent()->toHtml(); - self::assertStringContainsString('class="component btn default component classes"', $html); - } - - public function testSetComponentClassesOverridesDefault() - { - config()->set( - 'bootstrap-components.components.' . $this->getComponentKey(), - get_class($this->getCustomComponent()) - ); - $html = $this->getComponent()->componentClasses(['custom', 'component', 'classes'])->toHtml(); - self::assertStringContainsString('class="component btn custom component classes"', $html); - self::assertStringNotContainsString('class="component btn default component classes"', $html); - } } diff --git a/tests/Unit/Buttons/Abstracts/SubmitTestAbstract.php b/tests/Unit/Buttons/Abstracts/SubmitTestAbstract.php index 349f7ca5..345913d0 100644 --- a/tests/Unit/Buttons/Abstracts/SubmitTestAbstract.php +++ b/tests/Unit/Buttons/Abstracts/SubmitTestAbstract.php @@ -18,28 +18,28 @@ abstract protected function getComponentType(): string; abstract protected function getCustomComponent(): ComponentAbstract; - public function testHelper() + public function testHelper(): void { self::assertInstanceOf(get_class($this->getComponent()), $this->getHelper()); } - public function testFacade() + public function testFacade(): void { self::assertInstanceOf(get_class($this->getComponent()), $this->getFacade()); } - public function testInstance() + public function testInstance(): void { self::assertInstanceOf(SubmitAbstract::class, $this->getComponent()); } - public function testType() + public function testType(): void { $html = $this->getComponent()->toHtml(); self::assertStringContainsString('