Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 4 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ The package will auto-register the service provider.

### Behaviour Matrix

**Note:** Rows in **bold** show special operator `=` & `!` behaviour.

| Value | `@attr` | `@data` | `@aria` | `@flag` |
|-----------------------|-------------------|------------------------|--------------------|-------------|
| `('foo', "bar")` | `foo="bar"` | `data-foo="bar"` | `aria-foo="bar"` | `foo` |
Expand All @@ -25,7 +23,6 @@ The package will auto-register the service provider.
| **`('foo=', true)`** | **`foo="true"`** | **`data-foo="true"`** | `aria-foo="true"` | `foo` |
| `('foo', false)` | _(nothing)_ | _(nothing)_ | `aria-foo="false"` | _(nothing)_ |
| **`('foo=', false)`** | **`foo="false"`** | **`data-foo="false"`** | `aria-foo="false"` | _(nothing)_ |
| **`('!foo', false)`** | _(throws)_ | _(throws)_ | **_(nothing)_** | _(throws)_ |
| `('foo', "0")` | `foo="0"` | `data-foo="0"` | `aria-foo="0"` | _(nothing)_ |
| `('foo', 0)` | `foo="0"` | `data-foo="0"` | `aria-foo="0"` | _(nothing)_ |
| `('foo', '')` | _(nothing)_ | _(nothing)_ | _(nothing)_ | _(nothing)_ |
Expand All @@ -35,18 +32,14 @@ The package will auto-register the service provider.
| `('foo', null)` | _(nothing)_ | _(nothing)_ | _(nothing)_ | _(nothing)_ |

**Gotchas:**
- `@attr` and `@data` allow the `=` suffix (e.g., `@attr('foo=', $value)`) to force values (always render with `="value"`, even for booleans and empty strings)
- `@aria` allows the `!` prefix (e.g., `@aria('!foo', $value)`) to negate false values for removing attribute entirely.
- `@attr` and `@data` allow the `=` suffix (`@attr('foo=', $value)`) to force values (always render with `="value"`, even for booleans and empty strings). The **bold** market rows show the special behaviour.

### Descriptions

- **`@attr`**: By default, `true` renders as a boolean flag (attribute name only), and `false`/empty/whitespace-only/null render nothing. With the force-value operator (`=` suffix like `'foo='`), always renders with values including`"true"`, `"false"`, and empty strings.

- **`@attr`**: By default, `true` renders as a boolean flag (attribute name only), and `false`/empty/whitespace-only/null render nothing. With the `=` force-value operator (e.g. `'foo='`), always renders with values including`"true"`,
`"false"`, and empty strings.
- **`@data`**: Same as `@attr` but automatically prefixes attribute names with `data-`.

- **`@aria`**: By default, renders all values including `"true"` and `"false"` (never as boolean flags). Never renders empty or whitespace-only strings. With the negation operator (`!` prefix like `'!
foo'`), `false` the attribute is completely removed instead of rendering as `"false"`.

- **`@aria`**: By default, renders all values including `"true"` and `"false"` (never as boolean flags). Never renders empty or whitespace-only strings.
- **`@flag`**: Outputs just the attribute name without a value (boolean flag), for truthy values only. Follows HTML spec for boolean attributes like `disabled`, `checked`, `required` or `data-foo`.

## Examples
Expand Down Expand Up @@ -99,10 +92,6 @@ foo'`), `false` the attribute is completely removed instead of rendering as `"fa
{{-- Before / After --}}
<div @if($label && $label !== '') aria-label="{{ $label }}" @endif></div>
<div @aria('label', $label)></div>

