From 9b59c5a949c91815f933a61d4e8036e910dbdd3a Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 13 Dec 2023 08:46:35 +0000 Subject: [PATCH 01/50] Upgrade to pest --- .github/workflows/run-tests.yml | 2 +- .gitignore | 1 + .php-cs-fixer.cache | 1 - .php_cs.cache | 1 - composer.json | 8 +++- tests/ExampleTest.php | 25 ++--------- tests/Models/Article.php | 18 -------- tests/Pest.php | 45 +++++++++++++++++++ tests/database/factories/ArticlesFactory.php | 23 ---------- ...021_01_01_000000_create_articles_table.php | 33 -------------- 10 files changed, 58 insertions(+), 99 deletions(-) delete mode 100644 .php-cs-fixer.cache delete mode 100644 .php_cs.cache delete mode 100644 tests/Models/Article.php create mode 100644 tests/Pest.php delete mode 100644 tests/database/factories/ArticlesFactory.php delete mode 100644 tests/database/migrations/2021_01_01_000000_create_articles_table.php diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index b55c96f..75bdaaf 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -51,7 +51,7 @@ jobs: composer update --${{ matrix.stability }} --prefer-dist --no-interaction - name: Execute tests - run: vendor/bin/phpunit --coverage-clover coverage.xml + run: vendor/bin/pest --coverage-clover coverage.xml - name: Upload coverage uses: codecov/codecov-action@v3 diff --git a/.gitignore b/.gitignore index 8e3e4c8..9155e46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /vendor/ +/build/ node_modules/ npm-debug.log yarn-error.log diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache deleted file mode 100644 index a910d0b..0000000 --- a/.php-cs-fixer.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","src\/Facades\/Ui.php":"2df93407364522584dd0449ed7eab8d1","src\/Components\/BaseComponent.php":"fdee75487fab03cdaa3c5f1d6e072567","src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","tests\/TestCase.php":"a0f6a56dd991f0c35941339d925c3ebe","tests\/Models\/Article.php":"16eb5ddaef3df65ab81b33a8e414f143","tests\/database\/migrations\/2021_01_01_000000_create_articles_table.php":"e05c96b26f34720cb56eb19da0b1f25b","tests\/database\/factories\/ArticlesFactory.php":"f1036385fe859c250832fdf00900dac8","tests\/ExampleTest.php":"68041e6f969ee73c9abfbfa075b774a8"}} \ No newline at end of file diff --git a/.php_cs.cache b/.php_cs.cache deleted file mode 100644 index da3a431..0000000 --- a/.php_cs.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"7.4.13","version":"2.17.3","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":true,"class_definition":true,"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":{"elements":["const","method","property"]},"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true},"hashes":{"tests\/database\/migrations\/2021_01_01_000000_create_articles_table.php":1906224168}} \ No newline at end of file diff --git a/composer.json b/composer.json index e7a665e..816d85d 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,8 @@ "require-dev": { "laravel/legacy-factories": "^1.1", "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", - "phpunit/phpunit": "^9.6" + "phpunit/phpunit": "^9.6", + "pestphp/pest": "^1.23" }, "autoload": { "psr-4": { @@ -48,5 +49,10 @@ "Ui": "AppKit\\Ui\\Facades\\Ui" } } + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } } } diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php index 8c7bea8..1a0f931 100644 --- a/tests/ExampleTest.php +++ b/tests/ExampleTest.php @@ -1,24 +1,7 @@ assertTrue(true); - } - - /** @test */ - public function articlesCanBeLoaded() - { - // create 5 articles - factory(Article::class, 5)->create(); - - // check the database for 5 articles - $this->assertEquals(5, Article::count()); - } -} +test('true is true', function () { + expect(true)->toBeTrue(); +}); diff --git a/tests/Models/Article.php b/tests/Models/Article.php deleted file mode 100644 index 5017400..0000000 --- a/tests/Models/Article.php +++ /dev/null @@ -1,18 +0,0 @@ -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. +| +*/ + +function something() +{ + // .. +} diff --git a/tests/database/factories/ArticlesFactory.php b/tests/database/factories/ArticlesFactory.php deleted file mode 100644 index 4265b36..0000000 --- a/tests/database/factories/ArticlesFactory.php +++ /dev/null @@ -1,23 +0,0 @@ -define(Article::class, function (Faker $faker) { - return [ - 'title' => $faker->sentence, - 'body' => $faker->paragraph, - ]; -}); diff --git a/tests/database/migrations/2021_01_01_000000_create_articles_table.php b/tests/database/migrations/2021_01_01_000000_create_articles_table.php deleted file mode 100644 index 3dc7a07..0000000 --- a/tests/database/migrations/2021_01_01_000000_create_articles_table.php +++ /dev/null @@ -1,33 +0,0 @@ -increments('id'); - $table->string('title'); - $table->text('body'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - * - * @return void - */ - public function down() - { - Schema::dropIfExists('articles'); - } -} From 37af500e40aa267dd45ebec877813bebd5363f45 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 13 Dec 2023 09:37:42 +0000 Subject: [PATCH 02/50] Add in the attribute builder --- composer.json | 4 +-- src/AttributeBuilder.php | 62 ++++++++++++++++++++++++++++++++ src/Components/BaseComponent.php | 2 +- src/Facades/Ui.php | 4 +-- tests/AttributeBagTest.php | 27 ++++++++++++++ tests/ExampleTest.php | 7 ---- tests/TestCase.php | 6 ++-- 7 files changed, 97 insertions(+), 15 deletions(-) create mode 100644 src/AttributeBuilder.php create mode 100644 tests/AttributeBagTest.php delete mode 100644 tests/ExampleTest.php diff --git a/composer.json b/composer.json index 816d85d..7e373a0 100644 --- a/composer.json +++ b/composer.json @@ -28,12 +28,12 @@ }, "autoload": { "psr-4": { - "AppKit\\Ui\\": "src" + "AppKit\\UI\\": "src" } }, "autoload-dev": { "psr-4": { - "AppKit\\Ui\\Tests\\": "tests" + "AppKit\\UI\\Tests\\": "tests" } }, "scripts": { diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php new file mode 100644 index 0000000..2d861f3 --- /dev/null +++ b/src/AttributeBuilder.php @@ -0,0 +1,62 @@ +merge(['class' => implode(' ', $classes)]); + + return $this; + } + + public function addOptionClass($option, $classes) { + $value = $this->options[$option]; + + $classes = value($classes); + + if (array_key_exists($value, $classes)) { + return $this->addClass($classes[$value]); + } + + return $this; + } + + public function setAttribute($attribute, $value = null) + { + $this->attributeBag->offsetSet($attribute, $value); + + return $this; + } + + public function setData($key, $value = null) + { + $this->attributeBag->offsetSet('data-' . $key, $value); + + return $this; + } + + public function merge($attributes) { + $this->attributeBag = $this->attributeBag->merge($attributes); + } + + public function getAttributeBag() + { + return $this->attributeBag; + } +} diff --git a/src/Components/BaseComponent.php b/src/Components/BaseComponent.php index 5daf914..e02381d 100644 --- a/src/Components/BaseComponent.php +++ b/src/Components/BaseComponent.php @@ -1,6 +1,6 @@ attributeBuilder = new AttributeBuilder($attributeBag, collect()); +}); + +it('can be initialised with an empty attribute bag', function () { + expect($this->attributeBuilder->getAttributeBag()->jsonSerialize())->toBeEmpty(); +}); + +it('can add classes in multiple ways', function (string $method, $data) { + $attributeBuilder = $this->attributeBuilder->addClass($data); + + expect($attributeBuilder->getAttributeBag()->jsonSerialize())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); +})->with([ + ['as a single string', 'class-1 class-2 class-3 class-4 class-5'], + ['as an array', ['class-1', 'class-2', 'class-3', 'class-4', 'class-5']], + ['as a nested array', [['class-1', 'class-2'], ['class-3', 'class-4', 'class-5']]], + ['in a mixed way', ['class-1 class-2', 'class-3', ['class-4', 'class-5']]], +]); diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php deleted file mode 100644 index 1a0f931..0000000 --- a/tests/ExampleTest.php +++ /dev/null @@ -1,7 +0,0 @@ -toBeTrue(); -}); diff --git a/tests/TestCase.php b/tests/TestCase.php index 2b7e3db..7fef7e5 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,9 +1,9 @@ Date: Wed, 13 Dec 2023 09:38:02 +0000 Subject: [PATCH 03/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 1 + src/AttributeBuilder.php | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 .php-cs-fixer.cache diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache new file mode 100644 index 0000000..215f1a0 --- /dev/null +++ b/.php-cs-fixer.cache @@ -0,0 +1 @@ +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"c58ab67f8e9d6394c20e71c223e42aa3","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"46e507aa1430c6b03070e726e3ed4308","tests\/Pest.php":"1a2dbf23f379555d7797b385d27b4bd7","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365"}} \ No newline at end of file diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 2d861f3..7a37246 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -11,8 +11,7 @@ class AttributeBuilder public function __construct( protected ComponentAttributeBag &$attributeBag, protected Collection $options - ) - { + ) { } @@ -25,7 +24,8 @@ public function addClass(...$classes) return $this; } - public function addOptionClass($option, $classes) { + public function addOptionClass($option, $classes) + { $value = $this->options[$option]; $classes = value($classes); @@ -51,7 +51,8 @@ public function setData($key, $value = null) return $this; } - public function merge($attributes) { + public function merge($attributes) + { $this->attributeBag = $this->attributeBag->merge($attributes); } From 23f6ce80722fecf941afa6841b6b43397e9e7b5c Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 13 Dec 2023 10:01:11 +0000 Subject: [PATCH 04/50] forward calls --- src/AttributeBuilder.php | 8 ++++++++ tests/AttributeBagTest.php | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 2d861f3..242d4d4 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -4,10 +4,13 @@ use Illuminate\Support\Arr; use Illuminate\Support\Collection; +use Illuminate\Support\Traits\ForwardsCalls; use Illuminate\View\ComponentAttributeBag; class AttributeBuilder { + use ForwardsCalls; + public function __construct( protected ComponentAttributeBag &$attributeBag, protected Collection $options @@ -59,4 +62,9 @@ public function getAttributeBag() { return $this->attributeBag; } + + public function __call($method, $parameters) + { + return $this->forwardCallTo($this->attributeBag, $method, $parameters); + } } diff --git a/tests/AttributeBagTest.php b/tests/AttributeBagTest.php index a63b658..e697d64 100644 --- a/tests/AttributeBagTest.php +++ b/tests/AttributeBagTest.php @@ -18,7 +18,7 @@ it('can add classes in multiple ways', function (string $method, $data) { $attributeBuilder = $this->attributeBuilder->addClass($data); - expect($attributeBuilder->getAttributeBag()->jsonSerialize())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); + expect($attributeBuilder->jsonSerialize())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); })->with([ ['as a single string', 'class-1 class-2 class-3 class-4 class-5'], ['as an array', ['class-1', 'class-2', 'class-3', 'class-4', 'class-5']], From 5b79008982baac9dac10e062f0ab89642b1dd48e Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 13 Dec 2023 10:04:12 +0000 Subject: [PATCH 05/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 215f1a0..0344e9d 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"c58ab67f8e9d6394c20e71c223e42aa3","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"46e507aa1430c6b03070e726e3ed4308","tests\/Pest.php":"1a2dbf23f379555d7797b385d27b4bd7","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"b4055192c0fafab371a573da3e44ded7","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"3cc2a5ea43461c9a70aa717213b343eb","tests\/Pest.php":"1a2dbf23f379555d7797b385d27b4bd7","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365"}} \ No newline at end of file From 4e660c451d0c647aa2496da8698f08f8092d8387 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 13 Dec 2023 13:11:00 +0000 Subject: [PATCH 06/50] Improve the tests --- tests/AttributeBagTest.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/AttributeBagTest.php b/tests/AttributeBagTest.php index e697d64..ec33cf8 100644 --- a/tests/AttributeBagTest.php +++ b/tests/AttributeBagTest.php @@ -5,20 +5,21 @@ uses(\AppKit\UI\Tests\TestCase::class); -beforeEach(function () { - $attributeBag = new ComponentAttributeBag(); +function createAttributeBuilder($attributes = [], $options = []) +{ + $attributeBag = new ComponentAttributeBag($attributes); - $this->attributeBuilder = new AttributeBuilder($attributeBag, collect()); -}); + return new AttributeBuilder($attributeBag, collect($options)); +} it('can be initialised with an empty attribute bag', function () { - expect($this->attributeBuilder->getAttributeBag()->jsonSerialize())->toBeEmpty(); + expect(createAttributeBuilder()->getAttributes())->toBeEmpty(); }); it('can add classes in multiple ways', function (string $method, $data) { - $attributeBuilder = $this->attributeBuilder->addClass($data); + $attributeBuilder = createAttributeBuilder()->addClass($data); - expect($attributeBuilder->jsonSerialize())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); + expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); })->with([ ['as a single string', 'class-1 class-2 class-3 class-4 class-5'], ['as an array', ['class-1', 'class-2', 'class-3', 'class-4', 'class-5']], From 51a9df3cb590ba484be7887016204e9efe22dc04 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 13 Dec 2023 13:11:21 +0000 Subject: [PATCH 07/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 0344e9d..9a8a2a0 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"b4055192c0fafab371a573da3e44ded7","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"3cc2a5ea43461c9a70aa717213b343eb","tests\/Pest.php":"1a2dbf23f379555d7797b385d27b4bd7","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"b4055192c0fafab371a573da3e44ded7","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"1a2dbf23f379555d7797b385d27b4bd7","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365"}} \ No newline at end of file From 31fd45a1aa7b0a937484ad315c1e26fdf2b4a5bd Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 13 Dec 2023 13:56:58 +0000 Subject: [PATCH 08/50] Tidy things up --- src/AttributeBuilder.php | 126 ++++++++++++++++++++++++++++---- tests/AttributeBagClassTest.php | 49 +++++++++++++ tests/AttributeBagTest.php | 28 ------- tests/Pest.php | 20 ++++- 4 files changed, 176 insertions(+), 47 deletions(-) create mode 100644 tests/AttributeBagClassTest.php delete mode 100644 tests/AttributeBagTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index f98ce34..c292b9f 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -2,10 +2,13 @@ namespace AppKit\UI; +use BadMethodCallException; use Illuminate\Support\Arr; use Illuminate\Support\Collection; use Illuminate\Support\Traits\ForwardsCalls; use Illuminate\View\ComponentAttributeBag; +use InvalidArgumentException; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; class AttributeBuilder { @@ -18,52 +21,143 @@ public function __construct( } - public function addClass(...$classes) + /** + * Add classes to the attribute bag + * + * @param mixed $classes + * @return AttributeBuilder + * @throws InvalidArgumentException + */ + public function addClass(...$classes): AttributeBuilder { + // flatten the arguments into the function $classes = Arr::flatten($classes); - $this->merge(['class' => implode(' ', $classes)]); + // merge the new classes with the existing ones + $this->mergeAttributes(['class' => implode(' ', $classes)]); + // return a fluent API return $this; } - public function addOptionClass($option, $classes) + /** + * Remove classes from the attribute bag + * + * @param mixed $classes + * @return AttributeBuilder + */ + public function removeClass(...$classes): AttributeBuilder { - $value = $this->options[$option]; - - $classes = value($classes); + // flatten the arguments to the function + $classes = Arr::flatten($classes); - if (array_key_exists($value, $classes)) { - return $this->addClass($classes[$value]); + // calculate the classes that we need to remove + $classesToRemove = collect($classes) + ->map(function ($item) { + // covert every item into an array + if (is_array($item)) { + // if we are already an array, we can just return it + return $item; + } + + // otherwise, we need to split the string on spaces + return explode(' ', $item); + }) + ->flatten() + ->map(function ($item) { + // trim all of the items in the collection + return trim($item); + }) + ->toArray(); + + // get all of the current classes already applied + $currentClasses = explode(' ', $this->getAttribute('class')); + + // create an array to store all of the new classes + $newClasses = []; + + // loop through all of the classes that we already have + foreach ($currentClasses as $currentClass) { + // trim the class + $currentClass = trim($currentClass); + + // check if it's in the list of classes to remove + if (!in_array($currentClass, $classesToRemove)) { + // if it's not, we add it to the list of classes that the attribute bag should have + $newClasses[] = $currentClass; + } } + // set the class attribute + $this->setAttribute('class', implode(' ', $newClasses)); + + // return a fluent API return $this; } - public function setAttribute($attribute, $value = null) + /** + * Set an attribute on the attribute bag + * + * @param mixed $attribute + * @param mixed $value + * @return AttributeBuilder + */ + public function setAttribute($attribute, $value = null): AttributeBuilder { - $this->attributeBag->offsetSet($attribute, $value); + // set the attribute on the attribute bag + $this->offsetSet($attribute, $value); + // return a fluent API return $this; } - public function setData($key, $value = null) + /** + * Get an attribute from the attribute bag + * + * @param mixed $attribute + * @param mixed $default + * @return AttributeBag + */ + public function getAttribute($attribute, $default = null): AttributeBag { - $this->attributeBag->offsetSet('data-' . $key, $value); - - return $this; + // get the attribute from the attribute bag + return $this->get($attribute, $default); } - public function merge($attributes) + /** + * Merge the attributes into the attribute bag + * + * @param mixed $attributes + * @return AttributeBuilder + * @throws InvalidArgumentException + */ + public function mergeAttributes($attributes): AttributeBuilder { + // merge on the attribute bag will return a new instance, so we need to update our reference to be the new one $this->attributeBag = $this->attributeBag->merge($attributes); + + // return a fluent API + return $this; } - public function getAttributeBag() + /** + * Return the underlying component attribute bag instance + * + * @return ComponentAttributeBag + */ + public function getAttributeBag(): ComponentAttributeBag { return $this->attributeBag; } + /** + * Magic method to catch everything that we aren't already dealing with + * + * @param mixed $method + * @param mixed $parameters + * @return mixed + * @throws BadMethodCallException + */ public function __call($method, $parameters) { return $this->forwardCallTo($this->attributeBag, $method, $parameters); diff --git a/tests/AttributeBagClassTest.php b/tests/AttributeBagClassTest.php new file mode 100644 index 0000000..c004a4e --- /dev/null +++ b/tests/AttributeBagClassTest.php @@ -0,0 +1,49 @@ +getAttributes())->toBeEmpty(); +}); + +it('can add classes in multiple ways', function (string $method, $data) { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->addClass($data); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); +})->with($classData); + +it('can add classes to the classes that already exist', function (string $method, $data) { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['class' => 'class-6']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->addClass($data); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5 class-6'); +})->with($classData); + +it('can remove classes in multiple ways', function (string $method, $data) { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['class' => 'class-1 class-2 class-3 class-4 class-5 class-6']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeClass($data); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-6'); +})->with($classData); diff --git a/tests/AttributeBagTest.php b/tests/AttributeBagTest.php deleted file mode 100644 index ec33cf8..0000000 --- a/tests/AttributeBagTest.php +++ /dev/null @@ -1,28 +0,0 @@ -getAttributes())->toBeEmpty(); -}); - -it('can add classes in multiple ways', function (string $method, $data) { - $attributeBuilder = createAttributeBuilder()->addClass($data); - - expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); -})->with([ - ['as a single string', 'class-1 class-2 class-3 class-4 class-5'], - ['as an array', ['class-1', 'class-2', 'class-3', 'class-4', 'class-5']], - ['as a nested array', [['class-1', 'class-2'], ['class-3', 'class-4', 'class-5']]], - ['in a mixed way', ['class-1 class-2', 'class-3', ['class-4', 'class-5']]], -]); diff --git a/tests/Pest.php b/tests/Pest.php index 5949c61..c2373f1 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,8 @@ in('Feature'); +uses(AppKit\UI\Tests\TestCase::class); /* |-------------------------------------------------------------------------- @@ -39,7 +42,18 @@ | */ -function something() +/** + * Create an instance of an attribute builder + * + * @param array $attributes + * @param array $options + * @return AttributeBuilder + */ +function createAttributeBuilder($attributes = [], $options = []) { - // .. + // create an attribute bag that will be passed to the attribute builder + $attributeBag = new ComponentAttributeBag($attributes); + + // return the attribute builder + return new AttributeBuilder($attributeBag, collect($options)); } From 1dcf783df8cdd5111556be9159a80abd675b9528 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 13 Dec 2023 13:57:15 +0000 Subject: [PATCH 09/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 9a8a2a0..28ef5d9 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"b4055192c0fafab371a573da3e44ded7","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"1a2dbf23f379555d7797b385d27b4bd7","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"d483dda67855326d1402d4eb4916cd50","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"4dbdb3261b8641a0c265d11dd2f68153","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07"}} \ No newline at end of file From 5f60fcd19c35bd7779fe6488b52c6ed1b5a411f7 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Thu, 14 Dec 2023 13:07:25 +0000 Subject: [PATCH 10/50] Setup the addition of attributes --- src/AttributeBuilder.php | 97 ++++++++++++++++++++++++++++- tests/AttributeBagAttributeTest.php | 79 +++++++++++++++++++++++ 2 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 tests/AttributeBagAttributeTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index c292b9f..2fb676d 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -5,15 +5,25 @@ use BadMethodCallException; use Illuminate\Support\Arr; use Illuminate\Support\Collection; +use Illuminate\Support\Str; use Illuminate\Support\Traits\ForwardsCalls; use Illuminate\View\ComponentAttributeBag; use InvalidArgumentException; +use ReflectionMethod; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; class AttributeBuilder { use ForwardsCalls; + protected $magicMethodRegex = <<<'EOD' + / + (?Pset|add|remove|toggle) + (?P[A-Za-z0-9]*) + (?PAttribute|Class) + /x + EOD; + public function __construct( protected ComponentAttributeBag &$attributeBag, protected Collection $options @@ -111,14 +121,28 @@ public function setAttribute($attribute, $value = null): AttributeBuilder return $this; } + public function removeAttribute(...$attributes): AttributeBuilder + { + // flatten the arguments to the function + $attributes = Arr::flatten($attributes); + + // remove each of the attributes from the attribute bag + foreach ($attributes as $attribute) { + $this->offsetUnset($attribute); + } + + // return a fluent API + return $this; + } + /** * Get an attribute from the attribute bag * * @param mixed $attribute * @param mixed $default - * @return AttributeBag + * @return mixed */ - public function getAttribute($attribute, $default = null): AttributeBag + public function getAttribute($attribute, $default = null): mixed { // get the attribute from the attribute bag return $this->get($attribute, $default); @@ -160,6 +184,75 @@ public function getAttributeBag(): ComponentAttributeBag */ public function __call($method, $parameters) { + // an array to store any possible matches from the magic method regex + $magicMethodRegexMatches = []; + + if (preg_match($this->magicMethodRegex, $method, $magicMethodRegexMatches)) { + $magicMethodParameterNames = ['attribute', 'value']; + + // we alias the add operation to set + if ($magicMethodRegexMatches['operation'] == 'add') { + $magicMethodRegexMatches['operation'] = 'set'; + } + + // calculate the name of the method that we actually want to call + $methodName = $magicMethodRegexMatches['operation'] . $magicMethodRegexMatches['type']; + + // the methods that we are allowed to call via the magic method + $allowedMethods = [ + 'addAttribute', + 'setAttribute', + 'removeAttribute' + ]; + + // check that it is an allowed method + if (in_array($methodName, $allowedMethods)) { + // get the reflection of the method that we are ultimately going to call + $methodReflection = new ReflectionMethod($this, $methodName); + + // get the parameters of that method + $methodParametersReflection = $methodReflection->getParameters(); + + // create an array to store the parameters that we will actually pass to the method + $methodParameterValues = []; + + foreach ($methodParametersReflection as $callableParameter) { + // get the name of the parameter + $parameterName = $callableParameter->getName(); + + // check if it is variadic + if ($callableParameter->isVariadic()) { + // if it is, then the name is going to be the singular name of the method + $parameterName = Str::of($parameterName)->singular()->__toString(); + } + + // check if we have a matching parameter in the regex matches + if (array_key_exists($parameterName, $magicMethodRegexMatches)) { + // if we do, we get that value and set it on the array that will be passed to the method + $methodParameterValues[$parameterName] = lcfirst($magicMethodRegexMatches[$parameterName]); + + // we then remove the parameter name from the list that we are still looking for + $magicMethodParameterNames = array_diff($magicMethodParameterNames, [$parameterName]); + } + } + + // because we have removed some things from the array, we want to reset the keys + $magicMethodParameterNames = array_values($magicMethodParameterNames); + + // loop through everything that is left + foreach ($magicMethodParameterNames as $parameterPosition => $parameterName) { + // and check if it was passed through as a parameter to the magic method + if (isset($parameters[$parameterPosition])) { + // if it was, then we add it to the list of parameters + $methodParameterValues[$parameterName] = $parameters[$parameterPosition]; + } + } + + // finally, we call the underlying method + return call_user_func_array([$this, $methodName], $methodParameterValues); + } + } + return $this->forwardCallTo($this->attributeBag, $method, $parameters); } } diff --git a/tests/AttributeBagAttributeTest.php b/tests/AttributeBagAttributeTest.php new file mode 100644 index 0000000..fdcf3e0 --- /dev/null +++ b/tests/AttributeBagAttributeTest.php @@ -0,0 +1,79 @@ +setAttribute('foo', 'bar'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bar'); +}); + + +it('can change the value of an existing attribute', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->setAttribute('foo', 'bat'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bat'); +}); + +it('can remove an existing attribute', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeAttribute('foo'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo'); +}); + +it('can remove multiple existing attributes via an array', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['foo' => 'bar', 'bat' => 'buz']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeAttribute(['foo', 'bat']); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKeys(['foo', 'bat']); +}); + +it('can remove multiple existing attributes via an multiple parameters', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['foo' => 'bar', 'bat' => 'buz']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeAttribute('foo', 'bat'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKeys(['foo', 'bat']); +}); + +it('can add an attribute via a magic method', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->setFooAttribute('bar'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bar'); +}); + +it('can remove an attribute via a magic method', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeFooAttribute(); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo'); +}); From 866328a7e909bbb7c2fa748599d400095a802cde Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Thu, 14 Dec 2023 13:08:10 +0000 Subject: [PATCH 11/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- src/AttributeBuilder.php | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 28ef5d9..0fbadee 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"d483dda67855326d1402d4eb4916cd50","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"4dbdb3261b8641a0c265d11dd2f68153","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"f1479433690718f3df6ce4989fd34a25","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"4dbdb3261b8641a0c265d11dd2f68153","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07","tests\/AttributeBagAttributeTest.php":"743dc9f7e4bc5c37ff1fdebe41e381bb"}} \ No newline at end of file diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 2fb676d..47df915 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -10,7 +10,6 @@ use Illuminate\View\ComponentAttributeBag; use InvalidArgumentException; use ReflectionMethod; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; class AttributeBuilder { @@ -202,7 +201,7 @@ public function __call($method, $parameters) $allowedMethods = [ 'addAttribute', 'setAttribute', - 'removeAttribute' + 'removeAttribute', ]; // check that it is an allowed method From 36cdc0e2fb5d3659b605fbb4f3a882584e12218b Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Fri, 15 Dec 2023 13:48:59 +0000 Subject: [PATCH 12/50] Setup attribute helpers --- src/AttributeBuilder.php | 92 ++++++++++++++++++----- tests/AttributeBagAttributeHelperTest.php | 87 +++++++++++++++++++++ tests/AttributeBagAttributeTest.php | 12 --- tests/Pest.php | 7 ++ 4 files changed, 167 insertions(+), 31 deletions(-) create mode 100644 tests/AttributeBagAttributeHelperTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 2fb676d..c25810d 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -10,19 +10,14 @@ use Illuminate\View\ComponentAttributeBag; use InvalidArgumentException; use ReflectionMethod; +use RuntimeException; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; class AttributeBuilder { use ForwardsCalls; - protected $magicMethodRegex = <<<'EOD' - / - (?Pset|add|remove|toggle) - (?P[A-Za-z0-9]*) - (?PAttribute|Class) - /x - EOD; + static $attributeHelpers = []; public function __construct( protected ComponentAttributeBag &$attributeBag, @@ -31,6 +26,11 @@ public function __construct( } + static function createAttributeHelper(string $name, callable $callback) + { + self::$attributeHelpers[$name] = $callback; + } + /** * Add classes to the attribute bag * @@ -112,23 +112,50 @@ public function removeClass(...$classes): AttributeBuilder * @param mixed $value * @return AttributeBuilder */ - public function setAttribute($attribute, $value = null): AttributeBuilder + public function setAttribute($attribute, $value = null, $attributeType = null): AttributeBuilder { - // set the attribute on the attribute bag - $this->offsetSet($attribute, $value); + // check if we have an attribute type + if ($attributeType != null) { + if (!array_key_exists($attributeType, self::$attributeHelpers)) { + throw new RuntimeException('No such attribute helper ' . $attributeType); + } + + // if we do, we need to pass it through the callback + $attributes = self::$attributeHelpers[$attributeType]($attribute, $value); + + foreach ($attributes as $attribute => $value) { + $this->setAttribute($attribute, $value); + } + } else { + // set the attribute on the attribute bag + $this->offsetSet($attribute, $value); + } + // return a fluent API return $this; } - public function removeAttribute(...$attributes): AttributeBuilder + public function removeAttribute($attribute, $attributeType = null): AttributeBuilder { - // flatten the arguments to the function - $attributes = Arr::flatten($attributes); + $attributeArray = (array) $attribute; + + foreach ($attributeArray as $attribute) { + // remove each of the attributes from the attribute bag + if ($attributeType != null) { + if (!array_key_exists($attributeType, self::$attributeHelpers)) { + throw new RuntimeException('No such attribute helper ' . $attributeType); + } + + // if we do, we need to pass it through the callback + $attributes = self::$attributeHelpers[$attributeType]($attribute, null); - // remove each of the attributes from the attribute bag - foreach ($attributes as $attribute) { - $this->offsetUnset($attribute); + foreach (array_keys($attributes) as $attribute) { + $this->offsetUnset($attribute); + } + } else { + $this->offsetUnset($attribute); + } } // return a fluent API @@ -184,11 +211,38 @@ public function getAttributeBag(): ComponentAttributeBag */ public function __call($method, $parameters) { + // we need to build up the regex that we are going to use to parse the method + $attributeTypesString = ''; + + // check if we have any attribute helpers + if (!empty(self::$attributeHelpers)) { + // create an array to store all of the helper names + $attributeTypes = []; + + foreach (self::$attributeHelpers as $helper => $closure) { + // pull out the name of the helper in studly case + $attributeTypes[] = Str::of($helper)->studly()->__toString(); + } + + // create the string that will be included in the regex + $attributeTypesString = '(?P' . implode('|', $attributeTypes) . ')?'; + } + + // create the regex + $magicMethodRegex = ' + / + (?Pset|add|remove|toggle) + ' . $attributeTypesString . ' + (?P[A-Za-z0-9]*)? + (?PAttribute|Class) + /x + '; + // an array to store any possible matches from the magic method regex $magicMethodRegexMatches = []; - if (preg_match($this->magicMethodRegex, $method, $magicMethodRegexMatches)) { - $magicMethodParameterNames = ['attribute', 'value']; + if (preg_match($magicMethodRegex, $method, $magicMethodRegexMatches)) { + $magicMethodParameterNames = ['attribute', 'value', 'attributeType']; // we alias the add operation to set if ($magicMethodRegexMatches['operation'] == 'add') { @@ -227,7 +281,7 @@ public function __call($method, $parameters) } // check if we have a matching parameter in the regex matches - if (array_key_exists($parameterName, $magicMethodRegexMatches)) { + if (array_key_exists($parameterName, $magicMethodRegexMatches) && !empty($magicMethodRegexMatches[$parameterName])) { // if we do, we get that value and set it on the array that will be passed to the method $methodParameterValues[$parameterName] = lcfirst($magicMethodRegexMatches[$parameterName]); diff --git a/tests/AttributeBagAttributeHelperTest.php b/tests/AttributeBagAttributeHelperTest.php new file mode 100644 index 0000000..a6ef9ab --- /dev/null +++ b/tests/AttributeBagAttributeHelperTest.php @@ -0,0 +1,87 @@ +setAttribute('foo', 'bar', 'data'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('data-foo', 'bar'); +}); + +it('can use attribute helpers via the magic method', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add in the data helper + addDataAttributeHelperToAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->setDataAttribute('foo', 'bar'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('data-foo', 'bar'); +}); + +it('can use attribute helpers and attribute name via the magic method', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add in the data helper + addDataAttributeHelperToAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->setDataFooAttribute('bar'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('data-foo', 'bar'); +}); + +it('can have attribute helpers which modify the attributes being removed', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['data-foo' => 'bar']); + + // add in the data helper + addDataAttributeHelperToAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeAttribute('foo', 'data'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKey('data-foo'); +}); + +it('can use attribute helpers to remove via the magic method', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['data-foo' => 'bar']); + + // add in the data helper + addDataAttributeHelperToAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeDataAttribute('foo'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKey('data-foo'); +}); + +it('can use attribute helpers and attribute name to remove via the magic method', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(['data-foo' => 'bar']); + + // add in the data helper + addDataAttributeHelperToAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder = $attributeBuilder->removeDataFooAttribute(); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->not()->toHaveKey('data-foo'); +}); diff --git a/tests/AttributeBagAttributeTest.php b/tests/AttributeBagAttributeTest.php index fdcf3e0..14ed4f3 100644 --- a/tests/AttributeBagAttributeTest.php +++ b/tests/AttributeBagAttributeTest.php @@ -11,7 +11,6 @@ expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bar'); }); - it('can change the value of an existing attribute', function () { // create a new attribute builder $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); @@ -45,17 +44,6 @@ expect($attributeBuilder->getAttributes())->not()->toHaveKeys(['foo', 'bat']); }); -it('can remove multiple existing attributes via an multiple parameters', function () { - // create a new attribute builder - $attributeBuilder = createAttributeBuilder(['foo' => 'bar', 'bat' => 'buz']); - - // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeAttribute('foo', 'bat'); - - // check that the classes are correct - expect($attributeBuilder->getAttributes())->not()->toHaveKeys(['foo', 'bat']); -}); - it('can add an attribute via a magic method', function () { // create a new attribute builder $attributeBuilder = createAttributeBuilder(); diff --git a/tests/Pest.php b/tests/Pest.php index c2373f1..961734f 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -57,3 +57,10 @@ function createAttributeBuilder($attributes = [], $options = []) // return the attribute builder return new AttributeBuilder($attributeBag, collect($options)); } + +function addDataAttributeHelperToAttributeBuilder() +{ + AttributeBuilder::createAttributeHelper('data', function ($attribute, $value) { + return ['data-' . $attribute => $value]; + }); +} From 4112ebc77fbbcf33420a9168776aaaa2d64a648d Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Fri, 15 Dec 2023 13:50:18 +0000 Subject: [PATCH 13/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- src/AttributeBuilder.php | 5 ++--- tests/AttributeBagAttributeHelperTest.php | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 28ef5d9..0b81975 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"d483dda67855326d1402d4eb4916cd50","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"4dbdb3261b8641a0c265d11dd2f68153","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"c63f12521e4ea0e8854bc7bae12d78cd","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"d77ce14d823620b135521106b7e6a415","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07","tests\/AttributeBagAttributeHelperTest.php":"2055123024e7e1e3cd705f13ee760b61","tests\/AttributeBagAttributeTest.php":"174797353c39a72656af9babc80a27a6"}} \ No newline at end of file diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index f608f61..c70e335 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -11,13 +11,12 @@ use InvalidArgumentException; use ReflectionMethod; use RuntimeException; -use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; class AttributeBuilder { use ForwardsCalls; - static $attributeHelpers = []; + public static $attributeHelpers = []; public function __construct( protected ComponentAttributeBag &$attributeBag, @@ -26,7 +25,7 @@ public function __construct( } - static function createAttributeHelper(string $name, callable $callback) + public static function createAttributeHelper(string $name, callable $callback) { self::$attributeHelpers[$name] = $callback; } diff --git a/tests/AttributeBagAttributeHelperTest.php b/tests/AttributeBagAttributeHelperTest.php index a6ef9ab..2676b7b 100644 --- a/tests/AttributeBagAttributeHelperTest.php +++ b/tests/AttributeBagAttributeHelperTest.php @@ -1,6 +1,5 @@ Date: Fri, 15 Dec 2023 16:06:11 +0000 Subject: [PATCH 14/50] Refactor --- src/AttributeBuilder.php | 15 +++++++++++++-- tests/Pest.php | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index c70e335..2d9d5fc 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -16,7 +16,11 @@ class AttributeBuilder { use ForwardsCalls; - public static $attributeHelpers = []; + /** + * An array to store the registered attribute helpers + * @var array + */ + protected static $attributeHelpers = []; public function __construct( protected ComponentAttributeBag &$attributeBag, @@ -25,7 +29,14 @@ public function __construct( } - public static function createAttributeHelper(string $name, callable $callback) + /** + * Register a new attribute helper + * + * @param string $name + * @param callable $callback + * @return void + */ + public static function registerAttributeHelper(string $name, callable $callback) { self::$attributeHelpers[$name] = $callback; } diff --git a/tests/Pest.php b/tests/Pest.php index 961734f..d478a0e 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -60,7 +60,7 @@ function createAttributeBuilder($attributes = [], $options = []) function addDataAttributeHelperToAttributeBuilder() { - AttributeBuilder::createAttributeHelper('data', function ($attribute, $value) { + AttributeBuilder::registerAttributeHelper('data', function ($attribute, $value) { return ['data-' . $attribute => $value]; }); } From eabc09dd684120d5078e2006394be0c7e5031631 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Fri, 15 Dec 2023 16:06:33 +0000 Subject: [PATCH 15/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 0b81975..9788782 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"c63f12521e4ea0e8854bc7bae12d78cd","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"d77ce14d823620b135521106b7e6a415","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07","tests\/AttributeBagAttributeHelperTest.php":"2055123024e7e1e3cd705f13ee760b61","tests\/AttributeBagAttributeTest.php":"174797353c39a72656af9babc80a27a6"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"13af4d0681d751179247e979d7cabe6f","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"79df432f9f55cb6bf63e4c69b32c8bee","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07","tests\/AttributeBagAttributeHelperTest.php":"2055123024e7e1e3cd705f13ee760b61","tests\/AttributeBagAttributeTest.php":"174797353c39a72656af9babc80a27a6"}} \ No newline at end of file From 51f6efdeb9b6c6357e2628f690f668d9e923cbc6 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Mon, 18 Dec 2023 16:18:05 +0000 Subject: [PATCH 16/50] Introduce conditionals to attributes --- src/AttributeBuilder.php | 40 ++++++++- tests/AttributeBagAttributeHelperTest.php | 13 ++- tests/AttributeBagAttributeTest.php | 12 +-- tests/AttributeBagClassTest.php | 6 +- tests/AttributeBagConditionalsTest.php | 103 ++++++++++++++++++++++ tests/Pest.php | 6 ++ 6 files changed, 162 insertions(+), 18 deletions(-) create mode 100644 tests/AttributeBagConditionalsTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 2d9d5fc..0965d4f 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -22,6 +22,8 @@ class AttributeBuilder */ protected static $attributeHelpers = []; + protected $conditionals = []; + public function __construct( protected ComponentAttributeBag &$attributeBag, protected Collection $options @@ -122,8 +124,30 @@ public function removeClass(...$classes): AttributeBuilder * @param mixed $value * @return AttributeBuilder */ - public function setAttribute($attribute, $value = null, $attributeType = null): AttributeBuilder + public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder { + // if we have a conditional + if ($condition != null) { + // check if we have a condition that exists in the helpers + if (is_string($condition)) { + $condition = $this->conditionals[$condition]; + } + + // evaluate the conditional + $conditionResult = $condition(); + + // negate the result of the conditional if we need to + if ($negateCondition) { + $conditionResult = !$conditionResult; + } + + // check that the conditional isn't false + if (!$conditionResult) { + // if it is, just return the fluent API + return $this; + } + } + // check if we have an attribute type if ($attributeType != null) { if (!array_key_exists($attributeType, self::$attributeHelpers)) { @@ -201,6 +225,13 @@ public function mergeAttributes($attributes): AttributeBuilder return $this; } + public function registerConditional($name, callable $callable) + { + $this->conditionals[$name] = $callable; + + return $this; + } + /** * Return the underlying component attribute bag instance * @@ -245,6 +276,11 @@ public function __call($method, $parameters) ' . $attributeTypesString . ' (?P[A-Za-z0-9]*)? (?PAttribute|Class) + ( + (If|On|When) + (?PNot)? + (?P[A-Za-z0-9]*) + )? /x '; @@ -252,7 +288,7 @@ public function __call($method, $parameters) $magicMethodRegexMatches = []; if (preg_match($magicMethodRegex, $method, $magicMethodRegexMatches)) { - $magicMethodParameterNames = ['attribute', 'value', 'attributeType']; + $magicMethodParameterNames = ['attribute', 'value', 'attributeType', 'condition']; // we alias the add operation to set if ($magicMethodRegexMatches['operation'] == 'add') { diff --git a/tests/AttributeBagAttributeHelperTest.php b/tests/AttributeBagAttributeHelperTest.php index 2676b7b..e38b288 100644 --- a/tests/AttributeBagAttributeHelperTest.php +++ b/tests/AttributeBagAttributeHelperTest.php @@ -1,6 +1,5 @@ setAttribute('foo', 'bar', 'data'); + $attributeBuilder->setAttribute('foo', 'bar', 'data'); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('data-foo', 'bar'); @@ -23,7 +22,7 @@ addDataAttributeHelperToAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->setDataAttribute('foo', 'bar'); + $attributeBuilder->setDataAttribute('foo', 'bar'); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('data-foo', 'bar'); @@ -37,7 +36,7 @@ addDataAttributeHelperToAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->setDataFooAttribute('bar'); + $attributeBuilder->setDataFooAttribute('bar'); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('data-foo', 'bar'); @@ -51,7 +50,7 @@ addDataAttributeHelperToAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeAttribute('foo', 'data'); + $attributeBuilder->removeAttribute('foo', 'data'); // check that the classes are correct expect($attributeBuilder->getAttributes())->not()->toHaveKey('data-foo'); @@ -65,7 +64,7 @@ addDataAttributeHelperToAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeDataAttribute('foo'); + $attributeBuilder->removeDataAttribute('foo'); // check that the classes are correct expect($attributeBuilder->getAttributes())->not()->toHaveKey('data-foo'); @@ -79,7 +78,7 @@ addDataAttributeHelperToAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeDataFooAttribute(); + $attributeBuilder->removeDataFooAttribute(); // check that the classes are correct expect($attributeBuilder->getAttributes())->not()->toHaveKey('data-foo'); diff --git a/tests/AttributeBagAttributeTest.php b/tests/AttributeBagAttributeTest.php index 14ed4f3..c8d4299 100644 --- a/tests/AttributeBagAttributeTest.php +++ b/tests/AttributeBagAttributeTest.php @@ -5,7 +5,7 @@ $attributeBuilder = createAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->setAttribute('foo', 'bar'); + $attributeBuilder->setAttribute('foo', 'bar'); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bar'); @@ -16,7 +16,7 @@ $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->setAttribute('foo', 'bat'); + $attributeBuilder->setAttribute('foo', 'bat'); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bat'); @@ -27,7 +27,7 @@ $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeAttribute('foo'); + $attributeBuilder->removeAttribute('foo'); // check that the classes are correct expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo'); @@ -38,7 +38,7 @@ $attributeBuilder = createAttributeBuilder(['foo' => 'bar', 'bat' => 'buz']); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeAttribute(['foo', 'bat']); + $attributeBuilder->removeAttribute(['foo', 'bat']); // check that the classes are correct expect($attributeBuilder->getAttributes())->not()->toHaveKeys(['foo', 'bat']); @@ -49,7 +49,7 @@ $attributeBuilder = createAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->setFooAttribute('bar'); + $attributeBuilder->setFooAttribute('bar'); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('foo', 'bar'); @@ -60,7 +60,7 @@ $attributeBuilder = createAttributeBuilder(['foo' => 'bar']); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeFooAttribute(); + $attributeBuilder->removeFooAttribute(); // check that the classes are correct expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo'); diff --git a/tests/AttributeBagClassTest.php b/tests/AttributeBagClassTest.php index c004a4e..9411a36 100644 --- a/tests/AttributeBagClassTest.php +++ b/tests/AttributeBagClassTest.php @@ -20,7 +20,7 @@ $attributeBuilder = createAttributeBuilder(); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->addClass($data); + $attributeBuilder->addClass($data); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5'); @@ -31,7 +31,7 @@ $attributeBuilder = createAttributeBuilder(['class' => 'class-6']); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->addClass($data); + $attributeBuilder->addClass($data); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-1 class-2 class-3 class-4 class-5 class-6'); @@ -42,7 +42,7 @@ $attributeBuilder = createAttributeBuilder(['class' => 'class-1 class-2 class-3 class-4 class-5 class-6']); // add the class to the attribute builder - $attributeBuilder = $attributeBuilder->removeClass($data); + $attributeBuilder->removeClass($data); // check that the classes are correct expect($attributeBuilder->getAttributes())->toHaveKey('class', 'class-6'); diff --git a/tests/AttributeBagConditionalsTest.php b/tests/AttributeBagConditionalsTest.php new file mode 100644 index 0000000..d071ffe --- /dev/null +++ b/tests/AttributeBagConditionalsTest.php @@ -0,0 +1,103 @@ +setAttribute('foo', 'bar', condition: fn () => true) + ->setAttribute('bat', 'ball', condition: fn () => false); + + // check that the classes are correct + expect($attributeBuilder->getAttributes()) + ->toHaveKey('foo', 'bar') + ->not()->toHaveKey('bat', 'ball'); +}); + +it('can have conditionals specified as helpers', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the conditional helpers + addConditionalHelpersToAttributeBuilder($attributeBuilder); + + // add the class to the attribute builder + $attributeBuilder + ->setAttribute('foo', 'bar', condition: 'true') + ->setAttribute('bat', 'ball', condition: 'false'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes()) + ->toHaveKey('foo', 'bar') + ->not()->toHaveKey('bat', 'ball'); +}); + +it('can use conditionals in magic methods', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the conditional helpers + addConditionalHelpersToAttributeBuilder($attributeBuilder); + + // add the class to the attribute builder + $attributeBuilder + ->setAttributeIfTrue('foo', 'bar') + ->setAttributeIfFalse('bat', 'ball'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes()) + ->toHaveKey('foo', 'bar') + ->not()->toHaveKey('bat', 'ball'); +}); + +it('can specify a negated condition to apply an attribute', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the class to the attribute builder + $attributeBuilder + ->setAttribute('foo', 'bar', condition: fn () => false, negateCondition: true) + ->setAttribute('bat', 'ball', condition: fn () => true, negateCondition: true); + + // check that the classes are correct + expect($attributeBuilder->getAttributes()) + ->toHaveKey('foo', 'bar') + ->not()->toHaveKey('bat', 'ball'); +}); + +it('can have negated conditionals specified as helpers', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the conditional helpers + addConditionalHelpersToAttributeBuilder($attributeBuilder); + + // add the class to the attribute builder + $attributeBuilder + ->setAttribute('foo', 'bar', condition: 'false', negateCondition: true) + ->setAttribute('bat', 'ball', condition: 'true', negateCondition: true); + + // check that the classes are correct + expect($attributeBuilder->getAttributes()) + ->toHaveKey('foo', 'bar') + ->not()->toHaveKey('bat', 'ball'); +}); + +it('can use negated conditionals in magic methods', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // add the conditional helpers + addConditionalHelpersToAttributeBuilder($attributeBuilder); + + // add the class to the attribute builder + $attributeBuilder + ->setAttributeIfNotFalse('foo', 'bar') + ->setAttributeIfNotTrue('bat', 'ball'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes()) + ->toHaveKey('foo', 'bar') + ->not()->toHaveKey('bat', 'ball'); +}); diff --git a/tests/Pest.php b/tests/Pest.php index d478a0e..a061b40 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -64,3 +64,9 @@ function addDataAttributeHelperToAttributeBuilder() return ['data-' . $attribute => $value]; }); } + +function addConditionalHelpersToAttributeBuilder(AttributeBuilder $attributeBuilder) +{ + $attributeBuilder->registerConditional('true', fn () => true); + $attributeBuilder->registerConditional('false', fn () => false); +} From bf89006b1026efbb924ba97929c13f5ed50d5f94 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Mon, 18 Dec 2023 16:18:49 +0000 Subject: [PATCH 17/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 9788782..0cd203f 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"13af4d0681d751179247e979d7cabe6f","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"79df432f9f55cb6bf63e4c69b32c8bee","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"3d4cb742577ee5bcede5e15358a63e07","tests\/AttributeBagAttributeHelperTest.php":"2055123024e7e1e3cd705f13ee760b61","tests\/AttributeBagAttributeTest.php":"174797353c39a72656af9babc80a27a6"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"a09ce008024fbee1bc8edeaede0e23bd","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file From 3d7e950a8f5e8fc991966c70c26ea7d686b48327 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Mon, 18 Dec 2023 16:26:52 +0000 Subject: [PATCH 18/50] Refactor --- src/AttributeBuilder.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 0965d4f..9b3cd34 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -300,7 +300,6 @@ public function __call($method, $parameters) // the methods that we are allowed to call via the magic method $allowedMethods = [ - 'addAttribute', 'setAttribute', 'removeAttribute', ]; @@ -320,12 +319,6 @@ public function __call($method, $parameters) // get the name of the parameter $parameterName = $callableParameter->getName(); - // check if it is variadic - if ($callableParameter->isVariadic()) { - // if it is, then the name is going to be the singular name of the method - $parameterName = Str::of($parameterName)->singular()->__toString(); - } - // check if we have a matching parameter in the regex matches if (array_key_exists($parameterName, $magicMethodRegexMatches) && !empty($magicMethodRegexMatches[$parameterName])) { // if we do, we get that value and set it on the array that will be passed to the method From c623b329853b350a5c1132a133f539f25c40bc86 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Mon, 18 Dec 2023 16:27:06 +0000 Subject: [PATCH 19/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 0cd203f..36db1a4 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"a09ce008024fbee1bc8edeaede0e23bd","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"ffde4e272b11d2e957720dbf542970aa","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file From 3e2179c5cb483ff6ec22a848b272882e584a51a4 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Mon, 18 Dec 2023 16:56:30 +0000 Subject: [PATCH 20/50] Remove Attribute method --- src/AttributeBuilder.php | 49 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 9b3cd34..ba79ca3 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -22,7 +22,11 @@ class AttributeBuilder */ protected static $attributeHelpers = []; - protected $conditionals = []; + /** + * An array to store the registered conditional helpers + * @var array + */ + protected $conditionalHelpers = []; public function __construct( protected ComponentAttributeBag &$attributeBag, @@ -122,7 +126,10 @@ public function removeClass(...$classes): AttributeBuilder * * @param mixed $attribute * @param mixed $value + * @param mixed $condition + * @param bool $negateCondition * @return AttributeBuilder + * @throws RuntimeException */ public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder { @@ -130,7 +137,7 @@ public function setAttribute($attribute, $value = null, $attributeType = null, $ if ($condition != null) { // check if we have a condition that exists in the helpers if (is_string($condition)) { - $condition = $this->conditionals[$condition]; + $condition = $this->conditionalHelpers[$condition]; } // evaluate the conditional @@ -165,13 +172,45 @@ public function setAttribute($attribute, $value = null, $attributeType = null, $ $this->offsetSet($attribute, $value); } - // return a fluent API return $this; } - public function removeAttribute($attribute, $attributeType = null): AttributeBuilder + /** + * Remove an attribute from the attribute bag + * + * @param mixed $attribute + * @param mixed $attributeType + * @param mixed $condition + * @param bool $negateCondition + * @return AttributeBuilder + * @throws RuntimeException + */ + public function removeAttribute($attribute, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder { + // if we have a conditional + if ($condition != null) { + // check if we have a condition that exists in the helpers + if (is_string($condition)) { + $condition = $this->conditionalHelpers[$condition]; + } + + // evaluate the conditional + $conditionResult = $condition(); + + // negate the result of the conditional if we need to + if ($negateCondition) { + $conditionResult = !$conditionResult; + } + + // check that the conditional isn't false + if (!$conditionResult) { + // if it is, just return the fluent API + return $this; + } + } + + // we need to make sure that the attribute are an array $attributeArray = (array) $attribute; foreach ($attributeArray as $attribute) { @@ -227,7 +266,7 @@ public function mergeAttributes($attributes): AttributeBuilder public function registerConditional($name, callable $callable) { - $this->conditionals[$name] = $callable; + $this->conditionalHelpers[$name] = $callable; return $this; } From d978788bab8013eb12e72989723675f272ed3d35 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Mon, 18 Dec 2023 16:56:49 +0000 Subject: [PATCH 21/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 36db1a4..2bd8c60 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"ffde4e272b11d2e957720dbf542970aa","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"54556518523fe8a61eba8c68a09e156f","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file From d2a3284e4c304409c0736e14b2f66cfd969b0ebf Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Tue, 19 Dec 2023 07:51:29 +0000 Subject: [PATCH 22/50] yup --- src/AttributeBuilder.php | 97 +++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index ba79ca3..0340ae4 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -133,26 +133,9 @@ public function removeClass(...$classes): AttributeBuilder */ public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder { - // if we have a conditional - if ($condition != null) { - // check if we have a condition that exists in the helpers - if (is_string($condition)) { - $condition = $this->conditionalHelpers[$condition]; - } - - // evaluate the conditional - $conditionResult = $condition(); - - // negate the result of the conditional if we need to - if ($negateCondition) { - $conditionResult = !$conditionResult; - } - - // check that the conditional isn't false - if (!$conditionResult) { - // if it is, just return the fluent API - return $this; - } + // if we have a conditional, we need to check if it passes + if ($condition != null && !$this->conditionalPasses($condition, $negateCondition)) { + return $this; } // check if we have an attribute type @@ -188,28 +171,25 @@ public function setAttribute($attribute, $value = null, $attributeType = null, $ */ public function removeAttribute($attribute, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder { - // if we have a conditional - if ($condition != null) { - // check if we have a condition that exists in the helpers - if (is_string($condition)) { - $condition = $this->conditionalHelpers[$condition]; - } + // if we have a conditional, we need to check if it passes + if ($condition != null && !$this->conditionalPasses($condition, $negateCondition)) { + return $this; + } - // evaluate the conditional - $conditionResult = $condition(); + $attribute = (array) $attribute; - // negate the result of the conditional if we need to - if ($negateCondition) { - $conditionResult = !$conditionResult; - } + $attribute = array_fill_keys($attribute, null); - // check that the conditional isn't false - if (!$conditionResult) { - // if it is, just return the fluent API - return $this; - } + dump($attribute); + + dd($this->parseAttributeArray($attribute, $attributeType)); + + foreach ($this->parseAttributeArray($attribute, $attributeType) as $attribute) { + $this->offsetUnset($attribute); } + return $this; + // we need to make sure that the attribute are an array $attributeArray = (array) $attribute; @@ -264,6 +244,49 @@ public function mergeAttributes($attributes): AttributeBuilder return $this; } + protected function parseAttributeArray($attributes, $attributeType) + { + $parsedAttributes = []; + + $attributes = (array) $attributes; + + foreach ($attributes as $attribute => $value) { + // remove each of the attributes from the attribute bag + if ($attributeType != null) { + if (!array_key_exists($attributeType, self::$attributeHelpers)) { + throw new RuntimeException('No such attribute helper ' . $attributeType); + } + + // if we do, we need to pass it through the callback + $attributes = self::$attributeHelpers[$attributeType]($attribute, $value); + + $parsedAttributes[] = $attributes; + } else { + $parsedAttributes[$attribute] = $value; + } + } + + return Arr::flatten($parsedAttributes); + } + + protected function conditionalPasses($condition, $negateCondition) + { + // check if we have a condition that exists in the helpers + if (is_string($condition)) { + $condition = $this->conditionalHelpers[$condition]; + } + + // evaluate the conditional + $conditionResult = $condition(); + + // negate the result of the conditional if we need to + if ($negateCondition) { + $conditionResult = !$conditionResult; + } + + return $conditionResult; + } + public function registerConditional($name, callable $callable) { $this->conditionalHelpers[$name] = $callable; From ec5f3c2286bd0516b49ad36f6b2e413a168011e1 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Tue, 19 Dec 2023 07:51:45 +0000 Subject: [PATCH 23/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 2bd8c60..f1b7429 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"54556518523fe8a61eba8c68a09e156f","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"ff48ccb9f872b167597f06ad3c058738","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file From 559113e3216bb87f8e796346decaad462a843113 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Tue, 19 Dec 2023 09:18:17 +0000 Subject: [PATCH 24/50] Pull out repeating functionality --- src/AttributeBuilder.php | 98 +++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 0340ae4..fb1adc1 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -42,7 +42,7 @@ public function __construct( * @param callable $callback * @return void */ - public static function registerAttributeHelper(string $name, callable $callback) + public static function registerAttributeHelper(string $name, callable $callback): void { self::$attributeHelpers[$name] = $callback; } @@ -138,20 +138,8 @@ public function setAttribute($attribute, $value = null, $attributeType = null, $ return $this; } - // check if we have an attribute type - if ($attributeType != null) { - if (!array_key_exists($attributeType, self::$attributeHelpers)) { - throw new RuntimeException('No such attribute helper ' . $attributeType); - } - - // if we do, we need to pass it through the callback - $attributes = self::$attributeHelpers[$attributeType]($attribute, $value); - - foreach ($attributes as $attribute => $value) { - $this->setAttribute($attribute, $value); - } - } else { - // set the attribute on the attribute bag + // loop through each of the attributes that we need to remove + foreach ($this->formatAttributes([$attribute => $value], $attributeType) as $attribute => $value) { $this->offsetSet($attribute, $value); } @@ -176,41 +164,18 @@ public function removeAttribute($attribute, $attributeType = null, $condition = return $this; } + // make sure that the attributes are an array $attribute = (array) $attribute; + // and then fill them with null $attribute = array_fill_keys($attribute, null); - dump($attribute); - - dd($this->parseAttributeArray($attribute, $attributeType)); - - foreach ($this->parseAttributeArray($attribute, $attributeType) as $attribute) { + // loop through each of the attributes that we need to remove + foreach ($this->formatAttributes($attribute, $attributeType) as $attribute => $value) { + // and remove it $this->offsetUnset($attribute); } - return $this; - - // we need to make sure that the attribute are an array - $attributeArray = (array) $attribute; - - foreach ($attributeArray as $attribute) { - // remove each of the attributes from the attribute bag - if ($attributeType != null) { - if (!array_key_exists($attributeType, self::$attributeHelpers)) { - throw new RuntimeException('No such attribute helper ' . $attributeType); - } - - // if we do, we need to pass it through the callback - $attributes = self::$attributeHelpers[$attributeType]($attribute, null); - - foreach (array_keys($attributes) as $attribute) { - $this->offsetUnset($attribute); - } - } else { - $this->offsetUnset($attribute); - } - } - // return a fluent API return $this; } @@ -244,32 +209,56 @@ public function mergeAttributes($attributes): AttributeBuilder return $this; } - protected function parseAttributeArray($attributes, $attributeType) + /** + * Format attributes, optionally passing them through an attribute helper + * + * @param array $attributes + * @param string $attributeType + * @return array + */ + protected function formatAttributes($attributes, $attributeType): array { - $parsedAttributes = []; + // an array to store the formatted attributes + $formattedAttributes = []; + // ensure that the attributes are an array (it's possible only one has been passed) $attributes = (array) $attributes; + // loop through all of the attributes foreach ($attributes as $attribute => $value) { // remove each of the attributes from the attribute bag if ($attributeType != null) { + // check that we do have an appropriate helper if (!array_key_exists($attributeType, self::$attributeHelpers)) { throw new RuntimeException('No such attribute helper ' . $attributeType); } // if we do, we need to pass it through the callback - $attributes = self::$attributeHelpers[$attributeType]($attribute, $value); + $attributeHelperResults = self::$attributeHelpers[$attributeType]($attribute, $value); - $parsedAttributes[] = $attributes; + // loop through the result of the helper, it's possible the helper sets multiple attributes + foreach ($attributeHelperResults as $attribute => $value) { + // and add it to the formatted attributes array + $formattedAttributes[$attribute] = $value; + } } else { - $parsedAttributes[$attribute] = $value; + // this is just a like for like + $formattedAttributes[$attribute] = $value; } } - return Arr::flatten($parsedAttributes); + // return the formatted attributes + return $formattedAttributes; } - protected function conditionalPasses($condition, $negateCondition) + /** + * Check that a conditional helper passes + * + * @param string|callable $condition + * @param boolean $negateCondition + * @return boolean + */ + protected function conditionalPasses(string|callable $condition, bool $negateCondition): bool { // check if we have a condition that exists in the helpers if (is_string($condition)) { @@ -287,7 +276,14 @@ protected function conditionalPasses($condition, $negateCondition) return $conditionResult; } - public function registerConditional($name, callable $callable) + /** + * Register a conditional + * + * @param string $name + * @param callable $callable + * @return AttributeBuilder + */ + public function registerConditional(string $name, callable $callable): AttributeBuilder { $this->conditionalHelpers[$name] = $callable; From 26369ba5dcd238f896d3da474470b10b1ea61e64 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Tue, 19 Dec 2023 09:18:57 +0000 Subject: [PATCH 25/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- src/AttributeBuilder.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index f1b7429..dd8d307 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"ff48ccb9f872b167597f06ad3c058738","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"462776b9e00e458ca1d02c8d67a096b3","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index fb1adc1..6b74fcb 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -255,8 +255,8 @@ protected function formatAttributes($attributes, $attributeType): array * Check that a conditional helper passes * * @param string|callable $condition - * @param boolean $negateCondition - * @return boolean + * @param bool $negateCondition + * @return bool */ protected function conditionalPasses(string|callable $condition, bool $negateCondition): bool { From f0ba58443f71d9c879f8672ceb79214d93f1c022 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Tue, 19 Dec 2023 09:46:47 +0000 Subject: [PATCH 26/50] Refactor --- src/AttributeBuilder.php | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index fb1adc1..80d7c19 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -216,35 +216,30 @@ public function mergeAttributes($attributes): AttributeBuilder * @param string $attributeType * @return array */ - protected function formatAttributes($attributes, $attributeType): array + protected function formatAttributes($attributes, $attributeType = null): array { - // an array to store the formatted attributes - $formattedAttributes = []; - // ensure that the attributes are an array (it's possible only one has been passed) $attributes = (array) $attributes; + // if we don't have an attribute type + if ($attributeType == null) { + // then we just return the attributes as they are + return $attributes; + } elseif (!array_key_exists($attributeType, self::$attributeHelpers)) { + // if we don't have a matching helper, then throw an exception + throw new RuntimeException('No such attribute helper ' . $attributeType); + } + + // an array to store the formatted attributes + $formattedAttributes = []; + // loop through all of the attributes foreach ($attributes as $attribute => $value) { - // remove each of the attributes from the attribute bag - if ($attributeType != null) { - // check that we do have an appropriate helper - if (!array_key_exists($attributeType, self::$attributeHelpers)) { - throw new RuntimeException('No such attribute helper ' . $attributeType); - } + // if we do, we need to pass it through the callback + $attributeHelperResults = self::$attributeHelpers[$attributeType]($attribute, $value); - // if we do, we need to pass it through the callback - $attributeHelperResults = self::$attributeHelpers[$attributeType]($attribute, $value); - - // loop through the result of the helper, it's possible the helper sets multiple attributes - foreach ($attributeHelperResults as $attribute => $value) { - // and add it to the formatted attributes array - $formattedAttributes[$attribute] = $value; - } - } else { - // this is just a like for like - $formattedAttributes[$attribute] = $value; - } + // merge in the result of the helper, it's possible the helper sets multiple attributes + $formattedAttributes = $formattedAttributes + $attributeHelperResults; } // return the formatted attributes From 15862f476784d03fc4fe5e249504118d1ae4b833 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Tue, 19 Dec 2023 09:47:04 +0000 Subject: [PATCH 27/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index dd8d307..2cac35d 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"462776b9e00e458ca1d02c8d67a096b3","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file From da55253a2bcc99913a75db02a31aaf9e4cea1e6f Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Tue, 19 Dec 2023 13:57:22 +0000 Subject: [PATCH 28/50] Upgrade Pest --- composer.json | 4 ++-- phpunit.xml.dist | 47 +++++++++++++++++++---------------------------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index 7e373a0..6a28391 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ "require-dev": { "laravel/legacy-factories": "^1.1", "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", - "phpunit/phpunit": "^9.6", - "pestphp/pest": "^1.23" + "phpunit/phpunit": "^10.5", + "pestphp/pest": "^2.28" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7dbb5d4..9bbaa58 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,30 +1,21 @@ - - - - tests - - - - - - - - src - - - - - + + + + tests + + + + + + + + + + + + + src + + From afb2edb933469ff6fb320036ea595e8b05c964af Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Tue, 19 Dec 2023 17:16:33 +0000 Subject: [PATCH 29/50] Revert "Upgrade Pest" This reverts commit da55253a2bcc99913a75db02a31aaf9e4cea1e6f. --- composer.json | 4 ++-- phpunit.xml.dist | 47 ++++++++++++++++++++++++++++------------------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index 6a28391..7e373a0 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ "require-dev": { "laravel/legacy-factories": "^1.1", "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0", - "phpunit/phpunit": "^10.5", - "pestphp/pest": "^2.28" + "phpunit/phpunit": "^9.6", + "pestphp/pest": "^1.23" }, "autoload": { "psr-4": { diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 9bbaa58..7dbb5d4 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,21 +1,30 @@ - - - - tests - - - - - - - - - - - - - src - - + + + + tests + + + + + + + + src + + + + + From e78c76b57c4d28a6ff831aa8aa9e34764434821f Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Tue, 19 Dec 2023 17:19:32 +0000 Subject: [PATCH 30/50] Setup tests properly --- tests/Pest.php | 2 +- tests/TestCase.php | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/Pest.php b/tests/Pest.php index a061b40..fa8268c 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -14,7 +14,7 @@ | */ -uses(AppKit\UI\Tests\TestCase::class); +uses(AppKit\UI\Tests\TestCase::class)->in('.'); /* |-------------------------------------------------------------------------- diff --git a/tests/TestCase.php b/tests/TestCase.php index 7fef7e5..2398b90 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,10 +4,13 @@ use AppKit\UI\Facades\Ui; use AppKit\UI\UiServiceProvider; +use Illuminate\Foundation\Testing\Concerns\InteractsWithViews; use Orchestra\Testbench\TestCase as OrchestraTestCase; class TestCase extends OrchestraTestCase { + use InteractsWithViews; + /** * Setup the test environment */ From 9f57e98a205afc4516942a08f31a3f02d3c49fd1 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Tue, 19 Dec 2023 17:19:50 +0000 Subject: [PATCH 31/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 2cac35d..932b4a8 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"c604a4e71fc4eb4fd33fea3467aadf9a","tests\/TestCase.php":"50f792d241f3c09167cfc633cdb33365","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file From 094c172ca6cbb420686c2416b1ea778c1846e29e Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 09:11:15 +0000 Subject: [PATCH 32/50] Setup test component --- tests/ComponentIntegrationTest.php | 13 ++++++++++++ tests/Components/HigherOrderTestComponent.php | 21 +++++++++++++++++++ tests/Components/TestComponent.php | 20 ++++++++++++++++++ tests/Pest.php | 2 ++ 4 files changed, 56 insertions(+) create mode 100644 tests/ComponentIntegrationTest.php create mode 100644 tests/Components/HigherOrderTestComponent.php create mode 100644 tests/Components/TestComponent.php diff --git a/tests/ComponentIntegrationTest.php b/tests/ComponentIntegrationTest.php new file mode 100644 index 0000000..2733129 --- /dev/null +++ b/tests/ComponentIntegrationTest.php @@ -0,0 +1,13 @@ +blade(''); + + expect($blade)->not()->toBeEmpty(); +}); diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php new file mode 100644 index 0000000..d2a3c6f --- /dev/null +++ b/tests/Components/HigherOrderTestComponent.php @@ -0,0 +1,21 @@ + Date: Wed, 20 Dec 2023 09:11:33 +0000 Subject: [PATCH 33/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- tests/Components/HigherOrderTestComponent.php | 2 +- tests/Pest.php | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 932b4a8..76186d4 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"15efaa3bdbf88813c1d5e8288c6233f2","tests\/Components\/TestComponent.php":"87a27ebd27eb10f6eb24129251d21b37"}} \ No newline at end of file diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php index d2a3c6f..6e74f2d 100644 --- a/tests/Components/HigherOrderTestComponent.php +++ b/tests/Components/HigherOrderTestComponent.php @@ -8,7 +8,7 @@ class HigherOrderTestComponent extends TestComponent * The last instantiated instance * @var $this */ - static $lastInstance = null; + public static $lastInstance = null; public function __construct() { diff --git a/tests/Pest.php b/tests/Pest.php index e75ada7..fa8268c 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,8 +1,6 @@ Date: Wed, 20 Dec 2023 09:12:01 +0000 Subject: [PATCH 34/50] Remove debug statement --- tests/Components/TestComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index 39b5959..832300e 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -10,7 +10,7 @@ class TestComponent extends Component public function __construct() { - dump('HELLO!!!'); + } public function render() From 8d599ab30fd49e3ed642158684d5eebe7eb80c34 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 20 Dec 2023 09:12:16 +0000 Subject: [PATCH 35/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 76186d4..d3ec5c3 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"15efaa3bdbf88813c1d5e8288c6233f2","tests\/Components\/TestComponent.php":"87a27ebd27eb10f6eb24129251d21b37"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"15efaa3bdbf88813c1d5e8288c6233f2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758"}} \ No newline at end of file From 4dfcda861509f10792fe8d5717e3badb46ac0a8e Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 09:13:36 +0000 Subject: [PATCH 36/50] document --- tests/Components/HigherOrderTestComponent.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php index 6e74f2d..8de92ec 100644 --- a/tests/Components/HigherOrderTestComponent.php +++ b/tests/Components/HigherOrderTestComponent.php @@ -12,9 +12,12 @@ class HigherOrderTestComponent extends TestComponent public function __construct() { + // store the last instance static::$lastInstance = $this; + // check if we are extending a class that has a constructor if (method_exists(parent::class, '__construct')) { + // call the parent constructor with all of the arguments passed parent::__construct(...func_get_args()); } } From 6f1b5681ffeb9139e5f5ecb9e180e67ce52fe137 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 20 Dec 2023 09:13:53 +0000 Subject: [PATCH 37/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index d3ec5c3..71e952a 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"15efaa3bdbf88813c1d5e8288c6233f2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"f91501fa3cb3f07e6dc209c05ef44ab2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758"}} \ No newline at end of file From 09b7053621d09553f3659838457ca1556fb6dcff Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 12:32:40 +0000 Subject: [PATCH 38/50] Allow multiple attribute bags --- src/AttributeBuilder.php | 51 ++++++++++++++++++++++++------ tests/MultipleAttributeBagTest.php | 29 +++++++++++++++++ tests/Pest.php | 4 +-- 3 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 tests/MultipleAttributeBagTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 8dc46b4..478f5cf 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -28,11 +28,22 @@ class AttributeBuilder */ protected $conditionalHelpers = []; + /** + * An array of attribute bags for each of the registered elements + * @var array + */ + protected $elementAttributeBags = []; + public function __construct( protected ComponentAttributeBag &$attributeBag, - protected Collection $options + protected Collection $options, + array $elements ) { - + // loop through each of the elements that have been specified + foreach ($elements as $element) { + // create a new attribute bag for that element + $this->elementAttributeBags[$element] = new ComponentAttributeBag(); + } } /** @@ -131,7 +142,7 @@ public function removeClass(...$classes): AttributeBuilder * @return AttributeBuilder * @throws RuntimeException */ - public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder + public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false, $element = null): AttributeBuilder { // if we have a conditional, we need to check if it passes if ($condition != null && !$this->conditionalPasses($condition, $negateCondition)) { @@ -140,7 +151,7 @@ public function setAttribute($attribute, $value = null, $attributeType = null, $ // loop through each of the attributes that we need to remove foreach ($this->formatAttributes([$attribute => $value], $attributeType) as $attribute => $value) { - $this->offsetSet($attribute, $value); + $this->getAttributeBag($element)->offsetSet($attribute, $value); } // return a fluent API @@ -157,7 +168,7 @@ public function setAttribute($attribute, $value = null, $attributeType = null, $ * @return AttributeBuilder * @throws RuntimeException */ - public function removeAttribute($attribute, $attributeType = null, $condition = null, $negateCondition = false): AttributeBuilder + public function removeAttribute($attribute, $attributeType = null, $condition = null, $negateCondition = false, $element = null): AttributeBuilder { // if we have a conditional, we need to check if it passes if ($condition != null && !$this->conditionalPasses($condition, $negateCondition)) { @@ -173,7 +184,7 @@ public function removeAttribute($attribute, $attributeType = null, $condition = // loop through each of the attributes that we need to remove foreach ($this->formatAttributes($attribute, $attributeType) as $attribute => $value) { // and remove it - $this->offsetUnset($attribute); + $this->getAttributeBag($element)->offsetUnset($attribute); } // return a fluent API @@ -290,8 +301,12 @@ public function registerConditional(string $name, callable $callable): Attribute * * @return ComponentAttributeBag */ - public function getAttributeBag(): ComponentAttributeBag + public function getAttributeBag(?string $element = null): ComponentAttributeBag { + if ($element) { + return $this->elementAttributeBags[$element]; + } + return $this->attributeBag; } @@ -322,6 +337,23 @@ public function __call($method, $parameters) $attributeTypesString = '(?P' . implode('|', $attributeTypes) . ')?'; } + // we also need to build up the regex that we are going to use for the element + $attributeBagString = ''; + + // if we have any element attribute bags + if (!empty($this->elementAttributeBags)) { + // create an array to store all of the names + $elementAttributeBags = []; + + foreach ($this->elementAttributeBags as $name => $elementAttributeBag) { + // pull out each of the element names in study case + $elementAttributeBags[] = Str::of($name)->studly()->__toString(); + } + + // create the string that will be used in the regex + $attributeBagString = '((To|On|From)(?P' . implode('|', $elementAttributeBags) . '))?'; + } + // create the regex $magicMethodRegex = ' / @@ -329,8 +361,9 @@ public function __call($method, $parameters) ' . $attributeTypesString . ' (?P[A-Za-z0-9]*)? (?PAttribute|Class) + ' . $attributeBagString . ' ( - (If|On|When) + (If|When) (?PNot)? (?P[A-Za-z0-9]*) )? @@ -341,7 +374,7 @@ public function __call($method, $parameters) $magicMethodRegexMatches = []; if (preg_match($magicMethodRegex, $method, $magicMethodRegexMatches)) { - $magicMethodParameterNames = ['attribute', 'value', 'attributeType', 'condition']; + $magicMethodParameterNames = ['attribute', 'value', 'attributeType', 'condition', 'negateCondition', 'element']; // we alias the add operation to set if ($magicMethodRegexMatches['operation'] == 'add') { diff --git a/tests/MultipleAttributeBagTest.php b/tests/MultipleAttributeBagTest.php new file mode 100644 index 0000000..b2c7a34 --- /dev/null +++ b/tests/MultipleAttributeBagTest.php @@ -0,0 +1,29 @@ +setAttribute('foo', 'bar', element: 'label'); + + // check that the attribute is in the label attribute bag + expect($attributeBuilder->getAttributeBag('label')->getAttributes())->toHaveKey('foo', 'bar'); + + // check that the attribute has not been added to the default attribute bag + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); +}); + +it('can use magic methods to add to a different attribute bag', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(elements: ['label']); + + // add the class to the attribute builder + $attributeBuilder->setAttributeOnLabel('foo', 'bar'); + + // check that the attribute is in the label attribute bag + expect($attributeBuilder->getAttributeBag('label')->getAttributes())->toHaveKey('foo', 'bar'); + + // check that the attribute has not been added to the default attribute bag + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); +}); diff --git a/tests/Pest.php b/tests/Pest.php index fa8268c..3e65c44 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -49,13 +49,13 @@ * @param array $options * @return AttributeBuilder */ -function createAttributeBuilder($attributes = [], $options = []) +function createAttributeBuilder($attributes = [], $options = [], $elements = []) { // create an attribute bag that will be passed to the attribute builder $attributeBag = new ComponentAttributeBag($attributes); // return the attribute builder - return new AttributeBuilder($attributeBag, collect($options)); + return new AttributeBuilder($attributeBag, collect($options), $elements); } function addDataAttributeHelperToAttributeBuilder() From 7a060d7f392c89a388c580516878325367f0477a Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 20 Dec 2023 12:32:57 +0000 Subject: [PATCH 39/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 71e952a..4c32d82 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"200d24525a6f5ee9063ada882ab3e4b1","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"38e4b73a6086e30874b87b1e123815d7","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"f91501fa3cb3f07e6dc209c05ef44ab2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"d01a3a44e9ab1f149961c4a99341b243","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"0f27a1c27c1a4c6d5fa0c4450e20e2fa","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"f91501fa3cb3f07e6dc209c05ef44ab2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758","tests\/MultipleAttributeBagTest.php":"a81165935e9c4ca620523115f265116e"}} \ No newline at end of file From 1b459df83ea784064f0385f301bca5912f94838b Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 12:34:20 +0000 Subject: [PATCH 40/50] allow attributes to be defined with a prefix --- src/AttributeBuilder.php | 21 ++++++++++++++++ tests/MultipleAttributeBagTest.php | 39 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 478f5cf..0fbbddd 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -43,6 +43,27 @@ public function __construct( foreach ($elements as $element) { // create a new attribute bag for that element $this->elementAttributeBags[$element] = new ComponentAttributeBag(); + + // generate the element prefix + $elementPrefix = Str::of($element . ':')->kebab()->__toString(); + + // loop through all of the attributes that we have in the attribute bag + foreach ($attributeBag->getAttributes() as $attributeName => $value) { + // get an instance of a String for the attribute name + $attributeString = Str::of($attributeName); + + // check if the attribute name starts with the prefix + if ($attributeString->startsWith($elementPrefix)) { + // get the new name of the attribute, without the prefix + $newAttributeName = $attributeString->remove($elementPrefix)->__toString(); + + // add the attribute to the appropriate element attribute bag + $this->getAttributeBag($element)->offsetSet($newAttributeName, $value); + + // and remove it from the default attribute bag (which will still have the old name) + $this->getAttributeBag()->offsetUnset($attributeName); + } + } } } diff --git a/tests/MultipleAttributeBagTest.php b/tests/MultipleAttributeBagTest.php index b2c7a34..d17f6d0 100644 --- a/tests/MultipleAttributeBagTest.php +++ b/tests/MultipleAttributeBagTest.php @@ -14,6 +14,31 @@ expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); }); +it('can have element attributes defined when creating the builder', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(attributes: ['label:foo' => 'bar'], elements: ['label']); + + // check that the attribute is in the label attribute bag + expect($attributeBuilder->getAttributeBag('label')->getAttributes())->toHaveKey('foo', 'bar'); + + // check that the attribute has not been added to the default attribute bag + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); +}); + +it('can remove attributes from a particular element attribute bag', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(attributes: ['label:foo' => 'bar'], elements: ['label']); + + // remove the attribute from the attribute bag + $attributeBuilder->removeAttribute('foo', element: 'label'); + + // check that the attribute is in the label attribute bag + expect($attributeBuilder->getAttributeBag('label')->getAttributes())->not()->toHaveKey('foo', 'bar'); + + // check that the attribute has not been added to the default attribute bag + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); +}); + it('can use magic methods to add to a different attribute bag', function () { // create a new attribute builder $attributeBuilder = createAttributeBuilder(elements: ['label']); @@ -27,3 +52,17 @@ // check that the attribute has not been added to the default attribute bag expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); }); + +it('can use magic methods to remove from a different attribute bag', function () { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(attributes: ['label:foo' => 'bar'], elements: ['label']); + + // add the class to the attribute builder + $attributeBuilder->removeAttributeFromLabel('foo'); + + // check that the attribute is in the label attribute bag + expect($attributeBuilder->getAttributeBag('label')->getAttributes())->not()->toHaveKey('foo', 'bar'); + + // check that the attribute has not been added to the default attribute bag + expect($attributeBuilder->getAttributes())->not()->toHaveKey('foo', 'bar'); +}); From 60fe8840b4bf5a945ea37524fe15ea6e04ab056f Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 20 Dec 2023 13:16:22 +0000 Subject: [PATCH 41/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 4c32d82..c8f4fb8 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"d01a3a44e9ab1f149961c4a99341b243","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"0f27a1c27c1a4c6d5fa0c4450e20e2fa","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"f91501fa3cb3f07e6dc209c05ef44ab2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758","tests\/MultipleAttributeBagTest.php":"a81165935e9c4ca620523115f265116e"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"0861ba5f040ee52c2a4c2b4b73ce9e44","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"0f27a1c27c1a4c6d5fa0c4450e20e2fa","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"f91501fa3cb3f07e6dc209c05ef44ab2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758","tests\/MultipleAttributeBagTest.php":"e762cf5f3fdc3b2863c805ae509ccaf2"}} \ No newline at end of file From 760c30c0ad0eee2fed259e55cd05e3e26751e945 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 14:59:53 +0000 Subject: [PATCH 42/50] Integrate into components --- src/AttributeBuilder.php | 2 +- .../Concerns/HasAttributeBuilder.php | 155 ++++++++++++++++++ src/ElementAttributeBagWrapper.php | 18 ++ tests/ComponentCustomisationTest.php | 87 ++++++++++ tests/ComponentIntegrationTest.php | 28 +++- tests/Components/HigherOrderTestComponent.php | 12 +- tests/Components/TestComponent.php | 36 +++- 7 files changed, 330 insertions(+), 8 deletions(-) create mode 100644 src/Components/Concerns/HasAttributeBuilder.php create mode 100644 src/ElementAttributeBagWrapper.php create mode 100644 tests/ComponentCustomisationTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 0fbbddd..322fb2b 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -37,7 +37,7 @@ class AttributeBuilder public function __construct( protected ComponentAttributeBag &$attributeBag, protected Collection $options, - array $elements + array $elements = [] ) { // loop through each of the elements that have been specified foreach ($elements as $element) { diff --git a/src/Components/Concerns/HasAttributeBuilder.php b/src/Components/Concerns/HasAttributeBuilder.php new file mode 100644 index 0000000..8073321 --- /dev/null +++ b/src/Components/Concerns/HasAttributeBuilder.php @@ -0,0 +1,155 @@ +> + */ + protected static $attributeBuilderParsers = []; + + /** + * The elements that have been specified for the attribute builder + * @var array + */ + protected $attributeBuilderElements = []; + + /** + * The instance of the Attribute Builder + * @var AttributeBuilder + */ + protected AttributeBuilder $attributeBuilder; + + /** + * Add a new attribute builder parser at a given weight + * + * @param Closure $closure + * @param int $weight + * @return void + */ + public static function registerAttributeBuilderParser(Closure $closure, $weight = 10) { + // if this is the first time that we are seeing the weight + if (!array_key_exists($weight, static::$attributeBuilderParsers)) { + // set up an array to store all of the closures of this weight + static::$attributeBuilderParsers[$weight] = []; + } + + // store the closure in the parses array + static::$attributeBuilderParsers[$weight][] = $closure; + } + + /** + * @see registerAttributeBuilderParser + */ + public static function customize(Closure $closure, $weight = 10) { + // this is just an alias to registerAttributeBuilderParser + static::registerAttributeBuilderParser(...func_get_args()); + } + + /** + * @see registerAttributeBuilderParser + */ + public static function customise(Closure $closure, $weight = 10) { + // this is just an alias to registerAttributeBuilderParser + static::registerAttributeBuilderParser(...func_get_args()); + } + + /** + * Reset all of the attribute builder parsers + * + * @return void + */ + public static function resetAllAttributeBuilderParsers() + { + // empty out the array + static::$attributeBuilderParsers = []; + } + + /** + * @see resetAllAttributeBuilderParsers + */ + public static function resetAllCustomisations() + { + // this is just an alias to resetAllAttributeBuilderParsers + static::resetAllAttributeBuilderParsers(); + } + + /** + * @see resetAllAttributeBuilderParsers + */ + public static function resetAllCustomizations() + { + // this is just an alias to resetAllAttributeBuilderParsers + static::resetAllAttributeBuilderParsers(); + } + + /** + * Register a new element for the attribute builder + * + * @param string $element + * @return ElementAttributeBagWrapper + */ + protected function registerAttributeBuilderElement(string $element): ElementAttributeBagWrapper + { + // add the name to the array of elements + $this->attributeBuilderElements[] = $element; + + // return a wrapper, as we will need to generate the actual content attributes later + return new ElementAttributeBagWrapper($element); + } + + /** + * Run the attribute builder, and return the data that gets passed to the renderer + * + * @param array $data + * @return array + */ + protected function runAttributeBuilder(array $data): array + { + // get the instance of the attribute builder + $this->attributeBuilder = new AttributeBuilder($data['attributes'], collect(), $this->attributeBuilderElements); + + // sort the parsers by their weight + ksort(static::$attributeBuilderParsers); + + // loop through each of the weights + foreach (static::$attributeBuilderParsers as $parsers) { + // and then through each of the parser of that weight + foreach ($parsers as $parser) { + // run the parser + $parser($this->attributeBuilder, $this); + } + } + + // pull out the "new" attributes + $data['attributes'] = $this->attributeBuilder->getAttributeBag(); + + // loop through each piece of data that we have + foreach ($data as $dataName => $dataElement) { + // check if it it's an instance of an element attribute bag + if ($dataElement instanceof ElementAttributeBagWrapper) { + // if it is, pull out the attributes and set everything we need to + $this->{$dataName} = $data[$dataName] = $dataElement->run($this->attributeBuilder); + } + } + + // return the updated data + return $data; + } + + /** + * Get the underlying attribute builder instance + * + * @return AttributeBuilder + */ + public function getAttributeBuilder(): AttributeBuilder + { + return $this->attributeBuilder; + } +} diff --git a/src/ElementAttributeBagWrapper.php b/src/ElementAttributeBagWrapper.php new file mode 100644 index 0000000..d3d09a9 --- /dev/null +++ b/src/ElementAttributeBagWrapper.php @@ -0,0 +1,18 @@ +getAttributeBag($this->element); + } +} diff --git a/tests/ComponentCustomisationTest.php b/tests/ComponentCustomisationTest.php new file mode 100644 index 0000000..6c9427a --- /dev/null +++ b/tests/ComponentCustomisationTest.php @@ -0,0 +1,87 @@ +setAttribute('foo', 'bar'); + }); + + // render a component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'bar'); +}); + +it('can apply multiple customisations', function () { + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttribute('foo', 'bar'); + }); + + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttribute('bat', 'ball'); + }); + + // render a component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'bar'); + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('bat', 'ball'); +}); + +it('can apply customisations with weights', function () { + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttribute('foo', 'bar'); + }); + + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttribute('foo', 'ball'); + }, 20); + + // render a component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'ball'); +}); + +it('applies customisations in the order they were defined for equal weights', function () { + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttribute('foo', 'bar'); + }); + + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttribute('foo', 'bat'); + }); + + // render a component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'bat'); +}); diff --git a/tests/ComponentIntegrationTest.php b/tests/ComponentIntegrationTest.php index 2733129..54e96c0 100644 --- a/tests/ComponentIntegrationTest.php +++ b/tests/ComponentIntegrationTest.php @@ -1,13 +1,35 @@ blade(''); +it('creates an attribute builder when a component is rendered', function () { + // render a component + $this->blade(''); - expect($blade)->not()->toBeEmpty(); + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that we have created an attribute builder, with the correct properties and values + expect($instance)->toHaveProperty('attributeBuilder'); + expect($instance->getAttributeBuilder())->toBeInstanceOf(AttributeBuilder::class); + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'bar'); +}); + +it('can take in element attributes by prefix', function () { + // render a component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->not()->toHaveKey('foo', 'bar'); + expect($instance->getAttributeBuilder()->getAttributeBag('label')->getAttributes())->toHaveKey('foo', 'bar'); }); diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php index 8de92ec..744e753 100644 --- a/tests/Components/HigherOrderTestComponent.php +++ b/tests/Components/HigherOrderTestComponent.php @@ -8,7 +8,7 @@ class HigherOrderTestComponent extends TestComponent * The last instantiated instance * @var $this */ - public static $lastInstance = null; + private static $lastInstance = null; public function __construct() { @@ -21,4 +21,14 @@ public function __construct() parent::__construct(...func_get_args()); } } + + /** + * Get the last instance + * + * @return static + */ + public static function lastInstance(): static + { + return static::$lastInstance; + } } diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index 832300e..617f298 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -2,19 +2,49 @@ namespace AppKit\UI\Tests\Components; +use AppKit\UI\AttributeBuilder; +use AppKit\UI\Components\Concerns\HasAttributeBuilder; +use AppKit\UI\ElementAttributeBagWrapper; +use Closure; +use Illuminate\Contracts\View\View; +use Illuminate\Contracts\Support\Htmlable; use Illuminate\View\Component; class TestComponent extends Component { - public $property = true; + use HasAttributeBuilder; + /** + * Example property that can be set on the component + * @var boolean + */ + public bool $property = true; + + /** + * An example element attribute bag + * @var ElementAttributeBagWrapper + */ + public $labelAttributes; + + /** + * Build the component + * + * @return void + */ public function __construct() { - + $this->labelAttributes = $this->registerAttributeBuilderElement('label'); } + /** + * Render the component + * + * @return Closure + */ public function render() { - + return function ($data) { + $data = $this->runAttributeBuilder($data); + }; } } From ebb336b7828dc059ff76d39113a79e71d79ea73c Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 20 Dec 2023 16:11:47 +0000 Subject: [PATCH 43/50] PSR-12 Coding Standards --- .php-cs-fixer.cache | 2 +- src/Components/Concerns/HasAttributeBuilder.php | 9 ++++++--- src/ElementAttributeBagWrapper.php | 10 ++++------ tests/ComponentIntegrationTest.php | 1 - tests/Components/TestComponent.php | 5 +---- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index c8f4fb8..d9e2109 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"0861ba5f040ee52c2a4c2b4b73ce9e44","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"0f27a1c27c1a4c6d5fa0c4450e20e2fa","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"7adcf327b07274d8fbd43519a9a5c1e4","tests\/Components\/HigherOrderTestComponent.php":"f91501fa3cb3f07e6dc209c05ef44ab2","tests\/Components\/TestComponent.php":"ce0a8355fb407c343aff84f4f2967758","tests\/MultipleAttributeBagTest.php":"e762cf5f3fdc3b2863c805ae509ccaf2"}} \ No newline at end of file +{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"27988a702d16980467540882a83656be","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"0f27a1c27c1a4c6d5fa0c4450e20e2fa","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"f6b29cf715b195ef6156e33c37b1381f","tests\/Components\/HigherOrderTestComponent.php":"4ef4e9d562e1913af7273e71e98e251f","tests\/Components\/TestComponent.php":"d9d0b234df56dfdbbf5b1a649f7f6b80","tests\/MultipleAttributeBagTest.php":"e762cf5f3fdc3b2863c805ae509ccaf2","src\/Components\/Concerns\/HasAttributeBuilder.php":"68e8dd7f09584c8dd2a9d7c267f60684","src\/ElementAttributeBagWrapper.php":"d43f33fcaf55d6ef9f1edd019e5adbdf","tests\/ComponentCustomisationTest.php":"e109242aae1d6d80d86eafc47cc11269"}} \ No newline at end of file diff --git a/src/Components/Concerns/HasAttributeBuilder.php b/src/Components/Concerns/HasAttributeBuilder.php index 8073321..e405bc3 100644 --- a/src/Components/Concerns/HasAttributeBuilder.php +++ b/src/Components/Concerns/HasAttributeBuilder.php @@ -33,7 +33,8 @@ trait HasAttributeBuilder * @param int $weight * @return void */ - public static function registerAttributeBuilderParser(Closure $closure, $weight = 10) { + public static function registerAttributeBuilderParser(Closure $closure, $weight = 10) + { // if this is the first time that we are seeing the weight if (!array_key_exists($weight, static::$attributeBuilderParsers)) { // set up an array to store all of the closures of this weight @@ -47,7 +48,8 @@ public static function registerAttributeBuilderParser(Closure $closure, $weight /** * @see registerAttributeBuilderParser */ - public static function customize(Closure $closure, $weight = 10) { + public static function customize(Closure $closure, $weight = 10) + { // this is just an alias to registerAttributeBuilderParser static::registerAttributeBuilderParser(...func_get_args()); } @@ -55,7 +57,8 @@ public static function customize(Closure $closure, $weight = 10) { /** * @see registerAttributeBuilderParser */ - public static function customise(Closure $closure, $weight = 10) { + public static function customise(Closure $closure, $weight = 10) + { // this is just an alias to registerAttributeBuilderParser static::registerAttributeBuilderParser(...func_get_args()); } diff --git a/src/ElementAttributeBagWrapper.php b/src/ElementAttributeBagWrapper.php index d3d09a9..8fe0fca 100644 --- a/src/ElementAttributeBagWrapper.php +++ b/src/ElementAttributeBagWrapper.php @@ -2,16 +2,14 @@ namespace AppKit\UI; -use Illuminate\Support\Traits\ForwardsCalls; -use Illuminate\View\ComponentAttributeBag; - -class ElementAttributeBagWrapper { - function __construct(protected string $element) +class ElementAttributeBagWrapper +{ + public function __construct(protected string $element) { } - function run(AttributeBuilder $attributeBuilder) + public function run(AttributeBuilder $attributeBuilder) { return $attributeBuilder->getAttributeBag($this->element); } diff --git a/tests/ComponentIntegrationTest.php b/tests/ComponentIntegrationTest.php index 54e96c0..d1c733c 100644 --- a/tests/ComponentIntegrationTest.php +++ b/tests/ComponentIntegrationTest.php @@ -2,7 +2,6 @@ use AppKit\UI\AttributeBuilder; use AppKit\UI\Tests\Components\HigherOrderTestComponent; -use AppKit\UI\Tests\Components\TestComponent; beforeEach(function () { // we need to register the component diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index 617f298..863f788 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -2,12 +2,9 @@ namespace AppKit\UI\Tests\Components; -use AppKit\UI\AttributeBuilder; use AppKit\UI\Components\Concerns\HasAttributeBuilder; use AppKit\UI\ElementAttributeBagWrapper; use Closure; -use Illuminate\Contracts\View\View; -use Illuminate\Contracts\Support\Htmlable; use Illuminate\View\Component; class TestComponent extends Component @@ -16,7 +13,7 @@ class TestComponent extends Component /** * Example property that can be set on the component - * @var boolean + * @var bool */ public bool $property = true; From a81dfaf808196099307b74df67b0644d9a125cea Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 16:30:01 +0000 Subject: [PATCH 44/50] We don't need that in github --- .gitignore | 1 + .php-cs-fixer.cache | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 .php-cs-fixer.cache diff --git a/.gitignore b/.gitignore index 9155e46..94eeaba 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ Homestead.yaml Homestead.json /.vagrant .phpunit.result.cache +.php-cs-fixer.cache .php_cs .php_cs.cache composer.lock diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache deleted file mode 100644 index d9e2109..0000000 --- a/.php-cs-fixer.cache +++ /dev/null @@ -1 +0,0 @@ -{"php":"8.3.0","version":"3.41.1","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":true,"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_parentheses":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"sort_algorithm":"alpha"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":true,"visibility_required":true,"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline","keep_multiple_spaces_after_comma":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"array_syntax":{"syntax":"short"},"no_unused_imports":true,"trailing_comma_in_multiline":true,"phpdoc_scalar":true,"blank_line_before_statement":{"statements":["break","continue","declare","return","throw","try"]},"phpdoc_single_line_var_spacing":true,"phpdoc_var_without_name":true,"class_attributes_separation":{"elements":{"method":"one"}}},"hashes":{"src\/Ui.php":"ec96cb32a9fcac6082b91081b167a4e2","src\/AttributeBuilder.php":"27988a702d16980467540882a83656be","src\/Components\/BaseComponent.php":"438429a5bb6d0dd223cb2096769a041d","src\/Facades\/Ui.php":"0bb32dde85ecc794eb3f50296cad573f","src\/UiServiceProvider.php":"bf523875e640350bdee1ba48a9a1ef73","tests\/AttributeBagTest.php":"ade346e0748ee6b1a952246d5dc3e457","tests\/Pest.php":"0f27a1c27c1a4c6d5fa0c4450e20e2fa","tests\/TestCase.php":"d3975fb98e27a4494d743faaec54ba0b","tests\/AttributeBagClassTest.php":"d533457750083ee4e51e9ae040123fc8","tests\/AttributeBagAttributeHelperTest.php":"e909fd94515b765c9a97399f668ed7a3","tests\/AttributeBagAttributeTest.php":"ccf406c3648b25ee816efa1dec521e06","tests\/AttributeBagConditionalsTest.php":"aec2bbfba408a61d4fe44c0734d8071e","tests\/ComponentIntegrationTest.php":"f6b29cf715b195ef6156e33c37b1381f","tests\/Components\/HigherOrderTestComponent.php":"4ef4e9d562e1913af7273e71e98e251f","tests\/Components\/TestComponent.php":"d9d0b234df56dfdbbf5b1a649f7f6b80","tests\/MultipleAttributeBagTest.php":"e762cf5f3fdc3b2863c805ae509ccaf2","src\/Components\/Concerns\/HasAttributeBuilder.php":"68e8dd7f09584c8dd2a9d7c267f60684","src\/ElementAttributeBagWrapper.php":"d43f33fcaf55d6ef9f1edd019e5adbdf","tests\/ComponentCustomisationTest.php":"e109242aae1d6d80d86eafc47cc11269"}} \ No newline at end of file From 3c76cdfdaa85b5c7de9022412c0e3ecae478b711 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Wed, 20 Dec 2023 21:22:00 +0000 Subject: [PATCH 45/50] allow state to be passed to attribute builder --- src/AttributeBuilder.php | 3 +-- .../Concerns/HasAttributeBuilder.php | 16 ++++++++++++- tests/ComponentCustomisationTest.php | 24 +++++++++++++++++++ tests/Components/HigherOrderTestComponent.php | 2 +- tests/Components/TestComponent.php | 17 ++++++++++--- tests/Pest.php | 4 ++-- 6 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 322fb2b..7d2e2f6 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -36,7 +36,6 @@ class AttributeBuilder public function __construct( protected ComponentAttributeBag &$attributeBag, - protected Collection $options, array $elements = [] ) { // loop through each of the elements that have been specified @@ -300,7 +299,7 @@ protected function conditionalPasses(string|callable $condition, bool $negateCon $conditionResult = !$conditionResult; } - return $conditionResult; + return !!$conditionResult; } /** diff --git a/src/Components/Concerns/HasAttributeBuilder.php b/src/Components/Concerns/HasAttributeBuilder.php index e405bc3..3f187dc 100644 --- a/src/Components/Concerns/HasAttributeBuilder.php +++ b/src/Components/Concerns/HasAttributeBuilder.php @@ -26,6 +26,8 @@ trait HasAttributeBuilder */ protected AttributeBuilder $attributeBuilder; + protected $attributeBuilderStates = []; + /** * Add a new attribute builder parser at a given weight * @@ -92,6 +94,14 @@ public static function resetAllCustomizations() static::resetAllAttributeBuilderParsers(); } + public function exposePropertyAsState($property, $state = null) { + if (!$state) { + $state = $property; + } + + $this->attributeBuilderStates[$state] = fn () => $this->{$property}; + } + /** * Register a new element for the attribute builder * @@ -116,7 +126,11 @@ protected function registerAttributeBuilderElement(string $element): ElementAttr protected function runAttributeBuilder(array $data): array { // get the instance of the attribute builder - $this->attributeBuilder = new AttributeBuilder($data['attributes'], collect(), $this->attributeBuilderElements); + $this->attributeBuilder = new AttributeBuilder($data['attributes'], $this->attributeBuilderElements); + + foreach ($this->attributeBuilderStates as $state => $closure) { + $this->attributeBuilder->registerConditional($state, $closure); + } // sort the parsers by their weight ksort(static::$attributeBuilderParsers); diff --git a/tests/ComponentCustomisationTest.php b/tests/ComponentCustomisationTest.php index 6c9427a..c9b238e 100644 --- a/tests/ComponentCustomisationTest.php +++ b/tests/ComponentCustomisationTest.php @@ -85,3 +85,27 @@ // check that the attributes are created, and in the correct place expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'bat'); }); + +it('can pass properties down to state', function () { + HigherOrderTestComponent::customise(function (AttributeBuilder $attributes) { + $attributes->setAttributeWhenToggle('foo', 'bar'); + }); + + // render a component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->not()->toHaveKey('foo', 'bar'); + + // render another component + $this->blade(''); + + // get the instance of the component that was rendered + $instance = HigherOrderTestComponent::lastInstance(); + + // check that the attributes are created, and in the correct place + expect($instance->getAttributeBuilder()->getAttributes())->toHaveKey('foo', 'bar'); +}); diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php index 744e753..b5097f3 100644 --- a/tests/Components/HigherOrderTestComponent.php +++ b/tests/Components/HigherOrderTestComponent.php @@ -10,7 +10,7 @@ class HigherOrderTestComponent extends TestComponent */ private static $lastInstance = null; - public function __construct() + public function __construct($toggle = false, $size = '') { // store the last instance static::$lastInstance = $this; diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index 863f788..c016bfd 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -12,10 +12,16 @@ class TestComponent extends Component use HasAttributeBuilder; /** - * Example property that can be set on the component + * Example boolean property that can be set on the component * @var bool */ - public bool $property = true; + public bool $toggle = false; + + /** + * Example string property that can be set on the component + * @var string + */ + public string $size = ''; /** * An example element attribute bag @@ -28,9 +34,14 @@ class TestComponent extends Component * * @return void */ - public function __construct() + public function __construct($toggle = false, $size = '') { + $this->toggle = $toggle; + $this->size = $size; + $this->labelAttributes = $this->registerAttributeBuilderElement('label'); + + $this->exposePropertyAsState('toggle'); } /** diff --git a/tests/Pest.php b/tests/Pest.php index 3e65c44..7eea04f 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -49,13 +49,13 @@ * @param array $options * @return AttributeBuilder */ -function createAttributeBuilder($attributes = [], $options = [], $elements = []) +function createAttributeBuilder($attributes = [], $elements = []) { // create an attribute bag that will be passed to the attribute builder $attributeBag = new ComponentAttributeBag($attributes); // return the attribute builder - return new AttributeBuilder($attributeBag, collect($options), $elements); + return new AttributeBuilder($attributeBag, $elements); } function addDataAttributeHelperToAttributeBuilder() From 7678d58745fb77d8a149f0a7435e6c66be7c490e Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Wed, 20 Dec 2023 21:22:43 +0000 Subject: [PATCH 46/50] PSR-12 Coding Standards --- src/Components/Concerns/HasAttributeBuilder.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Components/Concerns/HasAttributeBuilder.php b/src/Components/Concerns/HasAttributeBuilder.php index 3f187dc..548b689 100644 --- a/src/Components/Concerns/HasAttributeBuilder.php +++ b/src/Components/Concerns/HasAttributeBuilder.php @@ -94,7 +94,8 @@ public static function resetAllCustomizations() static::resetAllAttributeBuilderParsers(); } - public function exposePropertyAsState($property, $state = null) { + public function exposePropertyAsState($property, $state = null) + { if (!$state) { $state = $property; } From 96fa56f16f10ff38ac93961bb1e86b2859505820 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Thu, 21 Dec 2023 10:31:19 +0000 Subject: [PATCH 47/50] Make it more generic --- tests/Components/HigherOrderTestComponent.php | 18 ++++++++++++------ tests/Components/TestComponent.php | 17 +---------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php index b5097f3..56ee00e 100644 --- a/tests/Components/HigherOrderTestComponent.php +++ b/tests/Components/HigherOrderTestComponent.php @@ -2,6 +2,10 @@ namespace AppKit\UI\Tests\Components; +use AppKit\UI\Tests\Components\TestComponent; +use Closure; +use ReflectionClass; + class HigherOrderTestComponent extends TestComponent { /** @@ -10,16 +14,18 @@ class HigherOrderTestComponent extends TestComponent */ private static $lastInstance = null; - public function __construct($toggle = false, $size = '') + /** + * Render the component + * + * @return Closure + */ + public function render() { // store the last instance static::$lastInstance = $this; - // check if we are extending a class that has a constructor - if (method_exists(parent::class, '__construct')) { - // call the parent constructor with all of the arguments passed - parent::__construct(...func_get_args()); - } + // call the actual renderer + return parent::render(); } /** diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index c016bfd..097ec64 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -11,18 +11,6 @@ class TestComponent extends Component { use HasAttributeBuilder; - /** - * Example boolean property that can be set on the component - * @var bool - */ - public bool $toggle = false; - - /** - * Example string property that can be set on the component - * @var string - */ - public string $size = ''; - /** * An example element attribute bag * @var ElementAttributeBagWrapper @@ -34,11 +22,8 @@ class TestComponent extends Component * * @return void */ - public function __construct($toggle = false, $size = '') + public function __construct(public bool $toggle = false, public string $size = '') { - $this->toggle = $toggle; - $this->size = $size; - $this->labelAttributes = $this->registerAttributeBuilderElement('label'); $this->exposePropertyAsState('toggle'); From 3c9a040b0c3e188ce23222e76aa6d1a3756061c9 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Fri, 22 Dec 2023 10:16:42 +0000 Subject: [PATCH 48/50] Introduce state based attributes --- src/AttributeBuilder.php | 94 +++++++++++-------- .../Concerns/HasAttributeBuilder.php | 14 +-- src/StateType.php | 0 tests/AttributeBagConditionalsTest.php | 8 +- tests/AttributeBagStateBasedAttributeTest.php | 43 +++++++++ tests/Components/TestComponent.php | 2 +- tests/Pest.php | 12 ++- 7 files changed, 118 insertions(+), 55 deletions(-) create mode 100644 src/StateType.php create mode 100644 tests/AttributeBagStateBasedAttributeTest.php diff --git a/src/AttributeBuilder.php b/src/AttributeBuilder.php index 7d2e2f6..c96d792 100644 --- a/src/AttributeBuilder.php +++ b/src/AttributeBuilder.php @@ -26,7 +26,7 @@ class AttributeBuilder * An array to store the registered conditional helpers * @var array */ - protected $conditionalHelpers = []; + protected $states = []; /** * An array of attribute bags for each of the registered elements @@ -162,13 +162,25 @@ public function removeClass(...$classes): AttributeBuilder * @return AttributeBuilder * @throws RuntimeException */ - public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false, $element = null): AttributeBuilder + public function setAttribute($attribute, $value = null, $attributeType = null, $condition = null, $negateCondition = false, $element = null, $state = null): AttributeBuilder { // if we have a conditional, we need to check if it passes if ($condition != null && !$this->conditionalPasses($condition, $negateCondition)) { return $this; } + // if we have passed in and array + if (is_array($value)) { + // and have a state element + if ($state) { + // get the value of the state + $stateValue = $this->states[$state](); + + // we need to get the value from the array + $value = array_key_exists($stateValue, $value) ? $value[$stateValue] : null; + } + } + // loop through each of the attributes that we need to remove foreach ($this->formatAttributes([$attribute => $value], $attributeType) as $attribute => $value) { $this->getAttributeBag($element)->offsetSet($attribute, $value); @@ -288,30 +300,30 @@ protected function conditionalPasses(string|callable $condition, bool $negateCon { // check if we have a condition that exists in the helpers if (is_string($condition)) { - $condition = $this->conditionalHelpers[$condition]; + $condition = $this->states[$condition]; } - // evaluate the conditional - $conditionResult = $condition(); + // evaluate the conditional, ensuring that it's a boolean + $conditionResult = $condition() === true; // negate the result of the conditional if we need to if ($negateCondition) { $conditionResult = !$conditionResult; } - return !!$conditionResult; + return $conditionResult; } /** - * Register a conditional + * Register a state * * @param string $name * @param callable $callable * @return AttributeBuilder */ - public function registerConditional(string $name, callable $callable): AttributeBuilder + public function registerState(string $name, callable $callable): AttributeBuilder { - $this->conditionalHelpers[$name] = $callable; + $this->states[$name] = $callable; return $this; } @@ -330,58 +342,60 @@ public function getAttributeBag(?string $element = null): ComponentAttributeBag return $this->attributeBag; } - /** - * Magic method to catch everything that we aren't already dealing with - * - * @param mixed $method - * @param mixed $parameters - * @return mixed - * @throws BadMethodCallException - */ - public function __call($method, $parameters) + private function generateMagicMethodRegexCapture(string $captureGroup, array $values, array $triggers = []) { // we need to build up the regex that we are going to use to parse the method - $attributeTypesString = ''; + $regexString = ''; + $triggerString = ''; // check if we have any attribute helpers - if (!empty(self::$attributeHelpers)) { + if (!empty($values)) { // create an array to store all of the helper names - $attributeTypes = []; + $regexValues = []; - foreach (self::$attributeHelpers as $helper => $closure) { + foreach (array_keys($values) as $value) { // pull out the name of the helper in studly case - $attributeTypes[] = Str::of($helper)->studly()->__toString(); + $regexValues[] = Str::of($value)->studly()->__toString(); } - // create the string that will be included in the regex - $attributeTypesString = '(?P' . implode('|', $attributeTypes) . ')?'; - } - - // we also need to build up the regex that we are going to use for the element - $attributeBagString = ''; + if (!empty($triggers)) { + // create an array to store all of the helper names + $triggerValues = []; - // if we have any element attribute bags - if (!empty($this->elementAttributeBags)) { - // create an array to store all of the names - $elementAttributeBags = []; + foreach ($triggers as $trigger) { + // pull out the name of the helper in studly case + $triggerValues[] = Str::of($trigger)->studly()->__toString(); + } - foreach ($this->elementAttributeBags as $name => $elementAttributeBag) { - // pull out each of the element names in study case - $elementAttributeBags[] = Str::of($name)->studly()->__toString(); + $triggerString = '(' . implode('|', $triggerValues) . ')'; } - // create the string that will be used in the regex - $attributeBagString = '((To|On|From)(?P' . implode('|', $elementAttributeBags) . '))?'; + // create the string that will be included in the regex + $regexString = '(' . $triggerString . '(?P<' . $captureGroup . '>' . implode('|', $regexValues) . '))?'; } + return $regexString; + } + + /** + * Magic method to catch everything that we aren't already dealing with + * + * @param mixed $method + * @param mixed $parameters + * @return mixed + * @throws BadMethodCallException + */ + public function __call($method, $parameters) + { // create the regex $magicMethodRegex = ' / (?Pset|add|remove|toggle) - ' . $attributeTypesString . ' + ' . $this->generateMagicMethodRegexCapture('attributeType', self::$attributeHelpers) . ' (?P[A-Za-z0-9]*)? (?PAttribute|Class) - ' . $attributeBagString . ' + ' . $this->generateMagicMethodRegexCapture('element', $this->elementAttributeBags, ['to', 'on', 'from']) . ' + ' . $this->generateMagicMethodRegexCapture('state', $this->states, ['for']) . ' ( (If|When) (?PNot)? diff --git a/src/Components/Concerns/HasAttributeBuilder.php b/src/Components/Concerns/HasAttributeBuilder.php index 3f187dc..357d960 100644 --- a/src/Components/Concerns/HasAttributeBuilder.php +++ b/src/Components/Concerns/HasAttributeBuilder.php @@ -26,7 +26,7 @@ trait HasAttributeBuilder */ protected AttributeBuilder $attributeBuilder; - protected $attributeBuilderStates = []; + protected $attributeBuilderConditionals = []; /** * Add a new attribute builder parser at a given weight @@ -94,12 +94,12 @@ public static function resetAllCustomizations() static::resetAllAttributeBuilderParsers(); } - public function exposePropertyAsState($property, $state = null) { - if (!$state) { - $state = $property; + public function exposePropertyAsConditional($property, $condition = null) { + if (!$condition) { + $condition = $property; } - $this->attributeBuilderStates[$state] = fn () => $this->{$property}; + $this->attributeBuilderConditionals[$condition] = fn () => $this->{$property}; } /** @@ -128,8 +128,8 @@ protected function runAttributeBuilder(array $data): array // get the instance of the attribute builder $this->attributeBuilder = new AttributeBuilder($data['attributes'], $this->attributeBuilderElements); - foreach ($this->attributeBuilderStates as $state => $closure) { - $this->attributeBuilder->registerConditional($state, $closure); + foreach ($this->attributeBuilderConditionals as $state => $closure) { + $this->attributeBuilder->registerState($state, $closure); } // sort the parsers by their weight diff --git a/src/StateType.php b/src/StateType.php new file mode 100644 index 0000000..e69de29 diff --git a/tests/AttributeBagConditionalsTest.php b/tests/AttributeBagConditionalsTest.php index d071ffe..8031e48 100644 --- a/tests/AttributeBagConditionalsTest.php +++ b/tests/AttributeBagConditionalsTest.php @@ -20,7 +20,7 @@ $attributeBuilder = createAttributeBuilder(); // add the conditional helpers - addConditionalHelpersToAttributeBuilder($attributeBuilder); + addStatesToAttributeBuilder($attributeBuilder); // add the class to the attribute builder $attributeBuilder @@ -38,7 +38,7 @@ $attributeBuilder = createAttributeBuilder(); // add the conditional helpers - addConditionalHelpersToAttributeBuilder($attributeBuilder); + addStatesToAttributeBuilder($attributeBuilder); // add the class to the attribute builder $attributeBuilder @@ -71,7 +71,7 @@ $attributeBuilder = createAttributeBuilder(); // add the conditional helpers - addConditionalHelpersToAttributeBuilder($attributeBuilder); + addStatesToAttributeBuilder($attributeBuilder); // add the class to the attribute builder $attributeBuilder @@ -89,7 +89,7 @@ $attributeBuilder = createAttributeBuilder(); // add the conditional helpers - addConditionalHelpersToAttributeBuilder($attributeBuilder); + addStatesToAttributeBuilder($attributeBuilder); // add the class to the attribute builder $attributeBuilder diff --git a/tests/AttributeBagStateBasedAttributeTest.php b/tests/AttributeBagStateBasedAttributeTest.php new file mode 100644 index 0000000..ed8dbf8 --- /dev/null +++ b/tests/AttributeBagStateBasedAttributeTest.php @@ -0,0 +1,43 @@ + 'small', + 'md' => 'medium', + 'lg' => 'large', + ]; + + // add the class to the attribute builder + $attributeBuilder->setAttribute('foo', $values, state: 'size'); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('foo', $values[$size]); +})->with(['sm', 'md', 'lg']); + +it('can define the value of an attribute based on a state value via a magic method', function ($size) { + // create a new attribute builder + $attributeBuilder = createAttributeBuilder(); + + // set the size to be small + addSizeStateToAttributeBuilder($attributeBuilder, $size); + + // get the values of the attribute + $values = [ + 'sm' => 'small', + 'md' => 'medium', + 'lg' => 'large', + ]; + + // add the class to the attribute builder + $attributeBuilder->setAttributeForSize('foo', $values); + + // check that the classes are correct + expect($attributeBuilder->getAttributes())->toHaveKey('foo', $values[$size]); +})->with(['sm', 'md', 'lg']); diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index 097ec64..5e63d91 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -26,7 +26,7 @@ public function __construct(public bool $toggle = false, public string $size = ' { $this->labelAttributes = $this->registerAttributeBuilderElement('label'); - $this->exposePropertyAsState('toggle'); + $this->exposePropertyAsConditional('toggle'); } /** diff --git a/tests/Pest.php b/tests/Pest.php index 7eea04f..b1031bd 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -65,8 +65,14 @@ function addDataAttributeHelperToAttributeBuilder() }); } -function addConditionalHelpersToAttributeBuilder(AttributeBuilder $attributeBuilder) +function addStatesToAttributeBuilder(AttributeBuilder $attributeBuilder) { - $attributeBuilder->registerConditional('true', fn () => true); - $attributeBuilder->registerConditional('false', fn () => false); + $attributeBuilder->registerState('true', fn () => true); + $attributeBuilder->registerState('false', fn () => false); + +} + +function addSizeStateToAttributeBuilder(AttributeBuilder $attributeBuilder, $size) +{ + $attributeBuilder->registerState('size', fn() => $size); } From 69a9cc5dba4934643ef256b268b9a4bb3b319be9 Mon Sep 17 00:00:00 2001 From: Darren Coutts Date: Fri, 22 Dec 2023 10:21:38 +0000 Subject: [PATCH 49/50] Update test component --- tests/Components/TestComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Components/TestComponent.php b/tests/Components/TestComponent.php index 5e63d91..097ec64 100644 --- a/tests/Components/TestComponent.php +++ b/tests/Components/TestComponent.php @@ -26,7 +26,7 @@ public function __construct(public bool $toggle = false, public string $size = ' { $this->labelAttributes = $this->registerAttributeBuilderElement('label'); - $this->exposePropertyAsConditional('toggle'); + $this->exposePropertyAsState('toggle'); } /** From 17cb40812fbfd5839385b070a037f18ab47598a8 Mon Sep 17 00:00:00 2001 From: darrencoutts118 Date: Fri, 22 Dec 2023 10:21:57 +0000 Subject: [PATCH 50/50] PSR-12 Coding Standards --- tests/Components/HigherOrderTestComponent.php | 2 -- tests/Pest.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/Components/HigherOrderTestComponent.php b/tests/Components/HigherOrderTestComponent.php index 56ee00e..6e5a6ea 100644 --- a/tests/Components/HigherOrderTestComponent.php +++ b/tests/Components/HigherOrderTestComponent.php @@ -2,9 +2,7 @@ namespace AppKit\UI\Tests\Components; -use AppKit\UI\Tests\Components\TestComponent; use Closure; -use ReflectionClass; class HigherOrderTestComponent extends TestComponent { diff --git a/tests/Pest.php b/tests/Pest.php index b1031bd..598b005 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -74,5 +74,5 @@ function addStatesToAttributeBuilder(AttributeBuilder $attributeBuilder) function addSizeStateToAttributeBuilder(AttributeBuilder $attributeBuilder, $size) { - $attributeBuilder->registerState('size', fn() => $size); + $attributeBuilder->registerState('size', fn () => $size); }