diff --git a/.github/workflows/alert.yml b/.github/workflows/alert.yml index a5f2bc58..7667504b 100644 --- a/.github/workflows/alert.yml +++ b/.github/workflows/alert.yml @@ -1,35 +1,39 @@ -name: Laravel +name: Testing on: push: - branches: [ "master" ] + branches: [ "master", "feature/laravel-10" ] pull_request: branches: [ "master" ] +permissions: + contents: read + jobs: - laravel-tests: + build: runs-on: ubuntu-latest steps: - - uses: shivammathur/setup-php@15c43e89cdef867065b0213be354c2841860869e - with: - php-version: '8.0' - uses: actions/checkout@v3 - - name: Copy .env - run: php -r "file_exists('.env') || copy('.env.example', '.env');" - - name: Install Dependencies - run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - - name: Generate key - run: php artisan key:generate - - name: Directory Permissions - run: chmod -R 777 storage bootstrap/cache - - name: Create Database - run: | - mkdir -p database - touch database/database.sqlite - - name: Execute tests (Unit and Feature tests) via PHPUnit + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Execute tests env: DB_CONNECTION: sqlite DB_DATABASE: database/database.sqlite - run: vendor/bin/phpunit + run: php vendor/bin/testbench package:test diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..18c91471 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/README.md b/README.md index a878083b..c649f1dd 100644 --- a/README.md +++ b/README.md @@ -1,206 +1,126 @@ -# Alert +

+ +

-[![Join the chat at https://gitter.im/digitlimit/alert](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/digitlimit/alert?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +> Version 2.0 -Alert is designed to make flashing messages in Laravel Applications a breeze. -Tested and works in Laravel 5 or less +Alert is Laravel package for displaying different types of messages in Laravel application views. +It's designed to make flashing messages in Laravel Applications a breeze, with a lot of easy to use and fluent methods. -### For this to work you need to include Twitter Bootstrap in your page +## Quick Start -## Installation +1. Install Alert with composer: -Add alert in your composer.json file: - -```php -"require": { - "digitlimit/alert": "v1.0" -} ``` - -Then run -```command -composer update +composer require digitlimit/alert ``` -In Laravel 5 include Alert Service Provider within config/app.php or app/config/app.php in Laravel 4 +2. Somewhere in the blade template -```php -'providers' => [ - 'Digitlimit\Alert\AlertServiceProvider' -]; +``` + ``` -You can also include alert facade in aliases array in same file above i.e config/app.php or app/config/app.php in Laravel 4 +Example: -```php -'aliases' => [ - 'Alert' => 'Digitlimit\Alert\Facades\Alert' -]; ``` +@extends('layouts.default') + +@section('content') +
+
+
+ +
+
-## Usage +
+ @include('form.profile') +
+
-In your controller simply set your alert before redirection like so: + @include('partials.footer') +@endsection +``` -In laravel 5 controller for example: +NB: At the moment the alert components are built with Twitter Bootstrap 5, and can be customized to use other CSS classes. +Need to ensure bootstrap is included on the page. -```php -success() - ->closable(); - - return redirect()->route('users.getRegister'); - } -} +namespace App\Http\Controllers; +use Alert; -class LoginController extends Controller +class DashboardController extends Controller { - public function postLogin(Request $request) + public function index() { - /*run validation on request parameters*/ - $validator = validator($request->only(['email','password']), [ - 'email' => 'required|email', - 'password' => 'required|string', - ]); - - /*return errors to view if any*/ - if($validator->fails()){ - - Alert::form('Some errors occured','Opps') - ->error(); - - return redirect() - ->back() - ->withErrors($validator); - } + Alert::message('Welcome! Please complete your profile') + ->info() + ->flash(); + + return view('home'); } } ``` -Then in your view your can include the flash message to your view like so: - -```html -
- - @include('alert::form') - -
-
- - -
-
- - -
-
- - -
- -
-
-``` -##### Form Alert Example -![alert](https://user-images.githubusercontent.com/2041419/53573721-5fba5880-3b6e-11e9-9fb4-b13f245e7b90.JPG) - - -## Features -There are basically three types of alert messages you can flash - -#### Form - used mostly to display message in the header of your form - -Some where in the controller: -```pph -//This simply displays a success message -Alert::form('Your account was successfully created','Congratulations')->success(); -``` -Some where in the view: -```html -@include('alert::form') -``` +4. Result -#### Notify - used mostly on the header of your page +image -Some where in the controller: -```pph -//This simply displays a success message -Alert::notify('Your account is going to expire today.','Info')->info(); -``` -Some where in the view layout where you want the alert to appear: -```html -@include('alert::notify') -``` -#### Modal - used mostly to display an overlay message +## Documentation -Some where in the controller: -```php -//This simply displays a success message -Alert::modal('Thanks for joining us.','Title')->info(); -``` -Some where in the view layout: -```html -@include('alert::modal') -``` +Learn how to get started with Alert and then dive deeper into other and advanced topics: -#### Customize alert views -To customized various alert views run artisan command below -``` - php artisan vendor:publish --provider="Digitlimit\Alert\AlertServiceProvider" -``` +[Complete documentation](https://github.com/digitlimit/alert/wiki) -This will publish `form.blade.php`, `modal.blade.php`, `notify.blade.php` and `sticky.blade.php` -to publish views to `resources/views/vendor/alert` directory. +## Change log -The following helpers can be used in blade templates +Coming soon -**Alert Icon** -This displays alert icon class e.g fa fa-cog -Example: -`` +## Code of conduct -**Alert Status** -This displays alert status -Example: -`
` +We will behave ourselves if you behave yourselves. For more details see our +[CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md). -**Alert Title** -This displays alert title -Example: -`

{{Alert::title()}}

` +## Contributing -**Alert Message** -This displays alert message -Example: -`

{{Alert::message()}}