{{-- Before / After --}}
<div @if($hidden) aria-hidden="true" @endif></div>
<div @aria('!hidden', $hidden)></div>
```

### `@flag` Directive
Expand Down
40 changes: 0 additions & 40 deletions src/BladeHtmlAttributesServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ protected function compileFlag(string $expression): string

[ $attribute, $data ] = array_map('trim', $parts);

if (str_starts_with($attribute, "'!") || str_starts_with($attribute, '"!')) {
throw new ViewCompilationException('The @flag directive does not support negation.');
}

if (str_ends_with($attribute, "='") || str_ends_with($attribute, '="')) {
throw new ViewCompilationException('The @flag directive does not support forced values.');
}
Expand All @@ -75,10 +71,6 @@ protected function compileAttr(string $expression): string

[ $attribute, $data ] = array_map('trim', $parts);

if (str_starts_with($attribute, "'!") || str_starts_with($attribute, '"!')) {
throw new ViewCompilationException('The @attr directive does not support negation.');
}

$forceValue = str_ends_with($attribute, "='") || str_ends_with($attribute, '="');

if ($forceValue) {
Expand All @@ -100,10 +92,6 @@ protected function compileData(string $expression): string

[ $attribute, $data ] = array_map('trim', $parts);

if (str_starts_with($attribute, "'!") || str_starts_with($attribute, '"!')) {
throw new ViewCompilationException('The @data directive does not support negation.');
}

$forceValue = str_ends_with($attribute, "='") || str_ends_with($attribute, '="');

if ($forceValue) {
Expand All @@ -125,12 +113,6 @@ protected function compileAria(string $expression): string

[ $attribute, $data ] = array_map('trim', $parts);

$negated = str_starts_with($attribute, "'!") || str_starts_with($attribute, '"!');

if ($negated) {
return "<?php echo \\NickSdot\\BladeHtmlAttributes\\BladeHtmlAttributesServiceProvider::renderAriaNegated($attribute, $data); ?>";
}

return "<?php echo \\NickSdot\\BladeHtmlAttributes\\BladeHtmlAttributesServiceProvider::renderAria($attribute, $data); ?>";
}

Expand Down Expand Up @@ -180,28 +162,6 @@ public static function renderData(string $attribute, string|int|float|bool|null
return self::renderCommon($data, 'data-' . $attribute);
}

/** @api */
public static function renderAriaNegated(string $attribute, string|int|float|bool|null $data): string
{
$attribute = 'aria-' . mb_substr($attribute, 1); // remove = operator, add `aria-` prefix

if (null === $data || false === $data) {
return '';
}

if (is_bool($data)) {
return $attribute . '="true"';
}

$stringData = (string) $data;

if ('' === $stringData || '' === mb_trim($stringData)) {
return '';
}

return $attribute . '="' . e($stringData) . '"';
}

/** @api */
public static function renderAria(string $attribute, string|int|float|bool|null $data): string
{
Expand Down
23 changes: 2 additions & 21 deletions tests/AriaDirectiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,36 +11,17 @@ final class AriaDirectiveTest extends TestCase
public function testAriaDirective(): void
{
$default = "@aria('foo', \$bar)";
$negated = "@aria('!foo', \$bar)";

$this->assertSame('aria-foo="test"', $this->render($default, [ 'bar' => 'test' ]));
$this->assertSame('aria-foo="test"', $this->render($negated, [ 'bar' => 'test' ]));

$this->assertSame('aria-foo="0"', $this->render($default, [ 'bar' => 0 ]));
$this->assertSame('aria-foo="0"', $this->render($negated, [ 'bar' => '0' ]));

$this->assertSame('aria-foo="1"', $this->render($default, [ 'bar' => 1 ]));
$this->assertSame('aria-foo="1"', $this->render($negated, [ 'bar' => '1' ]));

$this->assertSame('aria-foo="8"', $this->render($default, [ 'bar' => 8 ]));
$this->assertSame('aria-foo="8"', $this->render($negated, [ 'bar' => '8' ]));

// aria never has empty strings
$this->assertSame('', $this->render($default, [ 'bar' => '' ]));
$this->assertSame('', $this->render($negated, [ 'bar' => '' ]));

// aria never has whitespace-only strings
$this->assertSame('', $this->render($default, [ 'bar' => ' ' ]));
$this->assertSame('', $this->render($negated, [ 'bar' => ' ' ]));

$this->assertSame('', $this->render($default, [ 'bar' => '' ])); // aria never has empty strings
$this->assertSame('', $this->render($default, [ 'bar' => ' ' ])); // aria never has whitespace-only strings
$this->assertSame('', $this->render($default, [ 'bar' => null ]));
$this->assertSame('', $this->render($negated, [ 'bar' => null ]));

$this->assertSame('aria-foo="true"', $this->render($default, [ 'bar' => true ]));
$this->assertSame('aria-foo="true"', $this->render($negated, [ 'bar' => true ]));

$this->assertSame('aria-foo="false"', $this->render($default, [ 'bar' => false ]));
$this->assertSame('', $this->render($negated, [ 'bar' => false ]));

$this->assertSame(
'aria-foo="&lt;script&gt;alert(&#039;xss&#039;)&lt;/script&gt;"',
Expand Down
8 changes: 0 additions & 8 deletions tests/AttrDirectiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,6 @@ public function testAttrDirectiveParameterCount(): void
Blade::compileString("@attr('foo')");
}

public function testAttrDirectiveUnsupportedNegation(): void
{
$this->expectException(ViewCompilationException::class);
$this->expectExceptionMessage('The @attr directive does not support negation.');

Blade::compileString("@attr('!foo', true)");
}

/** @param array<string, bool|int|string|null> $data */
protected function render(string $directive, array $data = []): string|false
{
Expand Down
8 changes: 0 additions & 8 deletions tests/DataDirectiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,6 @@ public function testDataDirectiveParameterCount(): void
Blade::compileString("@data('foo')");
}

public function testDataDirectiveUnsupportedNegation(): void
{
$this->expectException(ViewCompilationException::class);
$this->expectExceptionMessage('The @data directive does not support negation.');

Blade::compileString("@data('!foo', true)");
}

/** @param array<string, bool|int|string|null> $data */
protected function render(string $directive, array $data = []): string|false
{
Expand Down
8 changes: 0 additions & 8 deletions tests/FlagDirectiveTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,6 @@ public function testFlagDirectiveParameterCount(): void
Blade::compileString("@flag('disabled')");
}

public function testFlagDirectiveUnsupportedNegation(): void
{
$this->expectException(ViewCompilationException::class);
$this->expectExceptionMessage('The @flag directive does not support negation.');

Blade::compileString("@flag('!foo', true)");
}

/** @param array<string, bool|int|string|null> $data */
protected function render(string $renderable, array $data = []): string|false
{
Expand Down