feat(form): Add InputRadio class for HTML <input type="radio"> element with attributes and rendering capabilities.#35
Conversation
…ement with attributes and rendering capabilities.
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds a new InputRadio component implementing radio inputs with checked-state logic and optional unchecked hidden-value support, introduces a HasCheckedState trait shared by inputs, updates InputCheckbox to use the trait/type tweaks, and adds comprehensive tests plus a changelog entry. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
No actionable comments were generated in the recent review. 🎉 📜 Recent review detailsConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (3)
🧰 Additional context used🧠 Learnings (3)📚 Learning: 2026-02-09T11:28:04.228ZApplied to files:
📚 Learning: 2026-02-06T21:37:44.509ZApplied to files:
📚 Learning: 2026-02-07T18:51:37.079ZApplied to files:
🧬 Code graph analysis (2)src/Form/InputCheckbox.php (1)
src/Form/InputRadio.php (6)
🪛 PHPMD (2.15.0)src/Form/Mixin/HasCheckedState.php[warning] 74-113: The method buildAttributes() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10. (undefined) (CyclomaticComplexity) [warning] 74-113: The method buildAttributes() has an NPath complexity of 768. The configured NPath complexity threshold is 200. (undefined) (NPathComplexity) [error] 83-83: Avoid using static access to class '\UIAwesome\Html\Helper\Enum' in method 'buildAttributes'. (undefined) (StaticAccess) [error] 84-84: Avoid using static access to class '\UIAwesome\Html\Helper\Enum' in method 'buildAttributes'. (undefined) (StaticAccess) src/Form/InputRadio.php[warning] 120-120: Avoid variables with short names like $id. Configured minimum length is 3. (undefined) (ShortVariable) [error] 160-160: Avoid using static access to class '\UIAwesome\Html\Core\Html' in method 'run'. (undefined) (StaticAccess) ⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
🔇 Additional comments (5)
✏️ Tip: You can disable this entire section by setting Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #35 +/- ##
===========================================
Coverage 100.00% 100.00%
- Complexity 135 145 +10
===========================================
Files 70 72 +2
Lines 315 362 +47
===========================================
+ Hits 315 362 +47 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@src/Form/InputRadio.php`:
- Around line 229-270: In buildAttributes, the array-match loop never breaks on
a found match and the method's complexity triggers PHPMD warnings; fix by
extracting the scalar branch and the array branch into two small private helpers
(e.g., resolveCheckedForScalar(string $valueStr, $normalizedChecked) and
resolveCheckedForArray(string $valueStr, array $normalizedChecked)) that return
a boolean checked state, call them from buildAttributes instead of inlining the
logic, and ensure the array loop breaks immediately when a match is found; also
normalize the unchecked representation by always setting $attributes['checked']
= false when $normalizedChecked is null/false or no match is found so the
renderer sees a consistent key.
- Around line 92-98: The PHPDoc for InputRadio::checked() (and likewise
uncheckedValue()) declares a return of static but the method signatures use ":
self", causing an inconsistency with the immutable-setter pattern used by the
trait methods; update the method signatures of checked() and uncheckedValue() to
return static instead of self so the signature matches the `@return` static PHPDoc
and the other immutable setters (change the return type on the checked method
and on the uncheckedValue method in the InputRadio class to static).
In `@tests/Form/InputRadioTest.php`:
- Around line 215-236: Add a brief inline comment above the str_replace call in
testRenderWithCheckedAndValue explaining that CheckedProvider supplies
checkbox-oriented expected strings and the replacement converts those
expectations to radio equivalents (id prefix and type attribute), so future
readers understand why we adapt checkbox data for InputRadio::tag() tests;
reference CheckedProvider, the str_replace usage, and the
testRenderWithCheckedAndValue method so the comment is easy to find.
In `@tests/Provider/Form/CheckedProvider.php`:
- Around line 13-14: The provider CheckedProvider currently returns HTML strings
that embed a fixed id-prefix ("inputcheckbox-") and type="checkbox", forcing
InputRadioTest to patch them with str_replace; update CheckedProvider to return
structured data (e.g., include explicit fields like id_prefix and input_type
alongside the expected_html or provide templates/placeholders) so tests
InputCheckboxTest and InputRadioTest can assemble final expected strings without
brittle string replacements; change the provider return shape and adjust the
tests to use the id_prefix and input_type fields when building expected HTML,
keeping existing test semantics but removing ad-hoc str_replace usage.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
CHANGELOG.mdsrc/Form/InputRadio.phptests/Form/InputCheckboxTest.phptests/Form/InputRadioTest.phptests/Provider/Form/CheckedProvider.php
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-06T21:37:44.509Z
Learnt from: terabytesoftw
Repo: ui-awesome/html PR: 24
File: tests/Form/InputTextTest.php:33-620
Timestamp: 2026-02-06T21:37:44.509Z
Learning: In the ui-awesome/html repository, prefer individual test methods over PHPUnit data providers in test classes. This helps maintainers see exactly how each test behaves without cross-referencing data providers. Apply to all PHP test files under tests. Use separate, descriptively named test methods to cover different inputs/edge cases; retain data providers only if they clearly improve readability or reduce duplication.
Applied to files:
tests/Provider/Form/CheckedProvider.phptests/Form/InputCheckboxTest.phptests/Form/InputRadioTest.php
🧬 Code graph analysis (1)
src/Form/InputRadio.php (4)
src/Root/Html.php (1)
Html(27-40)src/Phrasing/Label.php (2)
Label(27-68)for(42-45)src/Form/Mixin/HasLabel.php (3)
notLabel(148-154)label(50-56)labelAttributes(72-78)src/Form/Mixin/CanBeEnclosedByLabel.php (1)
enclosedByLabel(32-38)
🪛 PHPMD (2.15.0)
src/Form/InputRadio.php
[warning] 160-160: Avoid variables with short names like $id. Configured minimum length is 3. (undefined)
(ShortVariable)
[error] 200-200: Avoid using static access to class '\UIAwesome\Html\Core\Html' in method 'run'. (undefined)
(StaticAccess)
[warning] 229-270: The method buildAttributes() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10. (undefined)
(CyclomaticComplexity)
[warning] 229-270: The method buildAttributes() has an NPath complexity of 768. The configured NPath complexity threshold is 200. (undefined)
(NPathComplexity)
[error] 238-238: Avoid using static access to class '\UIAwesome\Html\Helper\Enum' in method 'buildAttributes'. (undefined)
(StaticAccess)
[error] 239-239: Avoid using static access to class '\UIAwesome\Html\Helper\Enum' in method 'buildAttributes'. (undefined)
(StaticAccess)
tests/Form/InputRadioTest.php
[warning] 41-1022: The class InputRadioTest has 64 public methods and attributes. Consider reducing the number of public items to less than 45. (undefined)
(ExcessivePublicCount)
[warning] 41-1022: The class InputRadioTest has 64 non-getter- and setter-methods. Consider refactoring InputRadioTest to keep number of methods under 25. (undefined)
(TooManyMethods)
[warning] 41-1022: The class InputRadioTest has 64 public methods. Consider refactoring InputRadioTest to keep number of public methods under 10. (undefined)
(TooManyPublicMethods)
[warning] 41-1022: The class InputRadioTest has an overall complexity of 64 which is very high. The configured complexity threshold is 50. (undefined)
(ExcessiveClassComplexity)
[error] 41-1022: The class InputRadioTest has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13. (undefined)
(CouplingBetweenObjects)
[error] 371-377: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabel'. (undefined)
(StaticAccess)
[error] 392-399: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabelAndCustomTemplate'. (undefined)
(StaticAccess)
[error] 425-432: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabelAndLabelFor'. (undefined)
(StaticAccess)
[error] 462-468: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabelWithoutId'. (undefined)
(StaticAccess)
[warning] 489-489: Avoid variables with short names like $id. Configured minimum length is 3. (undefined)
(ShortVariable)
[error] 500-500: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithGlobalDefaultsAreApplied'. (undefined)
(StaticAccess)
[error] 508-508: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithGlobalDefaultsAreApplied'. (undefined)
(StaticAccess)
[error] 878-878: Avoid using static access to class '\UIAwesome\Html\Form\InputRadio' in method 'testRenderWithToString'. (undefined)
(StaticAccess)
[error] 938-947: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithUncheckedValueAndEnclosedByLabel'. (undefined)
(StaticAccess)
[error] 953-953: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithUserOverridesGlobalDefaults'. (undefined)
(StaticAccess)
[error] 960-960: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithUserOverridesGlobalDefaults'. (undefined)
(StaticAccess)
[error] 979-979: Avoid using static access to class '\UIAwesome\Html\Form\InputRadio' in method 'testReturnNewInstanceWhenSettingAttribute'. (undefined)
(StaticAccess)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
- GitHub Check: phpunit / PHP 8.5-ubuntu-latest
- GitHub Check: phpunit / PHP 8.2-windows-2022
- GitHub Check: mutation / PHP 8.5-ubuntu-latest
- GitHub Check: linter / Super Linter
- GitHub Check: phpunit / PHP 8.5-windows-2022
- GitHub Check: easy-coding-standard / PHP 8.5-ubuntu-latest
- GitHub Check: linter / Super Linter
🔇 Additional comments (6)
src/Form/InputRadio.php (3)
157-214: Rendering logic looks solid — good label/enclosure/unchecked handling.The
run()method clearly handles the three rendering modes (no label, separate label, enclosing label) with correctforattribute linking and hidden-input placement. The idempotent rendering (tested intestRenderWithEnclosedByLabelIsIdempotent) confirms no mutation on repeated calls. Clean implementation overall.
44-51: Trait composition and class structure look appropriate.The class correctly composes
CanBeAutofocus,CanBeEnclosedByLabel,HasLabel,HasRequired,HasTabindex, andHasValue— matching the HTML spec for<input type="radio">. Thefinalkeyword prevents extension, which is the right call for a concrete element class.
144-150: This template string uses the framework's template processing, which correctly interprets the\nsequences as newline placeholders. The passing tests confirm that the rendered output contains actual newlines (not literal\ncharacters), so this is working as designed.tests/Form/InputCheckboxTest.php (1)
27-27: Documentation update accurately reflects the test coverage.CHANGELOG.md (1)
23-23: Changelog entry is consistent with existing format.tests/Form/InputRadioTest.php (1)
39-41: Comprehensive test coverage for InputRadio.The test suite thoroughly covers attributes, label configurations, providers, immutability, idempotency, and edge cases — mirroring the checkbox tests while correctly adapting for radio semantics. The PHPMD warnings about method count and coupling are expected for a test class of this breadth and can be safely suppressed.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/Form/InputRadio.php`:
- Around line 157-214: Extract the duplicated checked-input logic from
InputRadio and InputCheckbox into a shared location (either a trait named
HasCheckedState or an abstract class AbstractCheckedInput): move the private
properties $checked and $uncheckedValue and their setters, plus the identical
run() and buildAttributes() implementations, into that trait/abstract class and
have InputRadio and InputCheckbox use the trait or extend the abstract class;
keep each class's loadDefault() only to set the Type::RADIO or Type::CHECKBOX
difference and ensure references to run(), buildAttributes(), $checked,
$uncheckedValue, and loadDefault() still resolve correctly after the refactor.
📜 Review details
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (3)
src/Form/InputCheckbox.phpsrc/Form/InputRadio.phptests/Form/InputRadioTest.php
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: terabytesoftw
Repo: ui-awesome/html PR: 35
File: tests/Provider/Form/CheckedProvider.php:13-14
Timestamp: 2026-02-09T11:28:04.228Z
Learning: In the ui-awesome/html repository, using `str_replace` to adapt shared data provider HTML strings for different input types (e.g., adapting `CheckedProvider` for both `InputCheckboxTest` and `InputRadioTest` by replacing "inputcheckbox-" with "inputradio-" and "checkbox" with "radio") is acceptable and considered clear. Don't suggest refactoring such patterns to avoid string replacement.
📚 Learning: 2026-02-06T21:37:44.509Z
Learnt from: terabytesoftw
Repo: ui-awesome/html PR: 24
File: tests/Form/InputTextTest.php:33-620
Timestamp: 2026-02-06T21:37:44.509Z
Learning: In the ui-awesome/html repository, prefer individual test methods over PHPUnit data providers in test classes. This helps maintainers see exactly how each test behaves without cross-referencing data providers. Apply to all PHP test files under tests. Use separate, descriptively named test methods to cover different inputs/edge cases; retain data providers only if they clearly improve readability or reduce duplication.
Applied to files:
tests/Form/InputRadioTest.php
📚 Learning: 2026-02-09T11:28:04.228Z
Learnt from: terabytesoftw
Repo: ui-awesome/html PR: 35
File: tests/Provider/Form/CheckedProvider.php:13-14
Timestamp: 2026-02-09T11:28:04.228Z
Learning: In the ui-awesome/html repository, using `str_replace` to adapt shared data provider HTML strings for different input types (e.g., adapting `CheckedProvider` for both `InputCheckboxTest` and `InputRadioTest` by replacing "inputcheckbox-" with "inputradio-" and "checkbox" with "radio") is acceptable and considered clear. Don't suggest refactoring such patterns to avoid string replacement.
Applied to files:
tests/Form/InputRadioTest.phpsrc/Form/InputRadio.php
🧬 Code graph analysis (1)
src/Form/InputCheckbox.php (1)
src/Form/InputRadio.php (2)
checked(92-98)uncheckedValue(119-125)
🪛 PHPMD (2.15.0)
tests/Form/InputRadioTest.php
[warning] 41-1023: The class InputRadioTest has 64 public methods and attributes. Consider reducing the number of public items to less than 45. (undefined)
(ExcessivePublicCount)
[warning] 41-1023: The class InputRadioTest has 64 non-getter- and setter-methods. Consider refactoring InputRadioTest to keep number of methods under 25. (undefined)
(TooManyMethods)
[warning] 41-1023: The class InputRadioTest has 64 public methods. Consider refactoring InputRadioTest to keep number of public methods under 10. (undefined)
(TooManyPublicMethods)
[warning] 41-1023: The class InputRadioTest has an overall complexity of 64 which is very high. The configured complexity threshold is 50. (undefined)
(ExcessiveClassComplexity)
[error] 41-1023: The class InputRadioTest has a coupling between objects value of 15. Consider to reduce the number of dependencies under 13. (undefined)
(CouplingBetweenObjects)
[error] 372-378: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabel'. (undefined)
(StaticAccess)
[error] 393-400: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabelAndCustomTemplate'. (undefined)
(StaticAccess)
[error] 426-433: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabelAndLabelFor'. (undefined)
(StaticAccess)
[error] 463-469: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithEnclosedByLabelWithoutId'. (undefined)
(StaticAccess)
[warning] 490-490: Avoid variables with short names like $id. Configured minimum length is 3. (undefined)
(ShortVariable)
[error] 501-501: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithGlobalDefaultsAreApplied'. (undefined)
(StaticAccess)
[error] 509-509: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithGlobalDefaultsAreApplied'. (undefined)
(StaticAccess)
[error] 879-879: Avoid using static access to class '\UIAwesome\Html\Form\InputRadio' in method 'testRenderWithToString'. (undefined)
(StaticAccess)
[error] 939-948: Avoid using static access to class '\PHPForge\Support\LineEndingNormalizer' in method 'testRenderWithUncheckedValueAndEnclosedByLabel'. (undefined)
(StaticAccess)
[error] 954-954: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithUserOverridesGlobalDefaults'. (undefined)
(StaticAccess)
[error] 961-961: Avoid using static access to class '\UIAwesome\Html\Core\Factory\SimpleFactory' in method 'testRenderWithUserOverridesGlobalDefaults'. (undefined)
(StaticAccess)
[error] 980-980: Avoid using static access to class '\UIAwesome\Html\Form\InputRadio' in method 'testReturnNewInstanceWhenSettingAttribute'. (undefined)
(StaticAccess)
src/Form/InputRadio.php
[warning] 160-160: Avoid variables with short names like $id. Configured minimum length is 3. (undefined)
(ShortVariable)
[error] 200-200: Avoid using static access to class '\UIAwesome\Html\Core\Html' in method 'run'. (undefined)
(StaticAccess)
[warning] 229-268: The method buildAttributes() has a Cyclomatic Complexity of 13. The configured cyclomatic complexity threshold is 10. (undefined)
(CyclomaticComplexity)
[warning] 229-268: The method buildAttributes() has an NPath complexity of 768. The configured NPath complexity threshold is 200. (undefined)
(NPathComplexity)
[error] 238-238: Avoid using static access to class '\UIAwesome\Html\Helper\Enum' in method 'buildAttributes'. (undefined)
(StaticAccess)
[error] 239-239: Avoid using static access to class '\UIAwesome\Html\Helper\Enum' in method 'buildAttributes'. (undefined)
(StaticAccess)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: phpunit / PHP 8.5-ubuntu-latest
- GitHub Check: phpunit / PHP 8.1-ubuntu-latest
- GitHub Check: phpunit / PHP 8.2-windows-2022
- GitHub Check: phpunit / PHP 8.5-windows-2022
- GitHub Check: phpunit / PHP 8.3-windows-2022
- GitHub Check: phpunit / PHP 8.4-windows-2022
- GitHub Check: phpunit / PHP 8.3-ubuntu-latest
- GitHub Check: phpunit / PHP 8.2-ubuntu-latest
- GitHub Check: phpunit / PHP 8.1-windows-2022
- GitHub Check: phpunit / PHP 8.4-ubuntu-latest
- GitHub Check: mutation / PHP 8.5-ubuntu-latest
- GitHub Check: linter / Super Linter
- GitHub Check: linter / Super Linter
- GitHub Check: phpstan / PHP 8.5-ubuntu-latest
🔇 Additional comments (8)
src/Form/InputCheckbox.php (2)
92-98: LGTM — return type aligned with immutable-setter convention.The
self→staticchange onchecked()is consistent with the trait methods and the parallelInputRadioclass.
119-125: LGTM — samestaticalignment foruncheckedValue().src/Form/InputRadio.php (2)
1-51: Well-structured new class with clear documentation.The class declaration, trait usage, and docblock are consistent with the existing
InputCheckboxpattern.
92-98: LGTM — return types usestatic, consistent with the immutable-setter pattern.Both
checked()anduncheckedValue()now returnstatic, aligning with trait methods and the updatedInputCheckbox.Also applies to: 119-125
tests/Form/InputRadioTest.php (4)
1-51: Comprehensive and well-organized test suite.Good coverage of the
InputRadiopublic API surface including attributes, label handling, enclosure, immutability, and rendering defaults. The idempotency test (lines 437–452) is a nice addition.
215-237: Past suggestion addressed — inline comment added forstr_replaceadaptation.The comment on line 221 clearly explains why the replacement is needed.
978-1022: Good immutability coverage.All fluent setters are verified to return new instances. This ensures the immutable value-object contract is upheld.
499-510:⚠️ Potential issue | 🟡 MinorGlobal state leaks if assertion fails before cleanup.
If the assertion on line 503 (or 958) throws,
SimpleFactory::setDefaults(InputRadio::class, [])is never reached, leaving global defaults in place for subsequent tests. Wrap intry/finallyor usetearDownto guarantee cleanup.🛡️ Proposed fix using try/finally
public function testRenderWithGlobalDefaultsAreApplied(): void { SimpleFactory::setDefaults(InputRadio::class, ['class' => 'default-class']); - self::assertStringContainsString( - 'class="default-class"', - InputRadio::tag()->render(), - 'Failed asserting that global defaults are applied correctly.', - ); - - SimpleFactory::setDefaults(InputRadio::class, []); + try { + self::assertStringContainsString( + 'class="default-class"', + InputRadio::tag()->render(), + 'Failed asserting that global defaults are applied correctly.', + ); + } finally { + SimpleFactory::setDefaults(InputRadio::class, []); + } }Same pattern for
testRenderWithUserOverridesGlobalDefaults.Also applies to: 952-962
⛔ Skipped due to learnings
Learnt from: terabytesoftw Repo: ui-awesome/html PR: 24 File: tests/Form/InputTextTest.php:289-300 Timestamp: 2026-02-06T21:37:17.409Z Learning: In the ui-awesome/html repository, the maintainer prefers not to use defensive cleanup patterns (like try/finally) for global state in tests. Their philosophy is that if one test fails, they all fail anyway, so defensive cleanup is not necessary.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
Pull Request