` +Please read through our [contributing guidelines](./CONTRIBUTING.md). Included +are directions for opening issues. -**Form alert customization example** +## Versioning -``` -@if(Alert::has('form')) - -@endif -``` +## License + +Alert Free is free, open source, and GPL friendly. You can use it for +commercial projects, open source projects, or really almost whatever you want. + +- Code — MIT License + - In the Alert Free download, the MIT license applies all PHP files. +We've kept attribution comments terse, so we ask that you do not actively work +to remove them from files, especially code. They're a great way for folks to +learn about Alert. diff --git a/TODO b/TODO new file mode 100644 index 00000000..ac169bc9 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ + +Todo: + ☐ composer require "digitlimit/alert":"dev-feature/laravel-10" \ No newline at end of file diff --git a/composer.json b/composer.json index df6749c8..e198c81d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,15 @@ { "name": "digitlimit/alert", - "description": "An easy way of flashing messages in Laravel Application ", + "description": "An easy way of flashing messages in Laravel Application", + "license": "MIT", + "authors": [ + { + "name": "Emeka Mbah", + "email": "frankemeks77@yahoo.com", + "homepage": "https://emekambah.medium.com" + } + ], + "homepage": "https://github.com/digitlimit/alert", "keywords": [ "laravel", "views", @@ -8,25 +17,51 @@ "messages", "withErrors" ], - "license": "MIT", - "authors": [ - { - "name": "Mbah Emeka", - "email": "frankemeks77@yahoo.com" - } - ], "require": { - "php": ">=5.4.0", - "illuminate/support": "~5.0|^6.0" + "ext-json": "*", + "php": "^7.4|^8.0", + "illuminate/contracts": "^7.20|^8.19|^9.0|^10.0", + "illuminate/support": "^7.20|^8.19|^9.0|^10.0" }, "require-dev": { - "mockery/mockery": "dev-master", - "phpunit/phpunit": "^9.0" + "guzzlehttp/guzzle": "^7.3", + "laravel/framework": "^7.20|^8.19|^9.0|^10.0", + "phpunit/phpunit": "^10.0", + "orchestra/testbench": "^8.5", + "nunomaduro/collision": "^7.5", + "pestphp/pest": "^2.5" }, "autoload": { - "psr-0": { - "Digitlimit\\Alert": "src/" + "psr-4": { + "Digitlimit\\Alert\\": "src/" + }, + "files": [ + "src/Helpers.php" + ] + }, + "autoload-dev": { + "psr-4": { + "Digitlimit\\Alert\\Tests\\": "tests" } }, - "minimum-stability": "stable" + "extra": { + "laravel": { + "providers": [ + "Digitlimit\\Alert\\AlertServiceProvider" + ], + "aliases": { + "Alert": "Digitlimit\\Alert\\Facades\\Alert" + } + } + }, + "scripts": { + "post-autoload-dump": [ + "@php vendor/bin/testbench package:discover --ansi" + ] + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + } } diff --git a/config/alert.php b/config/alert.php new file mode 100644 index 00000000..03a89901 --- /dev/null +++ b/config/alert.php @@ -0,0 +1,40 @@ + [ + 'alert-normal' => [ + 'alert' => \Digitlimit\Alert\Types\Normal::class, + 'component' => \Digitlimit\Alert\View\Components\Normal::class, + ], + + 'alert-field' => [ + 'alert' => \Digitlimit\Alert\Types\Field::class, + 'component' => \Digitlimit\Alert\View\Components\Field::class, + ], + + 'alert-modal' => [ + 'alert' => \Digitlimit\Alert\Types\Modal::class, + 'component' => \Digitlimit\Alert\View\Components\Modal::class, + ], + + 'alert-notify' => [ + 'alert' => \Digitlimit\Alert\Types\Notify::class, + 'component' => \Digitlimit\Alert\View\Components\Notify::class, + ], + + 'alert-sticky' => [ + 'alert' => \Digitlimit\Alert\Types\Sticky::class, + 'component' => \Digitlimit\Alert\View\Components\Sticky::class, + ], + ], +]; diff --git a/phpunit.xml b/phpunit.xml index 3347b75b..01bf269d 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,18 +1,21 @@ - - - - ./tests/ - - + + + + + ./tests/Unit + + + ./tests/Feature + + + + + + + + + src/ + + diff --git a/resources/views/components/field.blade.php b/resources/views/components/field.blade.php new file mode 100644 index 00000000..1126fa64 --- /dev/null +++ b/resources/views/components/field.blade.php @@ -0,0 +1,35 @@ +@php + $name = $attributes->get('name', ''); + $tag = $attributes->get('tag', $defaultTag); + $field = $alert->named('field', $name, $tag) ?? $alert->tagged('field', $tag); +@endphp + +@if($slot->isNotEmpty()) + {{ $slot }} +@elseif($field) + @if(($name && $field->name == $name) || empty($name)) +
merge(['class' => 'form-text text-'.$field->level]) }}> + {{ $error ?? $field->message }} +
+ @elseif($field->messageFor($name)) +
merge(['class' => 'form-text text-'.$field->level]) }}> + {{ $field->messageFor($name) }} +
+ @endif +@elseif(isset($errors)) + @php + $level = $field->level ?? 'danger'; + @endphp + + @error($name, $tag) +
merge(['class' => 'form-text text-'.$level ]) }}> + {{ $errors->$tag->first($name) }} +
+ @enderror + + @error($name) +
merge(['class' => 'form-text text-'.$level ]) }}> + {{ $errors->first($name) }} +
+ @enderror +@endif \ No newline at end of file diff --git a/resources/views/components/modal.blade.php b/resources/views/components/modal.blade.php new file mode 100644 index 00000000..424ba96b --- /dev/null +++ b/resources/views/components/modal.blade.php @@ -0,0 +1,126 @@ +@php + $tag = $attributes->get('tag', $defaultTag); + $id = $attributes->get('id'); + $modal = $alert->tagged('modal', $tag); + + $cancel = $modal->cancel ?? ''; + $action = $modal->action ?? ''; + $view = $modal->view ?? ''; +@endphp + +@props([ + 'header', + 'body', + 'footer' +]) + +@if($modal) + @php + $id = $id ?? $modal->id; + $hasBody = isset($body) && $body->isNotEmpty(); + $hasHeader = isset($header) && $header->isNotEmpty(); + $hasFooter = isset($footer) && $footer->isNotEmpty(); + $hasTitle = $hasHeader || $modal->title; + @endphp +
merge(['id' => $id]) }} + {{ $attributes->merge(['class' => 'modal']) }} + {{ $attributes->merge(['tabindex' => '-1']) }} + > + +
+ +@endif \ No newline at end of file diff --git a/resources/views/components/normal.blade.php b/resources/views/components/normal.blade.php new file mode 100644 index 00000000..aeba864b --- /dev/null +++ b/resources/views/components/normal.blade.php @@ -0,0 +1,18 @@ +@php + $id = $attributes->get('id'); + $tag = $attributes->get('tag', $defaultTag); + $normal = $alert->tagged('normal', $tag); +@endphp +@if($normal) + @php + $id = $id ?? $normal->id; + @endphp +
merge(['class' => 'alert alert-dismissible alert-'.$normal->level]) }} role="alert"> + @if ($slot->isNotEmpty()) + {{ $slot }} + @else + @if($normal->title){{ $normal->title }}@endif {{ $normal->message }} + + @endif +
+@endif \ No newline at end of file diff --git a/resources/views/components/notify.blade.php b/resources/views/components/notify.blade.php new file mode 100644 index 00000000..a7e7bd5e --- /dev/null +++ b/resources/views/components/notify.blade.php @@ -0,0 +1,71 @@ +@php + $tag = $attributes->get('tag', $defaultTag); + $id = $attributes->get('id'); + $notify = $alert->tagged('notify', $tag); + $view = $notify->view ?? ''; + $position = $notify->position ?? 'bottom-0 end-0'; +@endphp +@if($notify) + @php + $id = $id ?? $notify->id; + $hasBody = isset($body) && $body->isNotEmpty(); + $hasHeader = isset($header) && $header->isNotEmpty(); + $hasTitle = $hasHeader || $notify->title; + @endphp +
merge(['class' => 'position-fixed p-3 ' . $position]) }} + {{ $attributes->merge(['z-index' => '100']) }} + > + +
+ +@endif \ No newline at end of file diff --git a/resources/views/components/sticky.blade.php b/resources/views/components/sticky.blade.php new file mode 100644 index 00000000..e90d7cb7 --- /dev/null +++ b/resources/views/components/sticky.blade.php @@ -0,0 +1,32 @@ +@php + $id = $attributes->get('id'); + $tag = $attributes->get('tag', $defaultTag); + $sticky = $alert->tagged('sticky', $tag); + $action = $sticky->action ?? ''; +@endphp +@if($sticky) + @php + $id = $id ?? $sticky->id; + @endphp + +
merge(['class' => 'alert alert-dismissible alert-'.$sticky->level]) }} role="alert"> + @if ($slot->isNotEmpty()) + {{ $slot }} + @else + @if($sticky->title){{ $sticky->title }}@endif {{ $sticky->message }} + + @if($action->label) + @if($action->link) + attributes) !!}> + {{ $action->label }} + + @else + + @endif + @endif + + @endif +
+@endif \ No newline at end of file diff --git a/src/Alert.php b/src/Alert.php new file mode 100644 index 00000000..2275a155 --- /dev/null +++ b/src/Alert.php @@ -0,0 +1,147 @@ +session->get( + SessionKey::key($type, $tag) + ); + } + + /** + * Fetch an alert based on the tag name. + */ + public function tagged( + string $type, + string $tag + ): MessageInterface|null { + if (!Type::exists($type)) { + throw new Exception("Invalid alert type '$type'. Check the alert config"); + } + + $tagged = $this->session->get( + SessionKey::key($type, $tag) + ); + + if (is_array($tagged)) { + return null; + } + + return $tagged; + } + + /** + * Fetch the normal alert. + */ + public function normal(string $message = null): MessageInterface + { + return MessageFactory::make($this->session, 'normal', $message); + } + + /** + * Fetch the field alert. + */ + public function field( + string $message = null, + ?Validator $validator = null + ): MessageInterface { + return MessageFactory::make( + $this->session, + 'field', + $message, + $validator + ); + } + + /** + * Fetch the modal alert. + */ + public function modal(string $message = null): MessageInterface + { + return MessageFactory::make($this->session, 'modal', $message); + } + + /** + * Fetch the notify alert. + */ + public function notify(string $message = null): MessageInterface + { + return MessageFactory::make($this->session, 'notify', $message); + } + + /** + * Fetch the sticky alert. + */ + public function sticky(string $message = null): MessageInterface + { + return MessageFactory::make($this->session, 'sticky', $message); + } + + /** + * Fetch the default alert type, which is the normal alert. + */ + public function message(string $message): MessageInterface + { + return $this->normal($message); + } + + /** + * Fetch an alert from the given alert type. + */ + public function from( + string $type, + string $message = null, + ...$args + ): MessageInterface { + if (!Type::exists($type)) { + throw new Exception("Invalid alert type '$type'. Check the alert config"); + } + + return MessageFactory::make($this->session, $type, $message, ...$args); + } +} diff --git a/src/AlertServiceProvider.php b/src/AlertServiceProvider.php new file mode 100644 index 00000000..266094ae --- /dev/null +++ b/src/AlertServiceProvider.php @@ -0,0 +1,75 @@ +loadViewsFrom(__DIR__.'/../resources/views', 'alert'); + + $this->registerComponents(); + + $this->bootForConsole(); + } + + /** + * Register any package services. + */ + public function register(): void + { + $this->app->bind(SessionInterface::class, Session::class); + $this->app->bind(ConfigInterface::class, Config::class); + + $this->app->singleton('alert', function ($app) { + return $app->make(Alert::class); + }); + + $this->mergeConfigFrom( + __DIR__.'/../config/alert.php', + 'alert' + ); + } + + /** + * Get the services provided by the provider. + */ + public function provides(): array + { + return ['alert']; + } + + /** + * Console-specific booting. + */ + protected function bootForConsole(): void + { + $this->publishes([ + __DIR__.'/../resources/views' => base_path('resources/views/vendor/digitlimit/alert'), + ], 'alert.views'); + + $this->publishes([ + __DIR__.'/../config/alert.php' => config_path('alert.php'), + ], 'alert.config'); + } + + /** + * Register alert components. + */ + protected function registerComponents(): void + { + Blade::componentNamespace('Digitlimit\\Alert\View\\Components', 'alert'); + + $types = config('alert.types'); + + foreach ($types as $name => $type) { + Blade::component($name, $type['component']); + } + } +} diff --git a/src/Component/Button.php b/src/Component/Button.php new file mode 100644 index 00000000..7b3ea6c2 --- /dev/null +++ b/src/Component/Button.php @@ -0,0 +1,42 @@ +label = $label; + } + + /** + * Set the button link. + */ + public function link(string $link): void + { + $this->link = $link; + } + + /** + * Set the button attributes. + */ + public function attributes(array $attributes): void + { + $this->attributes = $attributes; + } +} diff --git a/src/Config.php b/src/Config.php new file mode 100644 index 00000000..3a40a23e --- /dev/null +++ b/src/Config.php @@ -0,0 +1,26 @@ +config = $config; + } + + /** + * Fetch value from the config based on the given key. + */ + public function get(string $key, mixed $default = null): mixed + { + return $this->config->get($key, $default); + } +} diff --git a/src/ConfigInterface.php b/src/ConfigInterface.php new file mode 100644 index 00000000..472628a4 --- /dev/null +++ b/src/ConfigInterface.php @@ -0,0 +1,11 @@ +session = $session; - } - - protected function flash($type = 'flash') - { - $this->alert = [ - $this->type => true, - 'type' => $this->type, - 'title' => $this->title, - 'message' => $this->message, - 'status' => $this->status, - 'icon' => $this->icon, - - 'closable' => $this->closable, - 'un_closable' => $this->un_closable, - 'un_closable_strict' => $this->un_closable_strict, - 'self_destroy' => $this->self_destroy, - 'persist' => $this->persist, - - 'sticky_title' => $this->sticky_title, - 'sticky_message' => $this->sticky_message, - - 'modal_size' => $this->modal_size, - 'modal_view' => $this->modal_view, - - 'action_button_label' => $this->action_button_label, - 'action_button_url' => $this->action_button_url, - 'close_button_label' => $this->close_button_label, - 'close_button_url' => $this->close_button_url, - 'close_button_attributes' => $this->close_button_attributes, - - 'tag' => $this->tag, - ]; - - $this->session->$type('alert_message', $this->alert); - - return $this; - } - - protected function alert() - { - return $this->session->get('alert_message'); - } - - public function __call($method, $param) - { - $alert = $this->alert(); - if (is_array($alert) && property_exists($this, $method) && isset($alert[$method])) { - return $alert[$method]; - } - } - - //Actions - public function persist() - { - return $this->flash($type = 'put'); - } - - public function destroy() - { - return $this->flash($type = 'pull'); //retrieve an item and forget it - } - - public function unClosable() - { - $this->closable = false; - $this->un_closable = true; - - return $this->flash(); - } - - public function unClosableStrict() - { - $this->closable = false; - $this->un_closable = true; - $this->un_closable_strict = true; - - return $this->flash(); - } - - public function selfDestroy() - { - $this->self_destroy = true; - - return $this->flash(); - } - - public function setIcon($icon = '') - { - $this->icon = $icon; - - return $this->flash(); - } - - public function setActionButton($label = '', $url = '') - { - $this->action_button_label = $label; - $this->action_button_url = $url; - - return $this->flash(); - } - - public function setCloseButton($label = '', $url = '', $attributes = []) - { - $this->close_button_label = $label; - $this->close_button_url = $url; - $this->close_button_attributes = $attributes; - - return $this->flash(); - } - - public function large() - { - $this->modal_size = 'modal-lg'; - - return $this->flash(); - } - - public function small() - { - $this->modal_size = 'modal-sm'; - - return $this->flash(); - } - - //Alert status - public function success($class_name = 'success') - { - $this->status = $class_name; - $this->icon = 'fa fa-check-circle'; - - return $this->flash(); - } - - public function info($class_name = 'info') - { - $this->status = $class_name; - $this->icon = 'fa fa-info'; - - return $this->flash(); - } - - public function warning($class_name = 'warning') - { - $this->status = $class_name; - $this->icon = 'fa fa-warning'; - - return $this->flash(); - } - - public function error($class_name = 'danger') - { - $this->status = $class_name; - $this->icon = 'fa fa-times-circle'; - - return $this->flash(); - } - - public function royal($class_name = 'royal') - { - $this->status = $class_name; - $this->icon = 'fa fa-bullhorn'; - - return $this->flash(); - } - - public function primary($class_name = 'primary') - { - $this->status = $class_name; - $this->icon = 'fa fa-comments-o'; - - return $this->flash(); - } - - //Alert Types - public function modal($message, $title = '', $view = '') - { - $this->type = 'alert_modal_message'; - $this->title = $title; - $this->message = $message; - $this->modal_view = $view; - - return $this->flash(); - } - - public function form($message, $title = '') - { - $this->type = 'alert_form_message'; - $this->title = $title; - $this->message = $message; - - return $this->flash(); - } - - public function notify($message, $title = '') - { - $this->type = 'alert_notify_message'; - $this->title = $title; - $this->message = $message; - - return $this->flash(); - } - - public function sticky($message, $title = '') - { - $this->type = 'alert_sticky_message'; - $this->sticky_title = $title; - $this->sticky_message = $message; - - return $this->flash(); - } - - /** - * Check if given alert exists. - * - * @param $alert_type - * - * @return bool - */ - public function has($alert_type) - { - $alert = $this->alert(); - - $alert_type = "alert_{$alert_type}_message"; - - return is_array($alert) ? isset($alert[$alert_type]) : false; - } - - public function tag($name) - { - $this->tag = $name; - - return $this->flash(); - } - - /** - * Tag an alert. - * - * @param $name - * - * @return bool - */ - public function tagged($name) - { - $alert = $this->alert(); - - return $alert['tag'] == $name; - } - - /** - * Check if alert for success. - * - * @return bool - */ - public function hasSuccess() - { - return $this->message && $this->status == 'success'; - } - - /** - * Check if alert for error. - * - * @return bool - */ - public function hasError() - { - return $this->message && $this->status == 'error'; - } - - /** - * Check if alert for info. - * - * @return bool - */ - public function hasInfo() - { - return $this->message && $this->status == 'info'; - } -} diff --git a/src/Digitlimit/Alert/AlertServiceProvider.php b/src/Digitlimit/Alert/AlertServiceProvider.php deleted file mode 100644 index 6ca3d138..00000000 --- a/src/Digitlimit/Alert/AlertServiceProvider.php +++ /dev/null @@ -1,73 +0,0 @@ -app->singleton('digitlimit.alert', function ($app) { - return new Alert($app['session.store']); - }); - } - - /** - * Bootstrap the application events. - * - * @return void - */ - public function boot() - { - $this->loadViewsFrom(__DIR__.'/views', 'alert'); - $this->publishes([ - __DIR__.'/views' => base_path('resources/views/vendor/alert'), - ]); - - Blade::directive('alertHasSuccess', function () { - return ""; - }); - - Blade::directive('endAlertHasSuccess', function () { - return ''; - }); - - Blade::directive('alertHasNoSuccess', function () { - return ""; - }); - - Blade::directive('endAlertHasNoSuccess', function () { - return ''; - }); - - Blade::directive('alertHasError', function () { - return ""; - }); - - Blade::directive('endAlertHasError', function () { - return ''; - }); - - Blade::directive('alertHasNoError', function () { - return ""; - }); - - Blade::directive('endAlertHasNoError', function () { - return ''; - }); - } -} diff --git a/src/Digitlimit/Alert/views/field.blade.php b/src/Digitlimit/Alert/views/field.blade.php deleted file mode 100644 index 396c8241..00000000 --- a/src/Digitlimit/Alert/views/field.blade.php +++ /dev/null @@ -1,9 +0,0 @@ -@if($errors->has($field)) -

- @if(isset($tag) && $tag) - {{ $errors->${$tag}->first($field) }} - @else - {{ $errors->first($field) }} - @endif -

-@endif \ No newline at end of file diff --git a/src/Digitlimit/Alert/views/form.blade.php b/src/Digitlimit/Alert/views/form.blade.php deleted file mode 100644 index 7b682311..00000000 --- a/src/Digitlimit/Alert/views/form.blade.php +++ /dev/null @@ -1,14 +0,0 @@ -@if(Alert::has('form')) - -@endif diff --git a/src/Digitlimit/Alert/views/modal.blade.php b/src/Digitlimit/Alert/views/modal.blade.php deleted file mode 100644 index a9465e9c..00000000 --- a/src/Digitlimit/Alert/views/modal.blade.php +++ /dev/null @@ -1,44 +0,0 @@ -@if ( Alert::has('modal') ) - - - - -@endif \ No newline at end of file diff --git a/src/Digitlimit/Alert/views/notify.blade.php b/src/Digitlimit/Alert/views/notify.blade.php deleted file mode 100644 index ccebecc1..00000000 --- a/src/Digitlimit/Alert/views/notify.blade.php +++ /dev/null @@ -1,11 +0,0 @@ -@if(Alert::has('notify')) - -@endif \ No newline at end of file diff --git a/src/Digitlimit/Alert/views/sticky.blade.php b/src/Digitlimit/Alert/views/sticky.blade.php deleted file mode 100644 index 85076de7..00000000 --- a/src/Digitlimit/Alert/views/sticky.blade.php +++ /dev/null @@ -1,18 +0,0 @@ -@if (Alert::has('sticky')) -
- - @if(Alert::closable()) - - @endif - - @if(Alert::title()) - - @if(Alert::icon())@endif - {{Alert::title()}} - - @endif - - {!!Alert::sticky_message()!!} - -
-@endif \ No newline at end of file diff --git a/src/Digitlimit/Alert/Facades/Alert.php b/src/Facades/Alert.php similarity index 56% rename from src/Digitlimit/Alert/Facades/Alert.php rename to src/Facades/Alert.php index 9c5b1c0d..bd65eb84 100644 --- a/src/Digitlimit/Alert/Facades/Alert.php +++ b/src/Facades/Alert.php @@ -7,12 +7,12 @@ class Alert extends Facade { /** - * Get the binding in the IoC container. + * Get the registered name of the component. * * @return string */ - protected static function getFacadeAccessor() + protected static function getFacadeAccessor(): string { - return 'digitlimit.alert'; + return 'alert'; } } diff --git a/src/Helpers.php b/src/Helpers.php new file mode 100644 index 00000000..84b57926 --- /dev/null +++ b/src/Helpers.php @@ -0,0 +1,20 @@ +message($message) + ->title($title) + ->flash(); + } + + return $alert; + } +} diff --git a/src/Helpers/Attribute.php b/src/Helpers/Attribute.php new file mode 100644 index 00000000..29d85d0c --- /dev/null +++ b/src/Helpers/Attribute.php @@ -0,0 +1,21 @@ +get('alert.types'); + } + + /** + * Get the prefixed type for an alert type. + */ + public static function prefixed(string $type): string + { + return self::PREFIX.$type; + } + + /** + * Check if an alert type exists. + */ + public static function exists(string $type): bool + { + $types = self::types(); + + return isset($types[$type]) || isset($types[self::prefixed($type)]); + } + + /** + * Get an alert type. + */ + public static function type(string $type): array + { + $types = self::types(); + + return $types[$type] ?? $types[self::prefixed($type)]; + } + + /** + * Get alert class name. + */ + public static function clasname(string $type): string + { + if (!self::exists($type)) { + throw new Exception("The alert type '$type' does not exist in config"); + } + + return self::type($type)['alert']; + } +} diff --git a/src/Message/AbstractMessage.php b/src/Message/AbstractMessage.php new file mode 100644 index 00000000..ae2b7e1f --- /dev/null +++ b/src/Message/AbstractMessage.php @@ -0,0 +1,122 @@ +id = $id; + + return $this; + } + + /** + * Set alert level. + */ + public function level(string $level): self + { + $this->level = $level; + + return $this; + } + + /** + * Set alert message. + */ + public function message(string $message): self + { + $this->message = $message; + + return $this; + } + + /** + * Set the alert title. + */ + public function title(string $title): self + { + $this->title = $title; + + return $this; + } + + /** + * Set the alert tag. + */ + public function tag(string $tag): self + { + $this->tag = $tag; + + return $this; + } + + /** + * Fetch the alert tag. + */ + public function getTag(): string + { + return $this->tag; + } + + /** + * Flash alert to store. + * Its a temporal store that is deleted once pulled/fetched. + */ + public function flash(string $message = null, string $level = null): void + { + $this->message = $message ?? $this->message; + $this->level = $level ?? $this->level; + + $sessionKey = SessionKey::key($this->key(), $this->getTag()); + $this->session->flash($sessionKey, $this); + } +} diff --git a/src/Message/MessageFactory.php b/src/Message/MessageFactory.php new file mode 100644 index 00000000..17123115 --- /dev/null +++ b/src/Message/MessageFactory.php @@ -0,0 +1,24 @@ +store = $store; + } + + /** + * Flash alert to store. + */ + public function flash(string $key, mixed $value): void + { + $this->store->flash($key, $value); + } + + /** + * Fetch alert from the store. + */ + public function get(string $key, mixed $default = null): mixed + { + return $this->store->get($key, $default); + } +} diff --git a/src/SessionInterface.php b/src/SessionInterface.php new file mode 100644 index 00000000..2c84cb06 --- /dev/null +++ b/src/SessionInterface.php @@ -0,0 +1,16 @@ +level = 'primary'; + + return $this; + } + + /** + * Secondary alert level. + */ + public function secondary(): self + { + $this->level = 'secondary'; + + return $this; + } + + /** + * Success alert level. + */ + public function success(): self + { + $this->level = 'success'; + + return $this; + } + + /** + * Info alert level. + */ + public function info(): self + { + $this->level = 'info'; + + return $this; + } + + /** + * Error alert level. + */ + public function error(): self + { + $this->level = 'danger'; + + return $this; + } + + /** + * Warning alert level. + */ + public function warning(): self + { + $this->level = 'warning'; + + return $this; + } + + /** + * Light alert level. + */ + public function light(): self + { + $this->level = 'light'; + + return $this; + } + + /** + * Dark alert level. + */ + public function dark(): self + { + $this->level = 'dark'; + + return $this; + } +} diff --git a/src/Types/Field.php b/src/Types/Field.php new file mode 100644 index 00000000..fc0ee84e --- /dev/null +++ b/src/Types/Field.php @@ -0,0 +1,115 @@ +id(Helper::randomString()); + $this->messages = new MessageBag(); + } + + /** + * Message store key for the field alert. + */ + public function key(): string + { + return 'field'; + } + + /** + * Set field name. + */ + public function name(string $name): self + { + $this->name = $name; + + return $this; + } + + /** + * Set messages. + */ + public function messages(MessageBag $messages): self + { + $this->messages = $messages; + + return $this; + } + + /** + * Set errors. + */ + public function errors(Validator $validator): self + { + $this->messages = $validator->errors(); + + return $this; + } + + /** + * Fetch message for a given field name. + */ + public function messageFor(string $name): string + { + return $this->messages->first($name); + } + + /** + * Get field store tag. + */ + public function getTag(): string + { + if ($this->name) { + //e.g default.firstname + return $this->tag.'.'.$this->name; + } + + return $this->tag; + } + + /** + * Flash field instance to store. + */ + public function flash(string $message = null, string $level = null): void + { + $this->message = $message ?? $this->message; + $this->level = $level ?? $this->level; + + if ($this->messages->isEmpty() && empty($this->name)) { + throw new Exception( + 'Messages or field name is required. Hint: messages($validator) or name($name)' + ); + } + + $sessionKey = SessionKey::key($this->key(), $this->getTag()); + $this->session->flash($sessionKey, $this); + } +} diff --git a/src/Types/Modal.php b/src/Types/Modal.php new file mode 100644 index 00000000..f4757a0b --- /dev/null +++ b/src/Types/Modal.php @@ -0,0 +1,166 @@ +id(Helper::randomString()); + $this->action = new Button(); + $this->cancel = new Button(); + } + + /** + * Message store key for the modal alert. + */ + public function key(): string + { + return 'modal'; + } + + /** + * Set the action button. + */ + public function action(string $label, string $link = null, array $attributes = []): self + { + $this->action = new Button($label, $link, $attributes); + + return $this; + } + + /** + * Set the cancel button. + */ + public function cancel(string $label, string $link = null, array $attributes = []): self + { + $this->cancel = new Button($label, $link, $attributes); + + return $this; + } + + /** + * Set modal to scrollable. + */ + public function scrollable( + string $class = 'modal-dialog-scrollable' + ): self { + $this->scrollable = $class; + + return $this; + } + + /** + * Set modal size to small. + */ + public function small(string $class = 'modal-sm'): self + { + $this->size = $class; + + return $this; + } + + /** + * Set modal size to large. + */ + public function large(string $class = 'modal-lg'): self + { + $this->size = $class; + + return $this; + } + + /** + * Set modal size to extra-large. + */ + public function extraLarge(string $class = 'modal-xl'): self + { + $this->size = $class; + + return $this; + } + + /** + * Set modal size to fullscreen. + */ + public function fullscreen(string $class = 'modal-fullscreen'): self + { + $this->size = $class; + + return $this; + } + + /** + * Set modal position to center. + */ + public function centered(string $class = 'modal-dialog-centered'): self + { + $this->position = $class; + + return $this; + } + + /** + * Set a view for the modal. + */ + public function view(View $view): self + { + $this->view = $view->render(); + + return $this; + } + + /** + * Set HTML string for the modal. + */ + public function html(string $html): self + { + $this->view = $html; + + return $this; + } +} diff --git a/src/Types/Normal.php b/src/Types/Normal.php new file mode 100644 index 00000000..1be09a54 --- /dev/null +++ b/src/Types/Normal.php @@ -0,0 +1,31 @@ +id(Helper::randomString()); + } + + /** + * Message store key for the normal alert. + */ + public function key(): string + { + return 'normal'; + } +} diff --git a/src/Types/Notify.php b/src/Types/Notify.php new file mode 100644 index 00000000..1b61a23f --- /dev/null +++ b/src/Types/Notify.php @@ -0,0 +1,107 @@ +id(Helper::randomString()); + $this->bottomRight(); + } + + /** + * Message store key for the notify alert. + */ + public function key(): string + { + return 'notify'; + } + + /** + * Position notify on center of the screen. + */ + public function centered(string $class = 'top-50 start-50 translate-middle'): self + { + $this->position = $class; + + return $this; + } + + /** + * Position notify on the center of the screen. + */ + public function topLeft(string $class = 'top-0 start-0'): self + { + $this->position = $class; + + return $this; + } + + /** + * Position notify on the top right of the screen. + */ + public function topRight(string $class = 'top-0 end-0'): self + { + $this->position = $class; + + return $this; + } + + /** + * Position notify on the top center of the screen. + */ + public function topCenter(string $class = 'top-0 start-50 translate-middle-x'): self + { + $this->position = $class; + + return $this; + } + + /** + * Position notify on the bottom left of the screen. + */ + public function bottomLeft(string $class = 'bottom-0 start-0'): self + { + $this->position = $class; + + return $this; + } + + /** + * Position notify on the bottom right of the screen. + */ + public function bottomRight(string $class = 'bottom-0 end-0'): self + { + $this->position = $class; + + return $this; + } + + /** + * Position notify on the bottom center of the screen. + */ + public function bottomCenter(string $class = 'bottom-0 start-50 translate-middle-x'): self + { + $this->position = $class; + + return $this; + } +} diff --git a/src/Types/Sticky.php b/src/Types/Sticky.php new file mode 100644 index 00000000..78cfbbbd --- /dev/null +++ b/src/Types/Sticky.php @@ -0,0 +1,48 @@ +id(Helper::randomString()); + $this->action = new Button(); + } + + /** + * Message store key for the sticky alert. + */ + public function key(): string + { + return 'sticky'; + } + + /** + * Set the action button. + */ + public function action(string $label, string $link = null, array $attributes = []): self + { + $this->action = new Button($label, $link, $attributes); + + return $this; + } +} diff --git a/src/View/Components/Field.php b/src/View/Components/Field.php new file mode 100644 index 00000000..e8b8c43d --- /dev/null +++ b/src/View/Components/Field.php @@ -0,0 +1,37 @@ +alert = $alert; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('alert::components.field'); + } +} diff --git a/src/View/Components/Modal.php b/src/View/Components/Modal.php new file mode 100644 index 00000000..c7bb8222 --- /dev/null +++ b/src/View/Components/Modal.php @@ -0,0 +1,81 @@ + 'button', + 'class' => 'btn btn-primary', + ]; + + /** + * Default cancel button attributes. + */ + public array $cancelAttributes = [ + 'type' => 'button', + 'class' => 'btn btn-secondary', + 'data-bs-dismiss' => 'modal', + ]; + + /** + * Create a new component instance. + */ + public function __construct(Alert $alert) + { + $this->alert = $alert; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('alert::components.modal'); + } + + /** + * Merge and convert array attributes to HTML string attributes. + */ + public function actionAttributes(array $attributes): string + { + $newAttributes = array_merge( + $this->actionAttributes, + $attributes + ); + + return Attribute::toString($newAttributes); + } + + /** + * Merge and convert array attributes to HTML string attributes. + */ + public function cancelAttributes(array $attributes): string + { + $newAttributes = array_merge( + $this->cancelAttributes, + $attributes + ); + + return Attribute::toString($newAttributes); + } +} diff --git a/src/View/Components/Normal.php b/src/View/Components/Normal.php new file mode 100644 index 00000000..3baffde5 --- /dev/null +++ b/src/View/Components/Normal.php @@ -0,0 +1,37 @@ +alert = $alert; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('alert::components.normal'); + } +} diff --git a/src/View/Components/Notify.php b/src/View/Components/Notify.php new file mode 100644 index 00000000..c552ae48 --- /dev/null +++ b/src/View/Components/Notify.php @@ -0,0 +1,37 @@ +alert = $alert; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('alert::components.notify'); + } +} diff --git a/src/View/Components/Sticky.php b/src/View/Components/Sticky.php new file mode 100644 index 00000000..b1349f79 --- /dev/null +++ b/src/View/Components/Sticky.php @@ -0,0 +1,59 @@ + 'button', + 'class' => 'btn btn-sm btn-primary float-end', + ]; + + /** + * Create a new component instance. + */ + public function __construct(Alert $alert) + { + $this->alert = $alert; + } + + /** + * Get the view / contents that represent the component. + */ + public function render(): View|Closure|string + { + return view('alert::components.sticky'); + } + + /** + * Merge and convert array attributes to HTML string attributes. + */ + public function actionAttributes(array $attributes): string + { + $newAttributes = array_merge( + $this->actionAttributes, + $attributes + ); + + return Attribute::toString($newAttributes); + } +} diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/AlertTest.php b/tests/AlertTest.php deleted file mode 100644 index 24a47025..00000000 --- a/tests/AlertTest.php +++ /dev/null @@ -1,12 +0,0 @@ - 'btn-sm']); + + expect($button->label)->toEqual('Submit'); + expect($button->link)->toEqual('/about'); + expect($button->attributes)->toEqual(['class' => 'btn-sm']); + + $button->label('Register'); + $button->link('/register'); + $button->attributes(['id' => 'register']); + + expect($button->label)->toEqual('Register'); + expect($button->link)->toEqual('/register'); + expect($button->attributes)->toEqual(['id' => 'register']); +})->name('component', 'component-button'); diff --git a/tests/Feature/Components/FieldTest.php b/tests/Feature/Components/FieldTest.php new file mode 100644 index 00000000..1cfa7e81 --- /dev/null +++ b/tests/Feature/Components/FieldTest.php @@ -0,0 +1,80 @@ +success() + ->name('username') + ->flash(); + + $view = $this + ->blade(''); + + $view + ->assertSee('class="form-text text-success"', false) + ->assertSee('Username is available'); +})->name('view-component', 'view-component-field-default'); + +it('can render a tagged field view component', function () { + Alert::field('Please select a country') + ->tag('contact') + ->name('country') + ->warning() + ->flash(); + + $view = $this + ->blade(''); + + $view + ->assertSee('class="form-text text-warning"', false) + ->assertSee('Please select a country'); +})->name('view-component', 'view-component-field-tagged'); + +it('can render a named field view component', function () { + Alert::field('Good, you chose a valid country') + ->tag('contact') + ->name('country') + ->success() + ->flash(); + + Alert::field('Good, you chose a valid state') + ->tag('contact') + ->name('state') + ->success() + ->flash(); + + $view = $this + ->blade(''); + + $view + ->assertSee('class="form-text text-success"', false) + ->assertSee('Good, you chose a valid country') + ->assertDontSee('Good, you chose a valid state'); +})->name('view-component', 'view-component-field-tagged'); + +it('can render validation errors for the field view component', function () { + $validator = validator( + ['firstname' => '', 'lastname' => ''], + [ + 'firstname' => 'required', + 'lastname' => 'required', + ] + ); + + Alert::field() + ->tag('contact') + ->errors($validator) + ->error() + ->flash(); + + $this + ->blade('') + ->assertSee('class="form-text text-danger"', false) + ->assertSee('The firstname field is required'); + + $this + ->blade('') + ->assertSee('class="form-text text-danger"', false) + ->assertSee('The lastname field is required'); +})->name('view-component', 'view-component-field-tagged'); diff --git a/tests/Feature/Components/ModalTest.php b/tests/Feature/Components/ModalTest.php new file mode 100644 index 00000000..4629c878 --- /dev/null +++ b/tests/Feature/Components/ModalTest.php @@ -0,0 +1,79 @@ +flash(); + + $this + ->blade('') + ->assertSee('class="modal"', false) + ->assertSee('Than you for joining us'); +})->name('view-component', 'view-component-modal-default'); + +it('can render a default modal with buttons and title', function () { + Alert::modal('Your message has been recieved, you will hear from us soon') + ->action('Yes') + ->cancel('Cancel') + ->centered() + ->title('Please login') + ->error() + ->flash(); + + $view = $this + ->blade(''); + + $view + ->assertSee('class="modal"', false) + ->assertSee('Yes') + ->assertSee('Cancel') + ->assertSee('Please login') + ->assertSee('Your message has been recieved, you will hear from us soon'); +})->name('view-component', 'view-component-modal-buttons-title'); + +it('can render a default modal a the right position', function () { + Alert::modal() + ->centered('centered') + ->flash(); + + $view = $this + ->blade(''); + + $view + ->assertSee('class="modal-dialog centered "', false); +})->name('view-component', 'view-component-modal-position'); + +it('can render a default modal a the right size', function () { + Alert::modal() + ->small() + ->flash(); + + $this + ->blade('') + ->assertSee('class="modal-dialog modal-sm "', false); + + Alert::modal() + ->large() + ->flash(); + + $this + ->blade('') + ->assertSee('class="modal-dialog modal-lg "', false); + + Alert::modal() + ->extraLarge() + ->flash(); + + $this + ->blade('') + ->assertSee('class="modal-dialog modal-xl "', false); + + Alert::modal() + ->fullscreen() + ->flash(); + + $this + ->blade('') + ->assertSee('class="modal-dialog modal-fullscreen "', false); +})->name('view-component', 'view-component-modal-size'); diff --git a/tests/Feature/Components/NormalTest.php b/tests/Feature/Components/NormalTest.php new file mode 100644 index 00000000..be884498 --- /dev/null +++ b/tests/Feature/Components/NormalTest.php @@ -0,0 +1,13 @@ +flash(); + + $this + ->blade('') + ->assertSee('class="alert alert-dismissible alert-"', false) + ->assertSee('Thank you for joining us'); +})->name('view-component', 'view-component-normal-default'); diff --git a/tests/Feature/Components/NotifyTest.php b/tests/Feature/Components/NotifyTest.php new file mode 100644 index 00000000..a8143662 --- /dev/null +++ b/tests/Feature/Components/NotifyTest.php @@ -0,0 +1 @@ +flash(); + + $this + ->blade('') + ->assertSee('class="alert alert-"', false) + ->assertSee('Thank you for joining us'); +})->name('view-component', 'view-component-sticky-default'); diff --git a/tests/Feature/Message/MessageInterfaceTest.php b/tests/Feature/Message/MessageInterfaceTest.php new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/tests/Feature/Message/MessageInterfaceTest.php @@ -0,0 +1 @@ +primary(); + expect($alert->level)->toEqual('primary'); + + $alert = Alert::message('Thank you!')->secondary(); + expect($alert->level)->toEqual('secondary'); + + $alert = Alert::message('Thank you!')->success(); + expect($alert->level)->toEqual('success'); + + $alert = Alert::message('Thank you!')->info(); + expect($alert->level)->toEqual('info'); + + $alert = Alert::message('Thank you!')->error(); + expect($alert->level)->toEqual('danger'); + + $alert = Alert::message('Thank you!')->warning(); + expect($alert->level)->toEqual('warning'); + + $alert = Alert::message('Thank you!')->light(); + expect($alert->level)->toEqual('light'); + + $alert = Alert::message('Thank you!')->dark(); + expect($alert->level)->toEqual('dark'); +})->name('traits', 'traits-levelable'); diff --git a/tests/Feature/Types/FieldTest.php b/tests/Feature/Types/FieldTest.php new file mode 100644 index 00000000..8eea10b0 --- /dev/null +++ b/tests/Feature/Types/FieldTest.php @@ -0,0 +1,17 @@ +name('firstname') + ->flash(); + + $default = Alert::named('field', 'firstname'); + + expect($default)->toBeInstanceOf(MessageInterface::class); + expect($default)->toBeInstanceOf(Field::class); + expect($default->message)->toEqual('Invalid firstname'); +})->name('types', 'types-field', 'types-field'); diff --git a/tests/Feature/Types/ModalTest.php b/tests/Feature/Types/ModalTest.php new file mode 100644 index 00000000..249809da --- /dev/null +++ b/tests/Feature/Types/ModalTest.php @@ -0,0 +1,15 @@ +flash(); + + $default = Alert::default('modal'); + + expect($default)->toBeInstanceOf(MessageInterface::class); + expect($default)->toBeInstanceOf(Modal::class); + expect($default->message)->toEqual('Thank you!'); +})->name('types', 'types-modal', 'types-modal'); diff --git a/tests/Feature/Types/NormalTest.php b/tests/Feature/Types/NormalTest.php new file mode 100644 index 00000000..d4cd807c --- /dev/null +++ b/tests/Feature/Types/NormalTest.php @@ -0,0 +1,15 @@ +flash(); + + $default = Alert::default('normal'); + + expect($default)->toBeInstanceOf(MessageInterface::class); + expect($default)->toBeInstanceOf(Normal::class); + expect($default->message)->toEqual('Thank you!'); +})->name('types', 'types-default', 'types-normal'); diff --git a/tests/Feature/Types/NotifyTest.php b/tests/Feature/Types/NotifyTest.php new file mode 100644 index 00000000..66e658bc --- /dev/null +++ b/tests/Feature/Types/NotifyTest.php @@ -0,0 +1,15 @@ +flash(); + + $default = Alert::default('notify'); + + expect($default)->toBeInstanceOf(MessageInterface::class); + expect($default)->toBeInstanceOf(Notify::class); + expect($default->message)->toEqual('Thank you!'); +})->name('types', 'types-notify', 'types-notify'); diff --git a/tests/Feature/Types/StickyTest.php b/tests/Feature/Types/StickyTest.php new file mode 100644 index 00000000..0e6b452a --- /dev/null +++ b/tests/Feature/Types/StickyTest.php @@ -0,0 +1,15 @@ +flash(); + + $default = Alert::default('sticky'); + + expect($default)->toBeInstanceOf(MessageInterface::class); + expect($default)->toBeInstanceOf(Sticky::class); + expect($default->message)->toEqual('Thank you!'); +})->name('types', 'types-sticky', 'types-sticky'); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 00000000..baff53e7 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,42 @@ +in('Feature'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +expect()->extend('toBeOne', function () { + return $this->toBe(1); +}); + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 00000000..9ae3ca2c --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,43 @@ +> + */ + protected function getPackageAliases($app) + { + return [ + + ]; + } + + protected function getEnvironmentSetUp($app) + { + // perform environment setup + } + + public function packagePath(string $path = '') + { + return __DIR__.'/../'.$path; + } +} diff --git a/tests/Unit/Helpers/AttributeTest.php b/tests/Unit/Helpers/AttributeTest.php new file mode 100644 index 00000000..be43bc79 --- /dev/null +++ b/tests/Unit/Helpers/AttributeTest.php @@ -0,0 +1,13 @@ + 'alert-id', + 'class' => 'alert alert-success', + ]); + + expect($attributesString) + ->toEqual('id="alert-id" class="alert alert-success"'); +})->name('helpers', 'helpers-to-string'); diff --git a/tests/Unit/Helpers/SessionKeyTest.php b/tests/Unit/Helpers/SessionKeyTest.php new file mode 100644 index 00000000..38740bf1 --- /dev/null +++ b/tests/Unit/Helpers/SessionKeyTest.php @@ -0,0 +1,10 @@ +toEqual(SessionKey::MAIN_KEY.'.notify.contact-form-1'); +})->name('helpers', 'helpers-session-key'); diff --git a/tests/Unit/Helpers/TypeTest.php b/tests/Unit/Helpers/TypeTest.php new file mode 100644 index 00000000..b3d9bbc7 --- /dev/null +++ b/tests/Unit/Helpers/TypeTest.php @@ -0,0 +1 @@ +